微服务
单体项目缺点:
随着项目的开发, 单体服务的构建产物会愈来愈大, 随之带来的问题就是项目的构建, 部署和发布会越来越慢
微服务优点:
微服务适用于敏捷开发, 各个功能的依赖不会互相影响
微服务开发技术:
- 服务网关 (请求路由, 负载均衡)
- 注册中心 (拉取或注册服务)
- 配置中心 (拉取配置信息)
- 分布式缓存
- 分布式搜索
- 消息队列
- 分布式日志
- 系统监控-链路追踪
部署技术:
- CL/CI
- Docker
- K8s
可分为:
微服务技术
- 微服务治理
- 微服务保护
- 分布式事务
缓存技术
- 分布式缓存
- 多级缓存
- Redis 集群
异步通信技术
- 异步通信
- 可靠消息服务
- 搜索技术
- DevOps
微服务技术
国内流行的微服务技术框架:
- SpringCloud
- 阿里巴巴 Dubbo
- SpringCloudAlibaba
技术对比:
Dubbo | SpringCloud | SpringCloudAlibaba | |
---|---|---|---|
注册中心 | ZooKeeper, Nacos | Eureka, Consul | Nacos, Eureka |
服务远程调用 | Dubbo 协议 | Feign (http 协议) | Dubbo, Feign |
配置中心 | 无 | SpringCloudConfig | SpringCloudConfig, Nacos |
服务网关 | 无 | SpringCloudGateway | SpringCloudGateway, Zuul |
服务监控和保护 | dubbo-admin | Hystrix | Sentinel |
一般的技术选型:
- SpringCloud + Feign
- SpringCloudAlibaba + Feign
- SpringCloudAlibaba + Dubbo
- Dubbo 原始模式
服务拆分
服务拆分注意事项:
- 单一职责: 不同微服务, 不要重复开发相同的业务
- 数据独立: 不要访问其它微服务数据库
- 面向服务: 将自己的业务暴露为接口, 供其它服务调用
消费者与服务者
服务提供者: 一次调用中, 被其它微服务调用的服务 (提供接口)
服务消费者: 一次调用中, 调用其它微服务的服务 (调用服务)
在实际的代码开发中, 我们需要知道服务提供者的访问地址(分布式架构, 意味着提供者的访问地址随时变化), 如何知道提供者的接口地址? 通过注册中心来发现提供者地址
注册中心
Eureka 注册中心
在 EureKa 架构中, 微服务角色有两类:
EurekaServer 服务端, 注册中心
- 记录服务信息
- 心跳记录
EurekaClient 客户端
Provider 服务提供者
- 注册自己的信息到 EurekaServer 发送心跳
- 每隔一段时间向 EurekaServer 发送心跳
consumer 服务消费者
- 根据服务名称从 EurekaServer 拉取服务列表
- 基于服务列表做负载均衡, 选中一个微服务后发起远程调用
搭建注册中心
创建一个项目模块 eureka-service
, 用于创建EurekaServer 服务端, 注册中心
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
配置 application.yml
server:
port: 10086
spring:
application:
name: eureka-server
cloud:
# 避免虚拟机带来的报错, 忽略虚拟网卡
inetutils:
ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
# 不向注册中心注册自己。
register-with-eureka: false
fetch-registry: false
最后, 在启动类中添加启用注解:
@EnableEurekaServer
启动后, 访问 localhost:10086 查看注册中心
服务注册 - 配置客户端
为其拆分的服务加入依赖 spring-cloud-starter-netflix-eureka-client
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置项目
spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
可复制实例, 模拟负载均衡, 在 IDEA 服务栏中, 选中运行的实例, 右键复制实例, 在 VM Options
中添加选型 -Dserver.port=8083
以修改服务端口, 以避免复制实例端口冲突
服务发现
服务发现须在启动类中添加方法 restTemplate
, 并添加注解 @LoadBalanced
@LoadBalanced
为负载均衡
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
然后, 就可通过 http://[服务名称]/[接口路径]
, 即将实际的域名改为微服务名称即可对服务提供者发起调用
String requestUrl = "http://user-service/user/" + order.getUserId();
User user = restTemplate.getForObject(requestUrl, User.class);
Ribbon 负载均衡
负载均衡流程:
负载均衡策略:
自定义负载均衡
可通过 IRule
实现来修改负载均衡规则, 有两种方式:
- 代码修改
- 配置修改
代码修改:
在启动类中, 定义一个新的 IRule
:
@Bean
public IRule randomRule(){
return new RandomRule();
}
配置方式, 在 application.yml
中添加以下配置:
eureka:
client:
user-service:
ribbon:
# 负载均衡规则
NFLoadBalanceRuleClassName: com.netflix.loadbalance.RandomRule
两种方法的生效范围不同,
代码修改的方法作用于全局, 所有调用的所有服务的负载均衡规则都将被修改
而配置方式作用于局部, 可根据不同的调用服务来配置不同的负载均衡规则
饥饿加载
Ribbon 默认采用懒加载, 即第一次访问时才会去创建 LoadBalanceClient
, 请求时间长
而饥饿加载会在项目启动时创建, 降低第一次访问的耗时
通过配置开启饥饿加载:
eureka:
ribbon:
eager-load:
enable: true
client: user-service # 对 user-service 进行饥饿加载
Nacos 注册中心
Nacos 是阿里巴巴的产品, 现在是 SpringCloud 中的一个组件
相比 Eureka 功能更加丰富, 在国内受欢迎程度较高
Nacos 安装
我是把 Nacos 部署至 Linux 虚拟机中, 用的是 Docker 运行
单机启动命令:
务必注意 Nacos 版本
docker run --name nacos-standalone \
-e MODE=standalone \
-p 8080:8080 \
-p 8848:8848 \
-p 9848:9848 \
-d nacos/nacos-server:v2.5.1
安装完成后, 访问其对应 http://[ip]:8848/nacos
进入 Nacos 控制台
服务注册到 Nacos
Nacos 与 Eureka 不能共存, 在引入 Nacos 时, 注意去掉 Eureka 相关依赖
在父级工程中添加 spring-cloud-alibaba
依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在服务中添加服务发现 (服务注册)依赖, 即 Nacos 客户端
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
在服务中的 application.yml
中配置 Nacos 客户端
spring:
cloud:
nacos:
server-addr: 192.168.233.128:8848
Nacos 服务分级存储模型
Nacos 分级存储模型分三级:
- 一级服务 (服务接口)
- 二级集群 (服务器集群, 例如广州, 上海)
- 三级实例 (服务器)
集群调用负载均衡
服务跨集群调用问题: 跨集群调用延迟较高
我们希望服务调用尽可能选择本地集群服务, 当本地集群不可访问时, 再去访问其它集群
若没配置集群, 则服务默认在 DEFAULT
集群中
我们可以通过 application.yml
配置服务所属集群
spring:
cloud:
nacos:
server-addr: 192.168.233.128:8848
discovery:
# 配置集群
cluster-name: CD # 集群 - 成都
要想优先访问本集群, 还需使用 Nacos 的负载均衡
user-service: # 服务名
ribbon:
# 负载均衡规则
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
Nacos 负载均衡策略:
- 优先选择同集群服务实例列表
- 本地集群找不到提供者, 才去其它集群寻找, 并且会报警告
- 确定了可用实例列表后, 再采用随机负载均衡挑选实例
权重负载均衡
在实际部署中会出现这样的问题:
服务器设备性能有差异, 部分实例机器性能号, 另一部分差
我们希望性能好的机器去承担更多的用户请求
可通过 Nacos 注册中心选择实例并配置权重, 权重范围 0 ~ 1
之间
实例的权重控制:
- Nacos 控制台可以设置实例的权重值
- 同集群内的多个实例, 权重越高被访问的频率越高
- 权重为 0 则完全不会被访问
环境隔离
软件工程的各种环境:
- 开发环境
- 测试环境
- 灰度环境
- 生产环境
为了实现各种环境的隔离, Nacos 使用 namespace 来隔离
Nacos 中服务存储的最外层都是一个名为 namespace 的东西, 用来做最外层隔离
在 Nacos 控制台中创建命名空间, 我们在这里可以创建一个为 dev
的命名空间
创建完成后, 会有一个命名空间 ID
我们需要将命名空间 ID 配置到服务中
spring:
cloud:
nacos:
server-addr: 192.168.233.128:8848
discovery:
cluster-name: CD
namespace: edc5e1fe-a6f4-4c33-951e-a0f2f4a25d00 # 命名空间 ID
临时/非临时实例
我们可以将服务改成非临时实例, 这样, Nacos 会主动查询服务实例的健康状况
配置服务实例为非临时实例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为非临时实例
总结
Nacos 与 Eureka 的共同点:
- 支持服务注册和服务拉取
- 支持服务提供者以心跳方式做健康检测
Nacos 与 Eureka 的区别:
Nacos支持服务端主动检测提供者状态
- 临时实例采用心跳模式
- 非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除, 非临时实例则不会被剔除
- Nacos 支持服务列表变更的消息推送模式, 服务列表更新更及时
- Nacos 集群默认采用 AP 方式, 当集群中存在非临时实例时, 采用 CP 模式
- Eureka 采用 AP 方式