一、微服务介绍
1. 系统架构的演变
1.1 单体架构
将业务的所有功能集中在一个项目中开发,打成一个包部署。当网站流量很小时,单体架构非常合适。
单体架构的优缺点如下:
优点:
- 架构简单
- 开发简单、运维简单
缺点:
- 耦合度高(维护困难、升级困难)
- 任何一个功能要升级,整个项目都需要重新编译、打包、部署
- 任何一个功能出bug,都有可能会影响到整个服务的运行
1.2 分布式服务
分布式架构:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。
分布式架构的优缺点:
优点:
- 降低服务耦合
- 有利于服务升级和拓展
缺点:
- 服务有重复代码
- 服务调用关系错综复杂
- 服务容错性较差
1.3 微服务
微服务其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案,微服务以如下特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责。一个服务只做一件事
- 服务自治:团队独立、技术独立、数据独立,独立部署和交付
- 面向服务:服务对外暴露统一标准的接口,与语言和技术无关。SpringCloud体系里使用HTTP协议的接口
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
但是使用了微服务,也会带来一些新的问题:
-
框架设计复杂
-
不同服务之间需要相互调用 –远程调用技术
-
远程调用时可能会出现问题 –服务保护,防止雪崩
-
远程调用时需要获取地址 – 服务治理问题
-
众多的服务需要一个统一入口 – 服务网关
-
每个服务都有配置文件,修改变更非常麻烦 –配置中心
2. SpringCloud介绍
SpringCloud微服务解决方案:不是一个框架,是一系列框架集合,目前包含二十多个框架,还在不断增加中……
SpringCloud是Pivotal团队提供的、基于SpringBoot开箱即用的、一站式微服务解决方案的框架体系。目前已成为国内外使用最广泛的微服务框架。
SpringCloud集成了各种微服务功能组件。
微服务需要解决的问题(微服务的5个核心组件):
- 注册中心:解决的是服务治理问题,即 服务地址的管理。Nacos
- 远程调用:解决服务之间的请求调用问题。RestTemplate(今天临时用),Feign
- 服务保护:防止服务之间出现级联问题导致雪崩。Hystrix,Sentinel
- 配置中心:解决的是每个服务都有配置文件,配置文件散乱不方便管理的问题。Nacos
- 服务网关:解决的是 要给客户端提供一个统一的访问入口。SpringCloudGateway
二、注册中心
1. 服务治理问题
目前已经可以实现微服务之间的调用,但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:
- 一旦服务提供者地址变化,就需要手工修改代码
- 一旦服务变得越来越多,人工维护调用关系困难
这时候就需要通过注册中心动态的实现服务治理。
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
- 服务注册: 在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
- 服务拉取: 服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实例的访问。
2. Nacos 使用入门
Nacos是SpringCloudAlibaba的组件,而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别。
主要差异在于:
- 依赖坐标不同
- 配置参数不同
微服务 开启服务注册与发现,步骤:
- 依赖:在添加SpringCloudAibaba的版本锁定,再添加nacos-discovery的依赖坐标
- 配置:注册中心的地址 和 微服务的名称
- 引导类:添加@EnableDiscoveryClient,开启服务注册与发现
示例
1. 依赖
父工程锁定SpringCloudAlibaba的依赖版本
在父工程pom.xml的dependencyManagement中添加SpringCloudAlibaba的版本锁定
<!--SpringCloudAlibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.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>
2. 配置
修改微服务的application.yaml,配置nacos
spring:
application:
name: order-service #微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #注册中心的地址
3. 开启服务
再所有微服务的引导类,在引导类上添加注解@EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication
@MapperScan("com.itheima.order.mapper")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4. 功能测试
验证服务是否注册到Nacos
-
先启动Nacos,再启动用户服务和订单服务
-
打开Nacos控制台,访问http://localhost:8848/nacos,查看注册的服务信息
验证远程调用是否成功
-
修改Order服务的引导类,给RestTemplate对象添加 @LoadBalanced
@Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } -
修改OrderService类,根据服务名进行远程调用
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; public Order findById(Long id){ Order order = orderMapper.selectById(id); //使用RestTemplate,向名称为user-service的服务发起远程调用,得到结果 User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class); order.setUser(user); return order; } } -
打开浏览器,访问http://localhost:7070/order/101
3. Nacos的原理
3.1 临时实例与非临时实例
从Nacos1.0开始就提供了一个配置参数:spring.cloud.nacos.discovery.ephemeral,用于设置微服务实例是临时实例还是非临时实例
- 值为true:微服务是临时实例。当微服务长时间不能向Nacos续约时,Nacos会剔除微服务实例的信息
- 值为false:微服务是非临时实例。即使微服务长时间不能向Nacos续约,Nacos也仅仅是将服务实例标记为不健康状态,而不会剔除服务实例的信息
两者的作用是:
- 临时实例:适用于应对流量突增的情况,当流量洪峰时,增加服务实例数量;当流量高峰过去以后,服务实例停止,就自动从Nacos里剔除了
- 非临时实例:适用于服务的保护。当Nacos发现短时间有大量微服务不健康时,实际可能是Nacos的网络波动等情况,而微服务实例其实是正常的。 这时Nacos仍然保留服务信息,供消费者进行拉取
3.2 Nacos的原理
三、负载均衡Ribbon
1. 什么是负载均衡
通俗的讲,负载均衡就是将负载(工作任务,访问请求)分摊到多个操作单元上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
-
服务端(提供者)负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡。
-
客户端(消费者)负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。
我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行。
2. Ribbon负载均衡
Ribbon是NetFlix提供的客户端负载均衡工具,它有助于控制HTTP和TCP客户端的行为。
Ribbon提供了多种负载均衡算法,包括轮询、随机等等,同时也支持自定义负载均衡算法。
它不需要独立部署,而是几乎存在于SpringCloud的每个组件中,包括Nacos也已经引入了Ribbon。
- 当使用RestTemplate发起远程调用时,需要给RestTemplate对象添加@LoadBalanced注解
- 当使用OpenFeign发起远程调用时,不需要做任何额外配置,就具备负载均衡效果
3. Ribbon实现原理分析
@LoadBalanced注解
注解的文档中说明了:如果使用@LoadBalanced标记一个RestTemplate或者WebClient对象,表示将会配置使用一个LoadBanalcerClient对象
LoadBalancerClient源码
LoadBalancerClient有一个子类RibbonLoadBalancerClient,它使用Ribbon实现了负载均衡调用
IRule源码
在上一步的getServer方法代码如下:
继续跟进去,发现是由rule对象挑选了一个目标服务地址
这个IRule是什么呢?
4. Ribbon负载均衡策略
4.1 负载均衡策略介绍
IRule是负载均衡策略的顶级接口,它的每个实现类就是一个我们可以选择使用的负载均衡策略
Ribbon内置了多种负载均衡策略,这些策略的顶级接口是com.netflix.loadbalancer.IRule,它的常用实现有:(可参考上一章节里的IRule类图)
| 策略名 | 描述 | 详细说明 |
|---|---|---|
| RandomRule | 随机策略 | 随机选择一个Server |
| RoundRobinRule | 轮询策略 | 按顺序依次选择Server(默认的策略) |
| RetryRule | 重试策略 | 在一个配置时间段内,如果选择的Server不成功,则一直尝试选择一个可用的Server |
| BestAvailableRule | 最低并发策略 | 逐个考察Server,如果Server断路器打开则忽略掉;再选择其余Server中并发链接最低的 |
| AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server |
| WeightedResponseTimeRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长权重越低,被选择到的机率越小。响应时间越短被选中的机率越高。刚开始运行时使用robin策略选择Server |
| ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个aws zone的运行性能是否可用,剔除不可用的zon中的所有server |
4.2 修改负载均衡策略
配置文件方式
我们可以通过修改配置文件来调用Ribbon的负载均衡策略,只要修改**调用者的配置文件**即可:
服务名:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
例如,在订单服务的application.yaml里添加如下配置:
#调用用户服务时,使用指定的负载均衡策略
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
代码@Bean方式
我们修改订单服务的配置类(或引导类),添加:
@Bean
public IRule randomRule(){
return new RandomRule();
}
注意事项
-
使用配置文件方式,只能设置 调用某一服务时指定负载均衡策略,而不是全局的策略;
使用@Bean方式,配置的是全局的负载均衡策略
-
实际开发中,通常使用默认的负载均衡策略,不需要修改
5. 饥饿加载
在我们的示例代码里,订单服务里必须要先拉取到用户服务的地址列表,创建LoadBanalcerClient对象,然后才可以发起远程调用。而Ribbon有两种策略:
- 懒加载:即第一次访问目标服务时,才会去创建LoadBalanceClient对象,会导致第一次请求时间比较长
- 饥饿加载:当服务启动时,就立即拉取服务列表,并创建好LoadBalancerClient对象;这样的话,即使第一次远程调用时也不会花费很长时间
Ribbon默认采用懒加载方式,如果我们需要修改成饥饿加载的话,以订单服务为例,可以在订单服务的配置文件里增加如下配置:
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: #要饥饿加载的服务列表
- user-service