1、学习路线
微服务基本概念,SpringCloud ,SpringCloud Alibaba ,RabbitMQ 消息队列,分布式事务解决方案
2、微服务
2.1 基本介绍
微服务是一种架构,这种架构是将单个的整体应用程序分割成更小的项目关联的独立的服务。一个服务通常实现一组独立的特性或功能,包含自己的业务逻辑和适配器。各个微服务之间的关联通过暴露api来实现。这些独立的微服务不需要部署在同一个虚拟机,同一个系统和同一个应用服务器中。
2.2 架构演变
架构的演变大致为:单一应用架构 ===> 垂直应用架构 ===> 分布式服务架构 ===> 流动计算架构微服务架构
2.2.1单体架构
将所有功能代码都部署在一起就可以,这样可以减少开发、部署和维护的成本
优点:
1.架构简单,容易上手
2.易于部署,可以实现快速维护、定位问题
缺点:
1.单点故障:有一个模块出现问题,可能会引发整个系统出现问题,无法单独对系统的某一个模块进行扩展,只能对整个系统进行水平扩展。无法单独定制实现弹性扩展。
2.因为All In One,整个项目的所有功能都部署在一台服务器上,无法承载高并发请求
2.2.2 垂直应用架构
就是将原来的一个应用拆成互不相干的几个应用,以提升效率。
单体应用拆分成多个应用,比如多个后台系统应用
优点:
系统拆分实现了流量分担,解决了并发问题,而且可以针对不同模块进行优化和水平扩展。
一个系统的问题不会影响到其他系统,提高容错率。
缺点:
系统之间相互独立, 无法进行相互调用。
系统之间相互独立, 会有重复的开发任务
2.2.3 分布式服务架构
垂直应用越来越多,重复的业务代码就会越来越多。这时候,我们就思考可不可以将重复的代码抽取出来,做成统一的业务层作为独立的服务,然后由前端控制层调用不同的业务层服务呢? 这就产生了新的分布式系统架构。它将把工程拆分成表现层和服务层两个部分,服务层中包含业务逻辑。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
优点:
抽取公共的功能为服务层,提高代码复用性。
缺点:
系统间耦合度变高,调用关系错综复杂,难以维护。
2.2.4 微服务架构
微服务架构在某种程度上是面向服务的架构,它更加强调服务的"彻底拆分"
特点:
1、为了满足高并发请求,对服务进行拆分,单独部署
2、服务独立、数据库独立、语言独立、团队独立
优势:
1、支持高并发场景
2、可以对某一个模块单独部署、运维、扩展等操作,而不影响其他业务。
缺点:
1、分布式系统开发的技术成本高(容错、分布式事务等)。
2、服务治理和服务监控关键。
3、多服务运维难度,随着服务的增加,运维的压力也在增大
微服务带来的难题:
这么多小服务,如何管理他们?
这么多小服务,他们之间如何通讯?
这么多小服务,客户端怎么访问他们?
这么多小服务,一旦出现问题了,应该如何自处理?
这么多小服务,一旦出现问题了,应该如何排错?
3、SpringCloud介绍
3.1 SpringCloud
Spring技术体系:
SpringFramework —> SpringBoot —> SpringCloud
微服务开发:
1、SpringCloud
2、SpringCloudAlibaba
3、ApacheDubbo 阿里开源的分布式RPC框架
4、其他自研微服务框架
3.2 SpringCloud 全家桶
SpringCloud
Eureka 注册中心
Gateway 网关
Openfeign 远程调用
Config 配置中心
Ribbon 负载均衡
Hystrix 熔断器
https://spring.io/projects/spring-cloud
3.2.1 SpringCloud – Eureka 注册中心
- 0、实例化服务
- 1、将服务注册到注册中心
- 2、注册中心收录服务
- 3、注册中心获取服务列表
- user-service 127.0.0.1:9091
- goods-service 127.0.0.1:9092
- sso-service 127.0.0.1:9093
- 4、基于负载均衡算法从地址列表选择一个服务地址进行服务调用
- 5、定期发送心跳
- 6、检查没有定期发送心跳的服务并在一定时间内剔除服务地址列表
通过服务中心来获取服务你不需要关注你调用的项目IP地址,由几台服务器组成,每次直接去服务中心获取可以使用的服务去调用既可。
作用:实现服务治理(服务注册与发现)
由两个组件组成:Eureka服务端和Eureka客户端。
Eureka服务端用作服务注册中心。支持集群部署。
Eureka客户端是一个java客户端,用来处理服务注册与发现。
在应用启动时,Eureka客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地。客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。
1、Eureka是SpringCloud提供的注册中心组件!
注册中心: 提供服务的自动注册与服务自动发现!
Eureka使用层面来看,分为两块内容:
Eureka服务端
1、会开启一个定时任务,每60秒执行一次,超过90秒没有发送心跳的服务就会被从服务列表中剔除!
Eureka客户端
1、我们的微服务,作为Eureka的客户端,每30秒发送一次心跳检测,证明自己还活着,是一个健康示例
2、搭建Eureka服务端
2.1 创建eureka-server项目
2.2 依赖
父项目:锁定springcloud版本 spring-cloud-dependencies
<!--锁定spring-cloud的版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
eureka依赖:spring-cloud-starter-netflix-eureka-server
<!--引入Eureka服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
web启动器依赖
2.3 yml配置
# 访问端口
server:
port: 8761
# 服务名称,一般和项目名称一样;注意:不要用下划线
spring:
application:
name: eureka-server
# eureka 客户端配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
# 不向注册中心注册自己,默认为true; 如果要搭建eureka集群,必须设置为true
register-with-eureka: false
#不从注册中心拉取服务
fetch-registry: false
2.5 启动类
// @EnableEurekaServer 开启Eureka服务端
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication { }
2.6 访问控制台
http://localhost:8761/
3、Eureka服务端配置
3.1 客户端注册到Eureka服务端
cinema-member-service 注册到Eureka
A. 依赖
<!--Eureka客户端依赖包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
B. yml 配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
C. 启动类
/**
* Eureka客户端注解:
* 1、@EnableEurekaClient 只能开启Eureka的客户端
* 2、@EnableDiscoveryClient 开启服务自动发现客户端,支持:Eureka与Nacos
* 3、不写也可以,springboot2以后
*/
@SpringBootApplication
@EnableDiscoveryClient
public class MemberApplication { }
Eureka服务治理:服务自动注册与发现
服务提供者或服务消费者一旦启动,会连上Eureka注册中心, 会向注册中心写入自身服务IP、端口、协议等信息;
同时,也会从Eureka服务端拉取服务列表,缓存到本地,更方便服务之间的调用。
Eureka服务心跳
为了维护服务的可用,eureka客户端会每隔30秒发送一次心跳检测,证明自己活着;
Eureka服务端,会通过一个定时任务,每60秒执行一次,检测超过90秒未发送心跳续约的服务,从服务列表中移除。
这样,可能会导致,有些服务由于网络原因未把心跳请求送达到eureka服务端,但是服务本身是正常的,导致被意外剔除!
造成服务不可用!Eureka通过自我保护机制解决这个问题。
Eureka自我保护机制
统计最后一分钟实际收到的心跳续约数,如果小于心跳续约阈值,就开启自我保护!
心跳续约阈值? 总心跳数 * 0.85
开启自我保护,
1. Eureka不会再剔除没有发送心跳续约的服务示例;
2.Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
4. 当网络稳定后,eureka服务端收到客户端发送的心跳续约满足关闭条件,自我保护就关闭。
自我保护?目的是为了保证服务的可用
4、Eureka集群
4.1 注册中心需要配置集群吗? 生产环境下,必须配置。否则单台注册中心出问题,整个微服务架构不可用。
4.2 如何搭建注册中心集群?
本地模拟:
动态端口的方式:
A. 配置中:
eureka:
client:
service-url:
defaultZone: ${defaultZone111:http://localhost:8762/eureka}
B. 运行时期,指定VM Options 参数
-Dport=8762 -DdefaultZone111=http://localhost:8761/eureka
Eureka客户端:
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
4.3 Eureka之间服务实例,如何同步?
1、请求转发:cinema-member-service服务注册到某一个节点后,该节点会把注册请求转发到集群中的其他节点,从而实现服务同步。
这样只要客户端需要用到服务,拉取任意节点的服务列表就可以获取服务的完整信息。
2、若转发注册请求失败,会转发心跳请求。eureka1转发注册请求到eureka2失败,就会在eureka1发送心跳请求时候转发到eureka2,eureka2发现在自己的服务缓存中不存在就会对该服务进行注册。
3、转发心跳请求失败,eureka内部会维护一个批处理流,保存转发失败的请求,每隔1秒重试,宕机的节点恢复后就可以收到服务注册请求,保证数据的可用性。
4、通过设置fetch-registry 和 register-with-eureka, 主动拉取方式
3.2.2 springCloud – Ribbon 负载均衡
场景: 服务提供者有多个,在客户端这里需要进行负载均衡(Load Balance)
1 服务端负载均衡
A. LB 把请求通过负载均衡策略分摊到不同的处理单元上; 前提:集群环境
B. Nginx就是服务端负载均衡
说明:
1、nginx负载均衡,单独的服务器,建立在客户端与服务端之间的一个web服务器
2、客户端发出请求,首先经过的就是nginx服务器,通过nginx进行负载均衡,找到具体处理请求的服务器
3、服务端与nginx之间通过心跳维持可用关系,如果服务端未发送心跳请求到nginx就会从可用服务中剔除
2 客户端负载均衡
特点:
1、通过代码实现,客户端调用服务端时候,需要获取可用服务列表清单,再通过负载均衡算法选择一个可用服务
2、要借助于注册中心实现,因为要通过注册中心获取服务列表 (eureka已经提供负载均衡的能力,内置了ribbon)
通过Ribbon实现,eureka集成了ribbon的包
3、不需要单独搭建负载均衡服务器
对于nginx服务器,所有请求到达nginx服务器后,由nginx服务器进行请求路由的分发,实现负载均衡。
对于Ribbon,是由客户端主动拉取注册中心的服务列表,然后通过负载均衡算法选取一个可用服务实例(其中通过自旋锁的cas来保证服务不被多个线程重复获取),整个过程是发生在客户端服务的
为什么说Nginx是服务端的负载均衡,Ribbon是客户端的负载均衡呢?
(1)用户发送请求到nginx,nginx是服务端。
(2)Ribbon是微服务之间通信的负载均衡,订单服务调用商品服务时,订单服务就是客户端,商品服务就是服务端
3、Ribbon负载均衡应用
3.1 依赖
不需要添加:
spring-cloud-starter-netflix-eureka-client 注册中心已经集成了
spring-cloud-starter-netflix-ribbon 依赖包
3.2 @LoadBalanced 注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.3 通过restTemplate,远程调用通过服务名完成
@GetMapping("/{memberId}")
public String findMemberById(@PathVariable("memberId") String memberId) {
String url = "http://cinema-member-service/member/" + memberId;
return restTemplate.getForObject(url, String.class);
}
3.4 现在已经实现了负载均衡,默认的策略是轮询
RandomRule
RoundRobinRule
RetryRule
3.5 修改负载均衡策略
局部:针对某一个服务有效
cinema-member-service: # 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
全局:针对所有服务有效
// 负载均衡全局配置, 所有的服务都使用RandomRule随机
@Bean
public IRule rules(){
return new RandomRule();
}
4、Ribbon负载均衡实现原理
4.1 通过RestTemplate上添加@LoadBalanced注解实现了客户端负载均衡。实现原理?
4.2 原理:SpringBoot自动配置(LoadBalancerAutoConfiguration) + LoadBalancedInterceptor
4.3 分析
A. spring-cound-commons包的META-INF/spring.factories
查看自动配置类:LoadBalancerAutoConfiguration
1、创建LoadBalancerInterceptor, 负载均衡拦截器对象
2、把LoadBalancerInterceptor添加到 List<ClientHttpRequestInterceptor> 拦截器的集合中
3、最后把拦截器集合设置到restTemplate中。这样确保restTemplate执行时候会执行拦截器
B. LoadBalancerInterceptor 负载均衡拦截器
1、客户端通过restTemplate发送请求时候,会执行拦截器
2、在LoadBalancerInterceptor拦截器中,获取负载聚合策略
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
3、再从可用服务列表中,选择一个可用服务处理请求
Server server = this.getServer(loadBalancer, hint);
4、最终就可用根据负载均衡设置,选择可用服务,处理请求
3.2.3 SpringCloud – Openfeign 远程调用
https://blog.csdn.net/weixin_43888891/article/details/126171740
1、Openfeign
1.1 SpringCloud提供的声明式远程调用的方式
1.2 Openfegin 实现原理:动态代理.
2、如何使用?
远程调用接口当中,一般我们称提供接口的服务为提供者,而调用接口的服务为消费者。而OpenFeign一定是用在消费者上
2.1 依赖
spring-cloud-starter-openfeign
2.2 开启feign注解支持
@SpringBootApplication
// 开启feign的客户端支持,实现远程调用
@EnableFeignClients
public class PortalApplication { }
2.3 定义feign接口: 从服务提供者拷贝其控制器方法
@FeignClient(value = "cinema-member-service") // value指定服务名称
public interface MemberFeignClient {
/**
* 远程调用的接口名称,要与服务提供者接口名称、参数类型与值保持一致。
* 注意:路径要完整
*/
@GetMapping("/member/{id}")
String findOne(@PathVariable("id") String id);
}
2.4 远程调用,直接通过@Autowired注入MemberFeignClient对象即可
@RestController
@RequestMapping("fegin")
@Slf4j
public class PortalOpenFeginController {
// 注入feign的客户端对象,代理对象
@Autowired
private MemberFeignClient memberFeignClient;
@GetMapping("{id}")
public String findMemberByfeign(@PathVariable("id")String id){
log.info("通过feign远程调用,参数:{}",id);
String result = memberFeignClient.findOne(id);
return result;
}
}
3、openfeign集成ribbon
默认在openfeign的依赖中已经集成了ribbon,我们可用直接使用。
局部配置:
cinema-member-service: # 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
全局配置:
// 负载均衡全局配置, 所有的服务都使用RandomRule随机
@Bean
public IRule rules(){
return new RoundRobinRule();
}
feign的其他功能
4、feign日志集成
4.1 配置feign日志级别
// feign日志输出级别
@Bean
public Logger.Level loggerLevel(){
return Logger.Level.FULL;
}
4.2 日志配置
logging:
level:
com.wnxy: debug
4.3 结果:查看feign远程调用过程日志
---> GET http://cinema-member-service/member/100 HTTP/1.1 【请求头】
<--- HTTP/1.1 200 (407ms) 【响应信息】
connection: keep-alive
content-length: 12
content-type: text/plain;charset=UTF-8
date: Tue, 28 Jun 2022 09:33:17 GMT
keep-alive: timeout=60
会员信息
<--- END HTTP (12-byte body)
5、feign配置
需求: 模拟一个超时方法,远程调用该超时方法,看看会发生什么
5.1 服务提供者,添加一个休眠
...
public class MemberController {
@GetMapping("/{id}")
public String findOne(@PathVariable("id") String id) {
log.info("根据会员ID查询,会员ID:{}",id);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "会员信息";
}
}
5.2 远程调用,报错:java.net.SocketTimeoutException: Read timed out
5.3 服务消费者,客户端配置超时时间
feign:
client:
config:
default:
# 连接超时
connectTimeout: 1000
# 业务处理超时
readTimeout: 3000
测试1:配置超时时间3秒后,没有报错!
测试2:readTimeout: 3000 改为 2000 后,在访问就报错,超时!
测试3:此时,超时,我想要自己配置重试次数
// feign 超时重试
@Bean
public Retryer feignRetryer() {
// 默认尝试5次
return new Retryer.Default();
}
作业:
一、面试题
1、Ribbon是什么?
ribbon是springclould 一个组件,提供负载均衡的功能,客户端调用服务端时候,获取服务列表清单,然后通过负载均衡算法选择一个可用服务处理请求
2、客户端负载均衡与服务端负载均衡区别?
服务端负载均衡一般指的是 nginx 负载均衡,客户端发送请求,首先会经过nginx服务器,,通过nginx 负载均衡,找到具体处理请求的、的服务器
客户端负载均衡:一般指的是ribbon 负载均衡,客户端在调用服务端的时候,获取服务列表,然后通过负载
3、服务之间,如何进行远程调用?
3.2.4 springcCloud – hystrix 熔断器
1、分布式系统遇到的问题
1.1 服务之间的调用错综复杂,当某一个服务出现问题,整个调用链都会收到影响
ServiceA----->ServiceB----->ServiceC----->ServiceD (出问题?)
1.2 某个服务调用时间过长,或者出现错误,引起整个微服务架构出现故障,设置导致系统崩溃,这种现象叫做雪崩!
1.3 为了避免服务的雪崩,对服务进行隔离或管理 (降级:快速返回错误信息)
1.4 通过hystrix可以对服务进行降级、熔断、还可以对服务进行实时的监控,服务限流,从而保证整个微服务系统的健壮
2、Hystrix实现服务降级
2.1 服务降级
当服务调用出现错误、超时,快速返回失败信息,这个就是服务降级!
2.2 如何实现?
第一步:依赖
spring-cloud-starter-netflix-hystrix
第二步:开启熔断降级注解
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class PortalApplication { }
或者
@SpringCloudApplication
@EnableFeignClients
public class PortalApplication { }
原理:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
第三步:编写降级方法
@RestController
@RequestMapping("fegin")
@Slf4j
@DefaultProperties(defaultFallback = "errorFallback") // --> 指定服务降级的默认方法
public class PortalOpenFeginController {
// 注入feign的客户端对象,代理对象
@Autowired
private MemberFeignClient memberFeignClient;
@GetMapping("{id}")
@HystrixCommand // ---> 需要降级的方法
public String findMemberByfeign(@PathVariable("id")String id){
log.info("通过feign远程调用,参数:{}",id);
String result = memberFeignClient.findOne(id);
return result;
}
// 降级方法
public String errorFallback(){ // ---> 自定义方法
return "服务降级,服务调用超时、异常!";
}
}
局部降级方法:针对某一个控制器单独编写降级方法
@GetMapping("{id}")
@HystrixCommand(defaultFallback = "errorFallback2") // 针对当前控制器方法单独编写的降级方法
public String findMemberByfeign(@PathVariable("id")String id){
log.info("通过feign远程调用,参数:{}",id);
String result = memberFeignClient.findOne(id);
return result;
}
public String errorFallback2(){
return "服务降级,局部,针对某一个方法";
}
第四步:feign集成hystrix
feign:
hystrix:
enable: true
2.3 对feign接口编写降级方法
上面的服务降级方法与业务代码耦合,非常不友好。可以对fegin接口的每一个方法都对应降级处理操作。
实现步骤:
第一:编写降级类, 实现feign接口
// 针对feign接口编写的降级类
@Component
public class MemberFeignClientCallback implements MemberFeignClient {
@Override
public String findOne(String id) {
return "根据会员id查询,出现未知错误!";
}
}
第二:修改feign接口
@FeignClient(
value = "cinema-member-service",
fallback = MemberFeignClientFallback.class
)
public interface MemberFeignClient {
第三:删除@HystrixCommand、@DefaultProperties 注解以及对应的降级方法
3、服务熔断
3.1 什么时服务熔断?
服务熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,
从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。
如果访问某个服务总是出现错误,会对接口做降级处理,老是出现错误达到一定的条件就进行熔断!
一旦进入熔断状态,即使正常的请求,也会直接返回错误信息。
3.2 熔断的三个状态
关闭状态 Closed
默认,所有的请求都可以正常访问服务,熔断器默认是关闭的。
打开状态 Open
在时间窗口内,默认是10秒,有50%的请求都出现异常,熔断器(断路器)就会打开
一旦熔断器处于打开状态,所有正常的请求也会直接返回降级方法。
半开状态 Half Open
熔断器不能一直处于打开状态,这样的话正常的请求也访问不了了;
所以熔断器打开后,5秒就进入半开状态,半开状态就会尝试关闭熔断器;
什么时候关闭? 当有正常的请求访问接口,就关闭熔断器。
当请求继续失败,熔断器就一直处理半开状态。
3.3 熔断测试
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "errorFallback",commandProperties = {
// 是否开启断路器
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"),
// 请求错误率大于 50% 就启动熔断器
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "50"),
// 默认10秒内访问接口失败达到20次,打开断路器,触发熔断; 为了方便测试,这里修改10秒内错误达到10次触发熔断。
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "10"),
//断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "10000")
})
public String findMemberByfeign(@PathVariable("id")Integer id){
log.info("通过feign远程调用,参数:{}",id);
if (id<=0) {
throw new RuntimeException("参数错误!");
}
// String result = memberFeignClient.findOne(id);
// return result;
return "success";
}
// 默认降级方法,返回string
public String errorFallback(@PathVariable("id")Integer id){
return "控制器接口的熔断降级方法!";
}
4、服务监控
通过Hystrix Dashboard 实现对微服务接口调用情况的监控。
步骤:
1、搭建监控中心微服务项目:hystrix-dashboard
2、配置要监听的微服务
3、访问监控中心
http://localhost:9002/hystrix
访问后,配置要监控的微服务地址:http://localhost:8000/hystrix.stream
步骤实现:
1、搭建监控中心微服务项目:hystrix-dashboard
第一:依赖
<!--配置hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
第二:yml 配置
server:
port: 9002
hystrix:
dashboard:
proxy-stream-allow-list: "*"
第三:启动类
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
2、配置要监听的微服务
cinema-portal-service (端口8000)
第一:依赖
<!--hystrix监控-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
</dependency>
第二:配置bean
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
3、访问监控中心
http://localhost:9002/hystrix
访问后,配置要监控的微服务地址:http://localhost:8000/hystrix.stream
面试题:
一、熔断与降级区别?
1、先进行服务降级:当访问接口出现异常、超时、线程池已满,就走降级方法
2、当服务降级达到一定的条件,比如:时间窗口内失败比率达到50%就进行服务熔断
3、再熔断状态下,所有的请求都会被降级
4、熔断打开5秒后,就进入半开状态,会尝试关闭熔断器; 当有正常请求访问就关闭熔断器,否则,一直处理半开状态
二、Hystrix是什么?包含哪些功能?
Hystrix是熔断器,提供了熔断、降级、服务监听等一些列功能。
特性:
1、服务降级
2、服务熔断
3、线程隔离
hystrix通过对每一个接口分配线程池,实现接口之间的隔离
4、请求缓存
会对调用结果进行缓存,后续的请求会走缓存,提高效率
举例:查询id=100的数据,把查询结果放入缓存中后续的查询会直接从缓存中获取。自带内存缓存。
5、请求合并
是指短时间内多个请求合并为一个请求,节省资源,降低服务的负载。
3.2.5 springcCloud – Gateway 网关
1、分布式系统遇到的问题
1.1 随着业务越来越复杂,微服务也越来越多
前端要调用后端接口,需要访问这些微服务,需要记住这些微服务的地址,比较繁琐
1.2 每隔微服务都要做安全认证,涉及大量的重复操作,不利于代码维护。
1.3 上述问题,都可以通过Gateway网关解决!
2、Gateway 网关
2.1 介绍
什么是Gateway?
A. 网关是建立在客户端与业务微服务之间的一个微服务,我们可以在网关中完成:
统一鉴权、请求路由、限流等一系列功能
B. 网关是作为微服务的统一访问入口
C. 我们要访问微服务,首先要先通过网关,再在网关中对请求进行断言(predicate)判断,
根据断言结果把请求路由(转发)到不同的微服务
网关作用:负载均衡ribbon,熔断降级hystrix,限流(基于redis实现),断言,过滤器,跨域
网关解决方案?
zuul 一代网关
Gateway 基于springboot2 + reactor构建
Nginx + Lua
Kong
网关高可用?
因为网关是微服务统一入口,所以必须要保证网关的高可用。
一旦搭建网关集群,这里就需要引入Nginx作为负载均衡服务器。
请求流转:
客户端---> Nginx ----> Gateway ----> 微服务
2.2 核心概念
(1)Route(路由):
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
(2)Predicate(断言):
路由断言,判断请求是否符合要求,符合则转发到路由目的地。
(3)Filter(过滤器):
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改。
3、Route Predicate 路由断言
3.1 Path Route Predicate:发送指定路径的请求会匹配该路由
server:
port: 10000 # 不要用6000
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-service
uri: http://localhost:9000
predicates:
- Path=/member/**
A. 先访问微服务,确保微服务OK: http://localhost:9000/member/100
B. 再通过网关访问微服务: http://localhost:10000/member/100
路径匹配:
1、判断判断,符合/member/**要求
2、找到微服务:http://localhost:9000
3、匹配微服务地址:
http://localhost:9000/member/100
路由:是由一组断言工厂组成,刚才用的是PathRoutePredicateFactory这个断言工厂
|-- RoutePredicateFactory
|-- PathRoutePredicateFactory
3.2 Query Route Predicate:带指定查询参数的请求可以匹配该路由
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-service
uri: http://localhost:9000
predicates:
- Query=name
查询必须要带上参数:name
举例: http://localhost:10000/member/100
断言工厂: QueryRoutePredicateFactory
3.3 After Route Predicate:在指定时间之后的请求会匹配该路由
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-service
uri: http://localhost:9000
predicates:
- After=2022-06-30T14:15:00+08:00[Asia/Shanghai]
- After: 在指定的时间之后,才可以访问微服务。
- Before: 之前
- Between=2022-03-24T16:30:00+08:00[Asia/Shanghai], 2022-03-24T16:30:00+08:00[Asia/Shanghai]
3.4 Method Route Predicate:路由匹配指定的请求方式
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-service
uri: http://localhost:9000
predicates:
- Method=post
必须post请求才可以通过网关路由到微服务。
3.5 Weight Route Predicate:使用权重来路由相应请求,以下表示有80%的请求会被路由到localhost:9000,20%会被路由到localhost:8999
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: http://localhost:8999
predicates:
- Weight=group1,8
- id: api-member-low
uri: http://localhost:9000
predicates:
- Weight=group1,2
带权重的断言
小结:
断言作用: 对访问网关的请求url地址进行断言判断,如果符合断言要求, 才会对请求地址进行路由,路由到对应的微服务!
4、动态路由:微服务的负载均衡配置
https://zhuanlan.zhihu.com/p/681456240
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member/**
说明:
lb://cinema-member-service lb表示负载均衡配置;后面的是服务名称
默认负载均衡策略是轮询,如果要改为随机,可以这样:
cinema-member-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
原理:
Eureka客户端集成了Ribbon;
而网关集成了Eureka,所以网关中可以直接使用Ribbon!
5、网关中配置过滤器
5.1 StripPrefix GatewayFilter:对指定数量的路径前缀进行去除的过滤器 【GatewayFilterFactory】
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member/**
filters:
- StripPrefix=1
StripPrefix=1 对访问路径表示去除一位前缀。
访问路径:
举例1: http://localhost:10000/member/100 --> 去除一位前缀 http://localhost:10000/100
举例2: http://localhost:10000/api/member/100 直接就不符合断言规则。
举例3: http://localhost:10000/member/member/100 符合断言规则; 再去除1位前缀; 最终可以访问。
重新配置测试:
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
访问地址:
http://localhost:10000/member-service/member/100
1、先进行断言判断: 符合
2、再执行过滤器,去除一个前缀,地址为:
http://localhost:10000/member/100
3、再路由到微服务
http://localhost:9000/member/100
5.2 PrefixPath GatewayFilter:与StripPrefix过滤器恰好相反,会对原有路径进行增加操作的过滤器
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Method=get
filters:
- PrefixPath=/member
- PrefixPath=/member 对请求添加一个路径前缀/member
地址1:http://localhost:10000/member/100 -->添加前缀后http://localhost:10000/member/member/100
地址2:http://localhost:10000/100 -->添加前缀后http://localhost:10000/member/100
6、Gateway网关集成Hystrix降级熔断
第一步:添加Hystrix依赖
<!--Gateway集成Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
第二步:编写降级类
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Map<String,Object> fallback(){
Map<String,Object> map = new HashMap<>();
map.put("code",500);
map.put("message","服务不可用");
return map;
}
}
第三步:yml配置,集成hystrix
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
第四步:停掉一台服务,测试。浏览器会返回降级信息。
第五步:超时降级配置
全局配置:
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
httpclient:
connect-timeout: 1000 # 建立连接的超时时间
response-timeout: 5000 # 响应的超时时间(处理业务的超时时间)
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 # hystrix线程隔离超时时间,默认是1秒
测试逻辑:
cinema-member-service微服务接口中休眠2秒,不配置超时,默认会返回降级信息;
配置完超时时间为两个5秒后,就不会再降级了。
局部配置:
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
metadata:
connection-timeout: 1000 # 局部配置,针对当前路由有效 (当前版本测试未生效!)
response-timeout: 4000
在gateway中配置服务的超时时间,推荐采用: 【全局配置】
7、Gateway 限流
7.1 介绍
基于Redis实现的限流。
原理:令牌桶算法。 以固定的频率生产令牌放入令牌桶中,桶的容量大小决定并发能力。由请求过来时候就从令牌桶中
获取令牌,获取不到拒绝访问。获取到令牌才可以访问处理业务。
7.2 实现
第一步:添加Redis依赖
<!--限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
第二步:限流策略:根据ip限流
@Configuration
public class RatelimitConfig {
/**
* 根据IP限流,即以每秒内请求数按IP分组统计,超出限流的url请求都将返回429状态
*/
@Bean
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
//获取请求的IP地址
String hostName = exchange.getRequest().getRemoteAddress().getHostName();
//创建Mono发布者对象,发布数据源
return Mono.just(hostName);
}
};
}
}
第三步:配置限流过滤器
spring:
application:
name: gateway
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
- name: RequestRateLimiter #限流过滤器
args:
#每秒补充1个,令牌桶每秒填充平均速率, 允许用户每秒处理多少个请求。
redis-rate-limiter.replenishRate: 1
#令牌桶总容量,每秒最大处理的请求数量
redis-rate-limiter.burstCapacity: 3
#限流策略,通过SpEL从容器中获取对象
key-resolver: "#{@ipKeyResolver}"
第四步:启动Redis (我上课用的是windows版本)
第五步:如何测试?
Jmeter 并发测试工具.
使用:
测试计划右键--->添加--->线程--->线程组
选中【线程组】--> 【添加】--> 【取样器】--->【http请求】
选中【http请求】 ---> 【添加】---> 【监听器】-->【查看结果树】
最后:输入请求地址!
并发测试5个请求,因为桶的容量是3,所以有2个请求返回429,表示请求过多,拒绝!
八、重试过滤器使用
spring:
cloud:
gateway:
routes:
- id: api-member-high
uri: lb://cinema-member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
- name: RequestRateLimiter #限流过滤器
args:
#每秒补充1个,令牌桶每秒填充平均速率, 允许用户每秒处理多少个请求。
redis-rate-limiter.replenishRate: 1
#令牌桶总容量,每秒最大处理的请求数量
redis-rate-limiter.burstCapacity: 3
#限流策略,通过SpEL从容器中获取对象
key-resolver: "#{@ipKeyResolver}"
- name: Retry
args:
retries: 1 # 重试次数
series:
- SERVER_ERROR #返回哪个状态码需要进行重试,这里表示返回状态码为5XX进行重试
Retry 重试过滤器:可以对指定的响应状态码进行重试。一般状态码都是错误状态码或自定义的状态码。
SERVER_ERROR 对应 HttpStatus中的枚举对象
九、网关跨域
@Configuration
public class CorsFilter {
// 跨域:协议、主机、端口三者有一个不一样,就产生跨域。
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
Nginx与API gateway网关
本质上API网关也是做了请求的转发,那既然Nginx也可以做请求转发,那这两者有什么区别
在具体架构设计时Nginx做负载均衡时,考虑到API网关在系统中不止一个(以集群的方式做高可用),通常可以将Nginx至于API网关前,负责对API网关的负载均衡,然后再由网关决定进入根据判定到哪个真实的web 服务器。 让两者的分工更加明确,也就是:API网关聚合服务,Nginx请求转发
业务网关(gateway):对于具体的后端业务应用或者是服务和业务有一定关联性的策略网关。业务网关针对具体的业务需要提供特定的流控策略、缓存策略、鉴权认证策略
流量网关(Nginx):与业务网关相反,定义全局性的、跟具体的后端业务应用和服务完全无关的策略网关。流量网关通常只专注于全局的Api管理策略,比如全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等
业务网关一般部署在流量网关之后、业务系统之前,比流量网关更靠近业务系统。通常API网指的是业务网关。 有时候我们也会模糊流量网关和业务网关,让一个网关承担所有的工作,所以这两者之间并没有严格的界线。
3.2.6 springcCloud – Config 配置中心
1、微服务架构中,配置如何管理?
1.1 目前
每个微服务,单独管理自己的配置文件!
application.yml 或 application.properties 文件存储配置!
1.2 问题
维护困难:后台对配置文件进行维护,变得很困难! (100个微服务)
安全性差:配置信息与代码一起保存在同一个项目中,一旦泄露风险大
局限性:配置的动态维护无法实现!
1.3 解决
引入统一的配置中心,解决上述问题!
市面上常见:
A. SpringCloud Alibaba Nacos 作为配置中心(或者注册中心)
B. SpringCloud Config 配置中心 【要学习】
C. 百度 disconf
D. 携程 Appollo
2、SpringCloud Config 组成
两部分
* Config 配置中心服务端
需要单独去搭建配置中心服务端
主要用来连接远程仓库,获取配置信息,再把获取的配置信息返回给对应的微服务
* Config 客户端
连上配置中心服务端
自动从配置中心服务端获取配置信息,配置中心服务端再从远程仓库获取配置
3、搭建配置中心服务端
3.1 目标
搭建项目config-server, 作为配置中心服务端,能够连上git
3.2 准备git仓库
登陆gitee.com,新建一个仓库,记住仓库地址、登陆gitee的账号和密码
3.3 动手搭建配置中心服务端
第一:依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
第二:启动类
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServerApplication { }
第三:yml配置
server:
port: 8000
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/yuanjie8080/cinema-mico-config.git
username: yuanjie8080
password: WONIU8080
eureka:
client:
service-url:
defaultZone: http://jet:123@localhost:8761/eureka
第四:gitee上创建配置文件cinema-member-service-dev.yml, 并拷贝微服务中配置信息到gitee上
第五:http://localhost:8000/cinema-member-service-dev.yml
确保: config-server 配置中心服务端 ----> 连上gitee
4、cinema-member-service 配置中心客户端
4.0 配置中心客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
4.1 删除原来的application.yml, 因为这里的配置信息已经拷贝到gitee上了
4.2 新建【bootstrap.yml】
spring:
cloud:
config:
name: cinema-member-service # 对应的是微服务名称,gitee上的文件名称的一部分
profile: dev # 激活的配置,与上面的name拼接共同构造了完整的配置文件名
label: master # git 上的分支名称
uri: http://localhost:8000/ # 配置中心服务端地址,就是config-server地址
4.3 启动,查看日志
... Fetching config from server at : http://localhost:8000/
表示连接配置中心8000,从其上获取配置信息
最后,访问cinema-member-service,进行测试:
http://localhost:9000/member/100
5、配置动态刷新
5.1 目标: 改了配置文件,不需要重启。 但是要发送一个post请求实现配置的动态更新。
5.2 步骤
第一:引入依赖
<!--配置动态刷新,引入actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二:yml配置动态刷新
management:
endpoints:
web:
exposure:
include: 'refresh'
第三:在希望动态刷新配置的控制器上,使用注解@RefreshScope
@RestController
@RequestMapping("refresh")
@RefreshScope
public class RefreshController {
@Value("${title}")
private String title;
@GetMapping("/test")
public String test() {
return title;
}
}
第四步:重新后。修改git上的配置信息,发现并没有动态刷新。
最后,还需要手动发送一个post请求:http://localhost:9000/actuator/refresh
访问的是配置中心客户端(cinema-member-service),此时会通知配置中心服务端重新去git上拉取最新的配置返回。
6、配置中心重试机制
6.1 为什么要使用重试?
cinema-member-service -------> config-server
配置中心客户端 连接? 配置中心服务端
配置中心客户端 在启动时候,如果没有连上配置中心服务端,就启动不了报错 timeout。
引入重试机制就是为了解决这个问题。
6.2 实现:
第一:引入spring-retry依赖
第二:重试相关配置 yml cinema-member-service的yml配置
spring:
cloud:
config:
name: cinema-member-service
profile: dev
label: master
uri: http://localhost:8000/
fail-fast: true #快速失败,进行重试
retry:
initial-interval: 5000 #初始间隔时间
max-interval: 30000 #最大间隔时间
max-attempts: 5 # 重试次数
multiplier: 2 #重试间隔时间倍数
第三:测试
停掉配置中心服务端,再启动配置中心客户端,通过日志就可以看到重试的过程!
7、多仓库配置
7.1 为什么要使用多仓库配置?
一个仓库,对应一个微服务的配置文件。
方便管理微服务的配置文件。
举例:10个微服务,10个仓库分别存放这10个微服务需要的配置文件。
问题? 配置中心服务端仓库地址不能够写死!
7.2 使用
第一:config-server 配置中心服务端,修改仓库地址,动态获取服务名称
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/yuanjie8080/{application}
username: yuanjie8080
password: WONIU8080
第二:cinema-member-service 客户端
spring:
application:
name: cinema-member-service # 服务名称,对于配置中心客户端来说也是仓库名称
cloud:
config:
#name: cinema-member-service # 注释
profile: dev
label: master
uri: http://localhost:8000/
添加的配置:服务名称,作为仓库名称,以及仓库中配置文件的名称
第三:新建仓库cinema-member-service,再在仓库下新建配置文件 cinema-member-service-dev.yml
OK!
8、配置中心高可用
8.1 搭建两台配置中心服务端,分别是8000、8001
config-server:
server:
port: ${port:8000}
通过VM Option配置多个端口,启动多个配置中心服务端
8.2 cinema-member-service 配置中心客户端,连接多个config服务端
spring:
application:
name: cinema-member-service # 服务名称,对于配置中心客户端来说也是仓库名称
cloud:
config:
#name: cinema-member-service
profile: dev
label: master
uri: http://localhost:8000,http://localhost:8001 # 多个config服务端
fail-fast: true #快速失败,进行重试
retry:
initial-interval: 5000 #初始间隔时间
max-interval: 30000 #最大间隔时间
max-attempts: 5 # 重试次数
multiplier: 2 #重试间隔时间倍数
8.3 测试
停掉8000,重启cinema-member-service客户端;
刚好看到日志:
Multiple Config Server Urls found listed.
Fetching config from server at : http://localhost:8000
Connect Timeout Exception on Url - http://localhost:8000. Will be trying the next url if available
Fetching config from server at : http://localhost:8001
从上面可以看出,先连接config服务端8000,连接超时,切换另外一个节点8001.
最终:做到了配置中心高可用!
3.2.7 Nacos
1、nacos作用:配置中心、注册中心
2、nacos配置中心
项目:
bootstrap.yml
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
profiles:
active: dev
nacos控制台,新建配置
命名:服务名+环境.yaml
举例:user-service-dev.yaml
编写控制器,通过@RefreshScope注解,自动实现配置的读取与自动更新!
3、Nacos配置管理三元组
问题:nacos配置中心如何管理配置文件?
方案1:dataId
Nacps配置中心:
user-service-dev.yaml
user-service-test.yaml
user-service-prod.yaml
程序中切换环境:
spring:
profiles:
active: prod
方案2:group
Nacps配置中心:
配置user-service-prod.yaml,
默认分组是DEFAULT_GROUP,创建完不能修改。可用先删除,再重新创建
现在指定为: PROD_GROUP
程序中:
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
group: PROD_GROUP # 默认分组 DEFAULT_GROUP
profiles:
active: prod
方案3:namespace
第一:创建一个namespace叫做dev
第二:先选中namepsace为dev(id是:9b90ad-1902-4026-b8ae-db2b70cda9f9),再新建配置
dataId: user-service-prod.yaml
group: PROD_GROUP
第三:boostrap.yml中
spring:
application:
name: user-service # dataId
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
namespace: 1b9b90ad-1902-4026-b8ae-db2b70cda9f9 # namespace
group: PROD_GROUP # group
profiles:
active: prod
4、MySQL存储配置信息
Nacos内嵌的数据库存储了配置信息,重启Nacos配置依赖存在。
当然也可以把配置信息存储到mysql中,实现配置的持久化存储。
修改nacos的conf目录下application.properties配置即可!
5、Nacos注解中心
5.1 实现
依赖:spring-cloud-starter-alibaba-nacos-discovery
配置:
spring:
cloud:
discovery:
server-addr: localhost:8848
这里也可以配置namespace与group
spring:
cloud:
discovery:
server-addr: localhost:8848
namespace: 57bb4bb9-8f8c-47a9-bffd-9134ad2fbefc
group: PROD_GROUP
5.2 多实例,服务集群
第一:在nacos配置中心创建三个配置文件
user-service-dev.yaml 端口:8888
user-service-test.yaml 端口:8887
user-service-prod.yaml 端口:8886
第二:idea配置同一个启动类,可以执行多次: Edit Configuration --> Allow parallel run
第三:微服务bootstrap.yml配置
spring:
profiles:
active: dev/test/prod # 分别配置
6、gateway网关
第一:父项目锁定spring-cloud-dependencies版本
第二:启动类
第三:yml配置
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
group: PROD_GROUP
namespace: 57bb4bb9-8f8c-47a9-bffd-9134ad2fbefc
gateway:
routes:
- id: api-gateway-service
uri: lb://user-service
predicates:
- Path=/user-service/**
filters:
- StripPrefix=1
第四:访问测试
用户微服务:http://localhost:8888/user
通过网关访问:http://localhost:10010/user-service/user
7、nacos 权重设置
7.1 控制台设置
7.2 配置bean
// 如果配置了服务权重,这里需要配置才会生效
@Bean
public IRule loadBalanceRule(){
return new NacosRule();
}
8、nacos实例
分类:
临时实例 AP架构,保证可用性
持久化实例 CP架构,保证一致性
保护阈值:
配置保护阈值,是0到1之间的数,比如设置为:0.6
当健康实例/总实例 < 保护阈值,就会返回所有的实例给客户端; 否则只返回健康实例
演示:
设置的保护阈值是:0.6
停止一台服务器: 健康实例/总实例 = 2/3 = 0.66 > 0.6, 只会返回健康实例,测试效果:页面不会报错。
停止两台服务器:健康实例/总实例 = 1/3 = 0.33 < 0.6, 返回所有实例,测试效果:会有报错
9、删除持久实例
持久化实例nacos不会删除,我们可用通过运维操作手动删除。
比如:查看nacos官网 open api章节中,有一章:注销实例
https://nacos.io/zh-cn/docs/open-api.html
nacos 和eureka 的区别
1.CAP理论 :C一致性,A高可用,P分区容错性
eureka只支持AP
nacos支持CP和AP两种
nacos是根据配置识别CP或AP模式,如果注册Nacos的client节点注册时是ephemeral=true即为临时节点,那么Naocs集群对这个client节点效果就是AP,反之则是CP,即不是临时节点
#false为永久实例,true表示临时实例开启,注册为临时实例
spring.cloud.nacos.discovery.ephemeral=true
2.连接方式
nacs使用的是netty和服务直接进行连接,属于长连接
eureka是使用定时发送和服务进行联系,属于短连接
3.服务异常剔除
Eureka client在默认情况每隔30s想Eureka Server发送一次心跳,当Eureka Server在默认连续90s秒的情况下没有收到心跳, 会把Eureka client 从注册表中剔除,在由Eureka-Server 60秒的清除间隔,把Eureka client 给下线
nacos:
nacos client 通过心跳上报方式告诉 nacos注册中心健康状态,默认心跳间隔5秒,
nacos会在超过15秒未收到心跳后将实例设置为不健康状态,可以正常接收到请求
超过30秒nacos将实例删除,不会再接收请求
4.自我保护机制
相同点:保护阈值都是个比例,0-1 范围,表示健康的 instance 占全部instance 的比例。
不同点:
1)保护方式不同
Eureka保护方式:当在短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,服务端也可以接受新的注册和查询请求,但是不会同步到其他节点,只保证当前节点可用,等到恢复正常后,再退出自我保护机制,同时将新的注册请求同步到其他节点。自我保护开关(eureka.server.enable-self-preservation: false)
Nacos保护方式:当域名健康实例 (Instance) 占总服务实例(Instance) 的比例小于阈值时,无论实例 (Instance) 是否健康,都会将这个实例 (Instance) 返回给客户端。这样做虽然损失了一部分流量,但是保证了集群的剩余健康实例 (Instance) 能正常工作。