提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
提示:以下是本篇文章正文内容,下面案例可供参考
微服务(Microservice Architecture)是最近几年流行的一种架构思想。简单来说,微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
应用(服务)组件化
每一个应用都是一个单独的服务,所有的应用都可以被单独的替代或升级,单独进行部署。
服务无状态化
这个是可以实现微服务快速扩展的基本条件。
标准而轻量级的通信协议
Restful,把所有的应用通过相同的协议集成在一个平台当中。
去中心化
包含数据去中心化,技术栈去中心化。
1)技术去中心化:既可以使用java,也可以使用php,go。只要使用支持标准通信协议就可以接入微服务平台。
2)数据去中心化:每个服务可以使用自己独立的数据源,可以使用mysql,redis等等。
故障处理设计和实时监控
微服务发展到后面会越来越复杂,服务变多,环境复杂,所以当出现问题需要有相应的系统容错机制和完善的实时的日志监控。
微服务:强调的是服务的大小,他关注的是某一个点。
微服务架构:他是一种风格,一种思想,需要从整体上对软件系统进行通盘的考虑。
分布式系统一定是由多个节点组成的系统。其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的。这些连通的节点上部署了我们的节点,并且相互的操作会有协同。分布式系统对于用户而言,他们面对的就是一个服务器,提供用户需要的服务而已,而实际上这些服务是通过背后的众多服务器组成的一个分布式系统,因此分布式系统看起来像是一个超级计算机一样。简单来说:很多"人”一起,干不样的事。这些不一样的事, 合起来是一件大事。
集群是指在几个服务器上部署相同的应用程序来分担客户端的请求。
它是同一个系统部署在不同的服务器上,比如一个登陆系统部署在不同的服务器上。好比 多个人一起做同样的事。集群主要的使用场景是为了分担请求的压力。但是,当压力进一步增大的时候,可能在需要存储的部分,比如mysql无法面对大量的“写压力”。因为在mysql做成集群之后,主要的写压力还是在master的机器上,其他slave机器无法分担写压力,这时,就引出了“分布式”。简单来说:很多“人”一起,干一样的事。
上面说了:分布式是一群人做不同的事情,所有的事务合起来是一个整体。集群是一群人做相同的事情。所有分布式系统中每一个节点服务都可以搭建集群,但是集群不一定是分布式架构。
一个war包或jar包的项目中,包含了应用所有功能的应用程序,我们通常称之为单体应用。单体架构中,全部功能集中在一个项目内(All in one),这是一种比较传统的架构风格。
优势:
架构简单,前期开发成本低、开发周期短,适合小型项目(OA、CRM、ERP 企业内部应用)。
缺点
① 复杂性高
② 技术债务逐渐上升
③ 部署速度逐渐变慢
④ 扩展能力受限,无法按需伸缩
⑤ 阻碍技术创新
垂直架构是指将单体架构中的多个模块拆分为多个独立的项目。形成多个独立的单体架构。垂直MVC项目主要有表现层,业务逻辑层,数据访问层组成的MVC架构,整个项目打包放在一个tomcat里面。适合于 访问量小,用户数不多的业务。
优点
技术栈可扩展(不同的系统可以用不同的编程语言编写)。
缺点
① 垂直架构中每一个系统还是单体架构。不利于开发、扩展、维护,项目的部署效率很低,代码全量编译和部署一次发布需要很长时间,更重要的是 如果某个功能出错有问题,所有的功能都需要再重新打包编译,部署效率极低。
② 相互无法访问,导致多个项目中可能要编写重复的功能代码,加载重复的数据。
③ 团队协作难度高,如多人使用 git 很可能在同一个功能上,多人同时进行了修改,作为一个大而全的项目,可能个人只是需要开发其中一个小的模块的需求,却需要导入整个项目全量的代码。
SOA 全称为 Service-Oriented Architecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。站在功能的角度,把业务逻辑抽象成可复用的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。
优点
① 可重用性高
② 维护成本低
③ 可扩展性强
缺点
① 抽取服务的粒度大
② 技术难度较高,不容易解决bug
其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
优点
① 服务拆分粒度更细,遵循单一职责原则,有利于提高开发效率。
② 可采用Http协议进行服务间的调用
③ 可以针对不同服务制定对应的优化方案,适用于互联网时代,产品迭代周期更短。
缺点
① 对开发团队技术要求高
RPC(Remote Procedure Call)— 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。
RPC 架构主要包括三部分:
服务消费者启动时向服务注册中心(Registry)获取服务提供方地址列表。服务注册中心(Registry)可实现负载均衡和故障切换。
RPC 调用过程
(1) 客户端(client)以本地调用方式调用服务;
(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
(3) 客户端通过 sockets 将消息发送到服务端;
(4) 服务端存根(server stub)收到消息后进行解码(将消息对象反序列化);
(5) 服务端存根(server stub)根据解码结果调用本地的服务;
(6) 本地服务执行并将结果返回给服务端存根(server stub);
(7) 服务端存根(server stub)将返回结果打包成消息(将结果消息对象序列化);
(8) 服务端(server)通过 sockets 将消息发送到客户端;
(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
(10) 客户端(client)得到最终结果。
RPC 就是要把 2、3、4、7、8、9 这些步骤都封装起来。
1.RPC调用: Netty(Socket)+自定义序列化
2.RestAPI: 严格来说,SpringCloud是使用Rest方式进行服务之间交互的,不属于RPC。HTTP+JSON
1.通过设计保证系统可以并行处理很多请求。应对大量流量与请求
2.高并发衡量指标
雪崩效应 : 在微服务之间进行服务调用是由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程。
服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
服务限流就是为了保护后端服务和数据不被大流量击垮而设计,当发生流量浪涌时,让实例能在最大有效负荷运行,而不会超过负载导致崩溃。限流就是限制某个服务每秒的调用本服务的频率。
API网关要做很多工作,它作为一个系统的后端总入口,承载着所有服务的组合路由转换等工作,除此之外,我们一般也会把安全,限流,缓存,日志,监控,重试,熔断等放到 API 网关来做。
当部署的服务越来越多的时候,需要跟踪一个请求从一个服务到下一个微服务的传播过程。追踪服务的调用链,记录整个系统执行请求过程。如:请求响应时间,判断链中的哪些服务属于慢服务(可能存在问题,需要改善)。
所谓的弹性,指的是云端的可用资源能够随着用户的需求而灵活变化、自由升降,在业务高峰或低谷期,能匹配适量的资源。
弹性云服务器,简单地说,是指运行在云计算环境中的虚拟服务器,支持用户自由调度计算资源,不受传统硬件设备的限制。
Elastic Compute Service(ECS)弹性计算服务
动态扩容,压榨服务器闲时能力
例如:双11,618,高峰时多配置些服务器,平时减少多余的服务器配置(用于其他服务应用),避免资源浪费
CREATE DATABASE IF NOT EXISTS cloud2022 DEFAULT CHARACTER SET utf8 ;USE cloud2022 ;DROP TABLE IF EXISTS payment ;CREATE TABLE payment (id BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT 'ID',SERIAL VARCHAR (300) DEFAULT NULL,PRIMARY KEY (id)
) ENGINE = INNODB AUTO_INCREMENT = 33 DEFAULT CHARSET = utf8 ;INSERT INTO payment (id, SERIAL) VALUES(31, '商品01'),(32, '商品02') ;
初始化项目已准备好直接导入即可
1.Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
2.Eureka Client通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会在Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)
导入相关依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-server com.atguigu cloud-api-commons ${project.version} org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok org.springframework.boot spring-boot-starter-test test junit junit
server:port: 7001spring:application:name: cloud-eureka-servereureka:instance:hostname: localhostclient:register-with-eureka: falsefetchRegistry: falseservice-url:defaultZone: http://localhost:7001/eureka
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerMain7001 {public static void main(String[] args) {SpringApplication.run(EurekaServerMain7001.class,args);}
}
访问:http://localhost:7001/,看到如下结果证明访问成功!!!
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
eureka:client:register-with-eureka: truefetchRegistry: trueservice-url:defaultZone: http://localhost:7001/eureka
@EnableEurekaClient
注解@SpringBootApplication
@MapperScan("com.tlc.mapper")
@EnableEurekaClient
public class PaymentMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentMain8001.class,args);}
}
在浏览其中访问:http://localhost:7001/,看到如下结果证明配置成功!!!
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
eureka:client:register-with-eureka: truefetchRegistry: trueservice-url:defaultZone: http://localhost:7001/eureka
@EnableEurekaClient
注解@SpringBootApplication
@EnableEurekaClient
public class OrderMain8000 {public static void main(String[] args) {SpringApplication.run(OrderMain8000.class,args);}
}
在浏览其中访问:http://localhost:7001/,看到如下结果证明配置成功!!!
1. LB(负载均衡)
2. 总结
Ribbon=负载均衡+RestTemplate调用
Ribbon在工作时分成两步:
第一步,先选择EurekaServer,它优先选择在同一个区域内负载较少的server。
第二步,再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略。比如:轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
1.创建多个cloud-provider-payment8001服务
①启动PaymentMain8001启动类
②在idea中拷贝2个PaymentMain800服务,PaymentMain8002、PaymentMain8003
③启动PaymentMain8002、PaymentMain8003服务
④重新编写cloud-provider-payment8001服务中的PaymentController类
@RestController
public class PaymentController {@Autowiredprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@GetMapping("/payment/get/{id}")public CommonResult getById(@PathVariable Long id){Payment payment = paymentService.getById(id);return new CommonResult(200,"查询成功----端口"+serverPort,payment);}
}
③重新启动PaymentMain8001、PaymentMain8002、PaymentMain8003服务
2.在cloud-consumer-order8000服务中添加Ribbon负载均衡
org.springframework.cloud spring-cloud-starter-netflix-ribbon
注意:这个不需要手动引用,Eureka客户端自带Ribbon
3.改写cloud-consumer-order8000服务中OrderController请求方式,通过Eureka服务拿到地址
@RestController
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/order/get/{id}")public CommonResult getById(@PathVariable long id){return restTemplate.getForObject("http://CLOUD-PAYMENT-SERVICE:8001/payment/get/" + id , CommonResult.class);}
}
4.在config.ApplicationContextConfig配置类中的getRestTemple方法上添加 @LoadBalanced注解
@SpringBootConfiguration
public class ApplicationContextConfig {@Bean@LoadBalanced //启用RestTemplate负载均衡的能力,才能通过服务名远程调用public RestTemplate getRestTemplate(){return new RestTemplate();}
}
5.在启动类上添加@RibbonClient注解
@SpringBootApplication
@EnableEurekaClient
public class OrderMain8000 {public static void main(String[] args) {SpringApplication.run(OrderMain8000.class,args);}
}
6.直接启动cloud-consumer-order8000服务测试默认的负载均衡策略
在ribbon中,默认的负载均衡策略是轮询
访问:http://localhost:8000/order/get/31
第一次查询结果:
第二次查询的结果
第三次的结果
以后查询的都依次顺序循环。轮询的顺序与你启动服务的顺序有关。
配置负载均衡策略的核心组件IRule
结构体系
策略介绍:
1.在启动类的上一级目录中创建配置规则包rule,在该包下创建策略
@Component
public class MyRule {/*** @Description: 存储一个随机的负载均衡的对象,到Spring容器中* 注意:不能被SpringBoot启动类扫描到,* 一旦被扫描到,该对象会应用到全局(对所有服务的调用都使用随机的访问方式)* @Author: TCL* @Date: 2022/11/25* @Time: 20:32*/@Beanpublic IRule randomRule(){return new RandomRule();}
}
2.在配启动类上配置@RibbonClient,让其扫描到该类将IRule添加到IOC容器中
@SpringBootApplication
@RibbonClient(value = "CLOUD-PAYMENT-SERVICE", configuration = MyRule.class)
public class OrderMain8000 {public static void main(String[] args) {SpringApplication.run(OrderMain8000.class,args);}
}
3.重启服务测试
访问:http://localhost:8000/order/get/31
每次访问结果都是随机的。
在yml中配置方式如下:
user-service: # 生产者服务名称
ribbon:
NFloadBalancerRuleClassName: XxxRule # 负载均衡策略
@Bean、@LoadLoadBalanced
注解http://应用服务名称:端口号/资源访问路径
1.能做什么
2.Feign集成了 Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
3.Feign和OpenFeign两者区别
4.实现图解
实现思想:微服务调用接口+@FeignClient
新项目中的内容与cloud-consumer-order8000一样,并且openFeign也是自带ribbon
org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-test test com.tlc cloud-api-commons 1.0-SNAPSHOT
server:port: 8000
spring:application:name: cloud-consumer-feign-order8000
eureka:client:register-with-eureka: truefetchRegistry: trueservice-url:defaultZone: http://localhost:7001/eureka
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderFeignMain8000 {public static void main(String[] args) {SpringApplication.run(OrderFeignMain8000.class,args);}
}
在service包下创建接口类
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //声明远程调用的服务名称
public interface PaymentFeignService {/*** @Description: 远程调用方法* @Author: TCL* @Date: 2022/11/25* @Time: 21:31*/@GetMapping("/payment/get/{id}")public CommonResult getById(@PathVariable("id") Long id);
}
@RestController
public class OrderController {// 调用远程微服务接口@Resourceprivate PaymentFeignService paymentFeignService;@GetMapping("/order/get/{id}")public CommonResult getById(@PathVariable long id){return paymentFeignService.getById(id);}
}
访问:http://localhost:8000/order/get/31
发现测试成功。
1.在服务提供方cloud-provider-payment8001故意写暂停程序
/*** @Description: 暂停程序* @Author: TCL* @Date: 2022/11/25* @Time: 21:55*/@GetMapping(value = "/payment/feign/timeout")public String paymentFeignTimeout(){try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();} //单位秒return "端口:"+serverPort;}
2.在消费端cloud-consumer-feign-order8000的服务调用接口PaymentFeignService中编写远程访问方法
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
3.在消费端cloud-consumer-feign-order8000的控制方法中调用超时方法
@GetMapping("/consumer/payment/feign/timeout")public String paymentFeignTimeout() {return paymentFeignService.paymentFeignTimeout();}
4.启动服务测试
http://localhost:8000/order/payment/feign/timeout
错误页面,OpenFeign默认等待一秒钟,超过后报错
#设置Feign客户端超时时间(openfeign默认支持ribbon)
ribbon:ConnectTimeout: 3000 #连接等待的最大时间,默认1000毫秒ReadTimeout: 3000 #请求处理等待的最大时间,默认1000毫秒MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用MaxAutoRetriesNextServer: 1 #切换服务的最大次数,不包括第一个服务OkToRetryOnAllOperations: false #是否所有请求都重试,false-仅get请求重试
#hystrix的超时时间
hystrix:command:default:execution:timeout:enabled: trueisolation:thread:timeoutInMilliseconds: 9000
一般情况下 都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制)
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,源码如下
要开启Feign的重试机制如下:(Feign默认重试五次 源码中有)
@Bean
Retryer feignRetryer() {return new Retryer.Default();
}
(MaxAutoRetries+1)*(MaxAutoRetriesNextServer+1)
一共产生4次调用。(1 + MaxAutoRetries+MaxAutoRetriesNextServer) * ReadTimeout
即按照以上的配置 hystrix的超时时间应该配置为 (1+1+1)*3=9秒
当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试
,如果是put或post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。如果不配置ribbon的重试次数,默认会重试一次注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试 非GET方式请求,只有连接异常时,才会进行重试
配置超时后,再次测试,发现发送请求后并不是立即报错,而是等待设置的超时时间过后报错。
日志打印:
日志级别
FULL
:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据在配置config报下创建FeignConfig,将日志级别加载到spring容器中
@Configuration
public class FeignConfig {@Beanpublic Logger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}
#开启OpenFeign日志打印功能
logging:level:com.tlc.service.PaymentFeignService: debug
访问:http://localhost:8000/order/get/31
复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免的失败。
服务雪崩
多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出"。
如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
也可以这样说:当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)
延迟
和容错
的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常
等。不会导致整体服务失败,避免级联故障
,以提高分布式系统的弹性。断路器
本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方无法处理的异常
,这样就保证了服务调用方的线程不会被长时间、不必要地占用
,从而避免了故障在分布式系统中的蔓延,乃至雪崩。秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行Flowlimit
下载地址:https://archive.apache.org/dist/jmeter/binaries/
添加一个并发请求的方式:
1.构建模块cloud-provider-hystrix-payment8001,并导入相关依赖
org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-netflix-eureka-client com.tlc cloud-api-commons 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
2.配置yml文件
server:port: 8001spring:application:name: cloud-hystrix-payment-serviceeureka:client:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://localhost:7001/eureka/
3.编写启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentHystrixMain8001.class,args);}
}
4.编写service接口以及实现类serviceImpl
public interface PaymentService {/*** @Description: 执行成功* @Author: TCL* @Date: 2022/11/25* @Time: 23:10*/public String paymentInfo_OK(Integer id);/*** @Description: 超时方法* @Author: TCL* @Date: 2022/11/25* @Time: 23:10*/public String payment_Timeout(Integer id);
}@Service
public class PaymentServiceImpl implements PaymentService {//成功public String paymentInfo_OK(Integer id){return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;}//失败public String payment_Timeout(Integer id){int timeNumber = 3;try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;}
}
5.编写controller类
@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Integer id){String result = paymentService.paymentInfo_OK(id);log.info("*******result:"+result);return result;}@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfo_Timeout(@PathVariable("id") Integer id){String result = paymentService.payment_Timeout(id);log.info("*******result:"+result);return result;}
}
6.启动服务测试
7.通过Jmeter进行压力测试
压测的过程中再来访问一下微服务
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
演示结果
压力测试结论
1.搭建cloud-consumer-feign-hystrix-order8000模块,并导入依赖
org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-starter-netflix-eureka-client com.tlc cloud-api-commons 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
2.编写yml配置文件
server:port: 8001spring:application:name: cloud-consumer-hystrix-ordereureka:client:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://localhost:7001/eureka/
4.编写启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderHystrixMain8000 {public static void main(String[] args) {SpringApplication.run(OrderHystrixMain8000.class,args);}
}
5.启动服务测试
访问:http://localhost:8000/consumer/payment/hystrix/ok/32
6.高并发测试
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.Sat Nov 26 08:15:59 CST 2022
There was an unexpected error (type=Internal Server Error, status=500).
Read timed out executing GET http://CLOUD-HYSTRIX-PAYMENT-SERVICE/payment/hystrix/timeout/32
feign.RetryableException: Read timed out executing GET http://CLOUD-HYSTRIX-PAYMENT-SERVICE/payment/hystrix/timeout/32at feign.FeignException.errorExecuting(FeignException.java:249)at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:129)at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)at com.sun.proxy.$Proxy107.paymentInfo_Timeout(Unknown Source)at com.tlc.controller.OrderHystrixController.paymentInfo_Timeout(OrderHystrixController.java:33)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)
服务的降级可以在消费端配置,也可以在服务提供端配置。首先看在服务端配置值服务降级,这里会使用@HystrixCommand
配置降级。该注解作用到方法上。表示该方法因为某种原因,该方法不能正常返回数据,降级使用备用的方法,返回数据。
在服务提供端使用服务降级的场景:
①在service层中,设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback。
@Service
public class PaymentServiceImpl implements PaymentService {//成功public String paymentInfo_OK(Integer id) {return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "哈哈哈";}/*** @Description: 设置超时时间,超过时间自动降级处理,走降级方法。* @Author: TCL* @Date: 2022/11/27* @Time: 8:06*/@HystrixCommand(// 降级方法调用fallbackMethod = "payment_TimeoutHandler" ,commandProperties = {// 设置超时时间,超时5s自动走降级方法@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")})public String payment_Timeout(Integer id) {
// int timeNumber = 3;int timeNumber = 10; // 模拟超时时间
// int i = 1/0; // 模拟异常try {TimeUnit.SECONDS.sleep(timeNumber);} catch (Exception e) {e.printStackTrace();}return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;}//兜底方法,上面方法出问题,我来处理,返回一个出错信息public String payment_TimeoutHandler(Integer id) {return "服务提供端,线程池:"+Thread.currentThread().getName()+" payment_TimeoutHandler,系统繁忙,请稍后再试\t o(╥﹏╥)o ";}
}
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
②在启动类上添加@EnableHystrix
注解
@SpringBootApplication
@EnableHystrix
@EnableEurekaClient
public class PaymentHystrixMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentHystrixMain8001.class,args);}
}
③启动服务,测试程序
访问:http://localhost:8001/payment/hystrix/timeout/31
测试超时和算数异常,都会走兜底方法——服务降级
订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端降级保护。注意:服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。在消费端中使用服务降级的场景:
1.在yml中配置,在远程调用出错时启用Hystrix
feign:hystrix:enabled: true #远程调用出错时,启用hystrix
2.在启动类上添加@EnableHystrix
注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain8000 {public static void main(String[] args) {SpringApplication.run(OrderHystrixMain8000.class,args);}
}
3.在业务中编写服务降级OrderHystrixController
@RestController
@Slf4j
public class OrderHystrixController {@Resourceprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Integer id){String result = paymentHystrixService.paymentInfo_OK(id);log.info("*******result:"+result);return result;}@HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")//超过1.5秒就降级自己})@GetMapping("/consumer/payment/hystrix/timeout/{id}")public String paymentInfo_Timeout(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级int timeout = 3000; // 模拟超时,导致降级if (timeout != 0){try {Thread.sleep(timeout);} catch (InterruptedException e) {e.printStackTrace();}}String result = paymentHystrixService.paymentInfo_Timeout(id);log.info("*******result:"+result);return result;}//兜底方法,上面方法出问题,我来处理,返回一个出错信息public String payment_TimeoutHandler(Integer id) {return "服务的消费端8000,对方支付系统繁忙请10秒后再试。或自己运行出错,请检查自己。";}
}
4.启动服务进行测试
访问:http://localhost:8000/consumer/payment/hystrix/timeout/31
出现的问题
如果没有特殊的处理方法,就使用默认的降级方法,通过@DefaultProperties(defaultFallback = “”)+@DefaultProperties(defaultFallback = “”)设置统一跳转到统一处理结果页面,通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。
1.代码实现
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {@Resourceprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Integer id){String result = paymentHystrixService.paymentInfo_OK(id);log.info("*******result:"+result);return result;}@HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")//超过1.5秒就降级自己})@GetMapping("/consumer/payment/hystrix/timeout/{id}")public String paymentInfo_Timeout(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级int timeout = 3000; // 模拟超时,导致降级if (timeout != 0){try {Thread.sleep(timeout);} catch (InterruptedException e) {e.printStackTrace();}}String result = paymentHystrixService.paymentInfo_Timeout(id);log.info("*******result:"+result);return result;}//兜底方法,上面方法出问题,我来处理,返回一个出错信息public String payment_TimeoutHandler(Integer id) {return "服务的消费端8000,对方支付系统繁忙请10秒后再试。或自己运行出错,请检查自己。";}@GetMapping("/consumer/payment/hystrix/timeout02/{id}")public String paymentInfo_Timeout02(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级int timeout = 3000; // 模拟超时,导致降级if (timeout != 0){try {Thread.sleep(timeout);} catch (InterruptedException e) {e.printStackTrace();}}String result = paymentHystrixService.paymentInfo_Timeout(id);log.info("*******result:"+result);return result;}@GetMapping("/consumer/payment/hystrix/timeout03/{id}")public String paymentInfo_Timeout03(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级int timeout = 3000; // 模拟超时,导致降级if (timeout != 0){try {Thread.sleep(timeout);} catch (InterruptedException e) {e.printStackTrace();}}String result = paymentHystrixService.paymentInfo_Timeout(id);log.info("*******result:"+result);return result;}//下面是全局fallback方法public String payment_Global_FallbackMethod(){return "Global异常处理信息,请稍后再试,(┬_┬)";}
}
2.重启服务器,测试程序
访问:http://localhost:8000/consumer/payment/hystrix/timeout02/31
http://localhost:8000/consumer/payment/hystrix/timeout03/31
测试结果都是:
3.出现的问题
服务的降级方法和业务逻辑混一起,会出现耦合。服务降级,客户端去调用服务端,碰上服务端宕机或关闭。可能导致整个服务宕机。解决方式只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
未来我们要面对的异常
再看我们的业务类PaymentController
修改cloud-consumer-feign-hystrix-order8000
根据cloud-consumer-feign-hystrix-order8000已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理PaymentFallbackService类实PaymentFeignClientService接口.
1.定义PaymentFallbackService类实PaymentFeignClientService
@Component
public class PaymentFallbackService implements PaymentHystrixService {@Overridepublic String paymentInfo_OK(Integer id) {return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";}@Overridepublic String paymentInfo_Timeout(Integer id) {return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";}
}
2.配置yml,添加容错机制
feign:hystrix:enabled: true #远程调用出错时,启用hystrix
3.编写PaymentFeignClientService接口
@FeignClient(value = "CLOUD-HYSTRIX-PAYMENT-SERVICE",fallback = PaymentFallbackService.class)
@Component
public interface PaymentHystrixService {@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Integer id);@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfo_Timeout(@PathVariable("id") Integer id);
}
4.测试程序
1.断路器
1.找到修改PaymentServiceImpl,添加服务熔断机制
//服务熔断@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //当在配置时间窗口内达到此数量,打开断路,默认20个@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //断路多久以后开始尝试是否恢复,默认5s@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%})public String paymentCircuitBreaker(Integer id){if (id < 0){throw new RuntimeException("*****id 不能负数");}String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;}public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;}
2.修改PaymentController,添加方法
//===服务熔断@GetMapping("/payment/circuit/{id}")public String paymentCircuitBreaker(@PathVariable("id") Integer id){String result = paymentService.paymentCircuitBreaker(id);log.info("*******result:"+result);return result;}
3.启动服务进行测试
自测cloud-provider-hystrix-payment8001
正确: http://localhost:8001/payment/circuit/31
错误: http://localhost:8001/payment/circuit/-31
一次正确一次错误trytry
重点测试
多次错误(狂点),然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路
1.大神总结
2.熔断器的类型
熔断打开:
请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态。熔断关闭:
熔断关闭不会对服务进行熔断熔断半开:
部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常。关闭熔断 :
当服务一直处于正常状态 能正常调用3.官网断路器流程图
①官网步骤
②断路器在什么情况下开始起作用
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"), //当快照时间窗(默认10秒)内达到此数量才有资格打开断路,默认20个@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"), //断路多久以后开始尝试是否恢复,默认5s@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
})
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值。
配置属性参考:https://github.com/Netflix/Hystrix/wiki/Configuration
快照时间窗 :
断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。请求总数阈值:
在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开错误百分比阈值:
当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。③断路器开启或者关闭的条件
④断路器打开之后
⑤All配置
因为Hystrix停止维护了,所以后面用阿里的Sentinel来实现服务限流
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard)
,Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
1.新建一个模块:cloud-consumer-hystrix-dashboard9001,并导入依赖
org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
2.配置yml文件
server:port: 9001
hystrix:dashboard:proxy-stream-allow-list: "*"
3.创建启动类,添加新的注解@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {public static void main(String[] args) {SpringApplication.run(HystrixDashboardMain9001.class,args);}
}
4.所有的服务提供者添加新的依赖
org.springframework.boot spring-boot-starter-actuator
5.启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
访问:http://localhost:9001/hystrix
1. 修改cloud-provider-hystrix-payment8001,添加一个配置类
@Configuration
public class HystrixConfig {/***此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",*只要在自己的项目里配置上下面的servlet就可以了*/@Beanpublic ServletRegistrationBean getServlet() {HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);registrationBean.setLoadOnStartup(1);registrationBean.addUrlMappings("/hystrix.stream");registrationBean.setName("HystrixMetricsStreamServlet");return registrationBean;}}
2.测试程序
9001监控8001
http://localhost:8001/hystrix.stream
测试地址
http://localhost:8001/payment/circuit/31
http://localhost:8001/payment/circuit/-31
上述测试通过
ok
先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
监控结果,成功
监控结果,失败
如何看
7色
1圈
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实体中快速的发现故障实例和高压力实例。
1线
曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
整体图说明
整体图说明二
监控多种复杂的服务提提供端
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关https://github.com/Netflix/zuul/wiki
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关代替Zull,那就是SpringCloud Geteway;
总之一句话:Geteway是原Zuul1.x版的替代
Route(路由):
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
Predicate(断言):
参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
Filter(过滤):
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
Web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
Predicate就是我们的匹配条件: 而Filter,就是可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler.
Handler再通过指定的过滤器链来将请求发送给我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量控制等有着非常重要的作用
网关的核心逻辑是:路由转发+执行过滤器链
创建新的模块cloud-gateway-gateway9527并导入相关依赖
org.springframework.cloud spring-cloud-starter-gateway com.tlc cloud-api-commons 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
server:port: 9527
spring:application:name: cloud-gatewayeureka:instance:hostname: cloud-gateway-serviceclient:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://localhost:7001/eureka
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {public static void main(String[] args) {SpringApplication.run(GateWayMain9527.class,args);}
}
cloud:gateway:routes:- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001 #匹配后提供服务的路由地址predicates:- Path=/payment/get/** #断言,路径相匹配的进行路由- id: payment_routh2uri: http://localhost:8001predicates:- Path=/payment/lb/** #断言,路径相匹配的进行路由
启动7001:cloud-eureka-server7001
启动8001:cloud-provider-payment8001
启动9527网关:cloud-gateway-gateway9527
访问说明
添加网关前: http://localhost:8001/payment/get/31
添加网关后: http://localhost:9527/payment/get/31
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
一个eureka7001+两个服务提供者8001/8002
修改uri属性:uri: lb://服务名称
discovery:locator:enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
cloud:gateway:discovery:locator:enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由routes:- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/payment/get/** #断言,路径相匹配的进行路由- id: payment_routh2
# uri: http://localhost:8001uri: lb://cloud-payment-servicepredicates:- Path=/payment/lb/** #断言,路径相匹配的进行路由
访问:http://localhost:9527/payment/get/31
启动我们的gatewat9527,查看启动日志
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapper基础框架的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合
Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给 Route。Spring Cloud Gateway包含许
多内置的Route Predicate Factories。
所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and 。
predicates: #断言,匹配方式,可以有多个,多个同时存在时,必须同时满足才能路由#- Path=/payment/get/** #断言,路径相匹配的进行路由#- After=2022-11-26T15:40:50.789+08:00[Asia/Shanghai] #在指定的时间之后访问#- Before=2022-11-26T15:40:50.789+08:00[Asia/Shanghai] #在指定的时间之前访问#- Between=2022-11-26T15:40:50.789+08:00[Asia/Shanghai], 2022-11-26T16:40:50.789+08:00[Asia/Shanghai] #在两个时间之间访问#- Cookie=chocolate, ch.p #请求头携带参数,参数名cookie 参数值chocolate=ch.p#- Header=X-Request-Id, \d+ #请求头携带参数,参数名X-Request-Id,参数值\d+(正则表达式,最少一个数字)#- Host=localhost:9527,**.com.org #请求主机地址为localhost:9527或者**.com.org时#- Method=GET,POST #请求方式为GET或POST#- Query=green #请求参数中包含名为 green 的参数
Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
GatewayFilter 局部过滤器,是针对单个路由的过滤器。
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
AddRequestParameter
yml配置
filters: #内置的过滤器- AddRequestParameter=red, blue #添加请求参数,red=blue
在服务提供者cloud-provider-payment8001中编写代码获取参数
@GetMapping("/payment/get/{id}")public CommonResult getById(@PathVariable Long id, HttpServletRequest request) throws JsonProcessingException {//获取所有的请求参数Map parameterMap = request.getParameterMap();//转为json, 使用的API为jacksonObjectMapper objectMapper = new ObjectMapper();System.out.println(objectMapper.writeValueAsString(parameterMap));Payment payment = paymentService.getById(id);return new CommonResult(200,"查询成功----端口"+serverPort,payment);}
重启服务测试
自定义全局过滤器可以实现全局日志记录、统一网关鉴权等功能。
@Slf4j
@Component
public class LoginFilter implements GlobalFilter, Ordered {/*** 模拟登陆过滤器* 参数中需要传递一个username* 如果没有,则判定为未登录,拦截请求*/@Overridepublic Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("**网关**请求进入自定义的过滤器>>>>>>>>>>>>>>>>>>>>");//获取请求参数usernameString username = exchange.getRequest().getQueryParams().getFirst("username");if(username == null){//1.拦截请求>>>>>>>>>>>>>>>>>//设置响应的状态码406exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//阻止请求继续执行,直接由网关完成响应return exchange.getResponse().setComplete();}//2.放行,让请求继续往后执行>>>>>>>>>>>>>>>>>return chain.filter(exchange);}@Overridepublic int getOrder() {//过滤器的优先级,数值越小,优先级越高return 0;}
}
Spring Cloud Sleuth 其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程,捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。
Zipkin 是 Twitter 的一个开源项目,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
Spring Cloud Sleuth和OpenZipkin(也称为Zipkin)集成。Zipkin是一个分布式跟踪平台,可用于跟踪跨多个服务调用的事务。Zipkin允许开发人员以图形方式查看事务占用的时间量,并分解在调用中涉及的每个微服务所用的时间。在微服务架构中,Zipkin是识别性能问题的宝贵工具。
建立Spring Cloud Sleuth和Zipkin涉及4项操作:
下载:https://github.com/openzipkin/zipkin
curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar
运行控制台
http://localhost:9411/zipkin/
1.添加依赖
org.springframework.cloud spring-cloud-starter-zipkin
2.配置yml文件
zipkin:base-url: http://localhost:9411sleuth:sampler:#采样率值介于0~1之间,1表示全部采样probability: 1
3.业务类PaymentController
@GetMapping("/payment/zipkin")
public String paymentZipkin(){return "hi ,i'am paymentzipkin server,welcome to atguigu,O(∩_∩)O哈哈~";
}
1.添加依赖
org.springframework.cloud spring-cloud-starter-zipkin
2.配置yml文件
zipkin:base-url: http://localhost:9411sleuth:sampler:#采样率值介于0~1之间,1表示全部采样probability: 1
3.业务类OrderController
//==> zipkin+sleuth
@GetMapping("/consumer/payment/zipkin")
public String paymentZipkin(){String result = restTemplate.getForObject("http://CLOUD-PAYMENT-SERVICE"+"/payment/zipkin/", String.class);return result;
}
① 访问:http://localhost:9411/zipkin/
②访问:http://localhost:8000/consumer/payment/zipkin
③ 打开zipkin页面,点击run query
④点击show,就可以看见详情
Spring Cloud Netflix项目进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now
说明:
什么是维护模式?
进入维护模式意味着什么?
诞生:2018.10.31,Spring Cloud Alibaba正式入驻了Spring Cloud官网孵化器,并在Maven中央库发布了第一个版本。
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.6.RELEASE pom import
一整套解决方案,简单理解就是替换Netflix那一套
Sentinel: 把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos: 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ: 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo: Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata: 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud ACM: 一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
Nacos = Eureka+Config+Bus
据说nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
https://github.com/alibaba/nacos/releases/tag/1.4.2
解压安装包,直接在命令行启动
startup.cmd -m standalone
默认:MODE="cluster"集群方式启动,如果单机启动需要设置-m standalone参数,否则,启动失败。
http://localhost:8848/nacos
默认账号密码都是nacos
https://spring.io/projects/spring-cloud-alibaba#learn
1.向父类springcloudparent中添加新的依赖
com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.6.RELEASE pom import
2.新建Module:cloudalibaba-provider-payment19001,并导入依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
3.编写yml配置文件
server:port: 19001spring:application:name: nacos-payment-providercloud:nacos:discovery: #nacos注册中心>>>>>>>>>>>>>>>>server-addr: localhost:8848 #配置Nacos地址#springboot-actuator监控组件
management:endpoints:web:exposure:include: '*' #默认只公开了/health和/info端点,要想暴露所有端点只需设置成星号
4.添加启动类,在启动类上添加@EnableDiscoveryClient注解
@RestController
public class PaymentController {@Value("${server.port}")private String serverPort;@GetMapping("/payment/get/{id}")public String get(@PathVariable("id") Integer id) {return "支付服务调用成功" + id + ",端口:" + serverPort;}
}
6.启动程序测试
访问:http://localhost:19001/payment/get/1
nacos服务注册中心+服务提供者19001都ok了
8.为了下一章节演示nacos的负载均衡,参照19001新建19002
1. 新建Module:cloudalibaba-consumer-nacos-order18000,并导入依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.tlc cloud-api-commons 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
为什么支持负载均衡
2.配置yml文件
server:port: 18000spring:application:name: nacos-order-consumercloud:nacos:discovery:server-addr: localhost:8848
3.创建启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain18000 {public static void main(String[] args){SpringApplication.run(OrderNacosMain18000.class,args);}
}
4.创建配置文件,将RestTemplete加载到spring容器中
@Configuration
public class ApplicationContextConfig {@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){return new RestTemplate();}}
5.创建controller控制层,调用服务提供者
@RestController
@Slf4j
public class OrderNacosController{@Autowired //需要自己创建并存入Spring容器,此处才能注入private RestTemplate restTemplate;@GetMapping("/consumer/payment/get/{id}")public String get(@PathVariable("id") Integer id){//远程访问 支付服务 提供的接口String result = restTemplate.getForObject("http://nacos-payment-provider/payment/get/" + id, String.class);return "消费者远程访问获取到的数据:"+result;}
}
6.测试
访问:http://localhost:18000/consumer/payment/get/1
1. Nacos和CAP
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。
因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。
2. Nacos支持AP和CP模式的切换
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
1. 创建Module:cloudalibaba-config-nacos-client3377,并导入依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
2.配置yml文件
Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
bootstrap.yml
server:port: 3377spring:application:name: nacos-config-clientcloud:nacos:discovery:server-addr: localhost:8848 #服务注册中心地址config:server-addr: localhost:8848 #配置中心地址file-extension: yaml #指定yaml格式的配置(yml和yaml都可以)#${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
#nacos-config-client-dev.yaml (一定要与file-extension值保持一致)
application.yml
spring:profiles:active: dev #表示开发环境
3.添加启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfigClientMain3377 {public static void main(String[] args) {SpringApplication.run(NacosConfigClientMain3377.class,args);}
}
4.编写控制层ConfigClientController
@RestController
@RefreshScope //通过SpringCould原生注解@RefreshScope实现配置自动更新
public class ConfigClientController{@Value("${config.info}") //对应nacos配置:nacos-config-client-dev.yamlprivate String configInfo;@GetMapping("/config/info")public String getConfigInfo() {return configInfo;}
}
5.在Nacos中添加配置信息
Nacos中的匹配规则
Nacos中的dataid的组成格式与SpringBoot配置文件中的匹配规则
官网 https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
最后公式:
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
nacos-config-client-dev.yaml
Nacos界面配置对应
config:info: "config info for dev,from nacos config center."
设置DataId
公式: ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
小总结说明
历史配置:Nacos会记录配置文件的历史版本默认保留30天
启动前需要在nacos客户端-配置管理-配置管理栏目下有没有对应的yaml配置文件
运行cloud-config-nacos-client3377的主启动类
调用接口查看配置信息: http://localhost:3377/config/info
8. 自带动态刷新
修改Nacos中的yaml配置文件,查看配置已经刷新
1.问题
多环境多项目管理
2. Nacos的图形化管理界面
4.案例
(1).DataID方案
指定spring.profiles.active和配置文件的DataID来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个DataID
新建dev配置DataID
新建test配置DataID
通过spring.profile.active属性就能进行多环境下配置文件的读取
测试
http://localhost:3377/config/info
配置是什么就加载什么
(2).Group方案
通过Group实现环境区分
新建Group
在nacos图形界面控制台上面新建配置文件DataID
bootstrap+application
在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP
(3)Namespace方案
新建dev/test的Namespace
回到服务管理-服务列表查看
按照域名配置填写
nacos-config-client-dev.yamlconfig: info: 9f62d48c-ef2e-4d83-a9fb-c9db5833f93b DEFAULT_GROUP nacos-config-client-dev.yaml
YML
bootstrap
# nacos配置
server:port: 3377spring:application:name: nacos-config-clientcloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址config:server-addr: localhost:8848 #Nacos作为配置中心地址file-extension: yaml #指定yaml格式的配置group: DEV_GROUPnamespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml
application
spring:profiles:active: dev # 表示开发环境#active: test # 表示测试环境#active: info
测试结果:
https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
1.官网架构图
集群部署架构图
因此开源的时候推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
2. 上图官网翻译,真实情况
3. 说明
默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL 的存储
。
https://github.com/alibaba/nacos/blob/master/distribution/conf/application.properties
https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql
https://nacos.io/zh-cn/docs/deployment.html
1. Nacos默认自带的是嵌入式数据库derby
https://github.com/alibaba/nacos/blob/develop/config/pom.xml
2. derby到mysql切换配置步骤
①在nacos-server-1.4.2\nacos\conf目录下找到sql脚本nacos-mysql.sql执行脚本
②在nacos-server-1.4.2\nacos\conf目录下找到application.properties
spring.datasource.platform=mysqldb.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
db.user=root
db.password=root
3. 启动nacos,可以看到是个全新的空记录界面,以前是记录进derby
4. 测试:新建配置,发现配置信息写入了MySQL数据库
1. 预计需要,1个nginx+3个nacos注册中心+1个mysql
2. Nacos下载linux版本,进行环境准备
https://github.com/alibaba/nacos/releases/tag/1.4.2
nacos-server-1.4.2.tar.gz
3. 集群配置步骤(重点)
①.创建/opt/nacoscluster目录,解压3个节点
②.Linux服务器上mysql数据库配置
③.三个节点conf/application.properties配置
修改每一个节点下server.port,分别设置为:8848,8849,8850,并添加以下配置
spring.datasource.platform=mysqldb.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config? characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=root
④三个节点/conf下配置cluster.conf
192.168.6.100:8848
192.168.6.100:8849
192.168.6.100:8850
⑤.启动三个节点前,修改内存大小
启动时,需要修改startup.sh文件内存大小,否则,内存可能不够用。
⑥启动三个节点
查看进程:ps -ef | grep nacos
登录任意节点查看集群:
⑦.Nginx的配置,由它作为负载均衡器
修改nginx的配置文件:vim /usr/local/nginx/conf/nginx.conf
nginx.conf
upstream nacoscluster{ server localhost:8848;server localhost:8849;server localhost:8850;
}server{ listen 1111;server_name localhost;location / {proxy_pass http://nacoscluster; }
....省略
4.截止到此处,1个Nginx+3个nacos注册中心+1个mysql
测试通过nginx访问nacos
http://192.168.6.100:1111/nacos
新建一个配置测试
linux服务器的mysql插入一条记录
5. 测试微服务从配置中心获取配置
微服务nacos-config-client启动注册进nacos集群
Yml
访问URL:
6. 高可用小总结
https://github.com/alibaba/Sentinel
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
一句话解释,之前我们讲解过的Hystrix
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
Sentinel 的主要特性:
Sentinel 的开源生态:
Sentinel 分为两个部分:
Sentinel 社区正在将流量治理相关标准抽出到 OpenSergo 标准中,Sentinel 作为流量治理标准实现。有关 Sentinel 流控降级与容错 spec 的最新进展,请参考 opensergo-specification,也欢迎社区一起来完善标准与实现。
下载:https://github.com/alibaba/Sentinel/releases
使用方式:https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_spring_cloud_alibaba_sentinel
请使用groupId为com.alibaba.cloud和artifactId为spring-cloud- start-alibaba-naco -discovery的启动器。
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
Sentinel 分为两个部分:
1. 下载
https://github.com/alibaba/Sentinel/releases
下载到本地sentinel-dashboard-1.8.2.jar
2. 运行命令
前提java8环境OK8080端口不能被占用
命令java -jar sentinel-dashboard-1.8.2.jar
3. 访问sentinel管理界面
http://localhost:8080
登录账号密码均为sentinel
访问:http://localhost:8848/nacos/
1.建立新的模块:cloudalibaba-sentinel-service18001,并导入依赖
com.tlc cloud-api-commons 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.csp sentinel-datasource-nacos com.alibaba.cloud spring-cloud-starter-alibaba-sentinel org.springframework.cloud spring-cloud-starter-openfeign org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true cn.hutool hutool-all 4.6.3 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
2.配置yml文件
server:port: 18001spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:dashboard: localhost:8080port: 8719 #默认8719,应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用HttpServermanagement:endpoints:web:exposure:include: '*'
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints
3.编写启动类
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp18001 {public static void main(String[] args) {SpringApplication.run(MainApp18001.class,args);}
}
4.编写控制层方法FlowLimitController
@RestController
public class FlowLimitController{@GetMapping("/testA")public String testA() {return "------testA";}@GetMapping("/testB")public String testB() {return "------testB";}
}
5.启动服务测试
①启动Sentinel8080
②启动微服务cloudalibaba-sentinel-service18001,然后查看Sentinel8080后台
空空如也,啥都没有
Sentinel采用的懒加载说明
执行一次访问即可
http://localhost:18001/testA
http://localhost:18001/testB
结论
sentinel8080正在监控微服务8401
1.直接(默认)
2.关联
3.配置A
5.链路
1. 直接->快速失败(默认的流控处理)
2. 预热
说明
官网: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6#%E5%9F%BA%E4%BA%8Eqps%E5%B9%B6%E5%8F%91%E6%95%B0%E7%9A%84%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考流量控制-Warm Up文档,具体的例子可以参见WarmUpFlowDemo。
通常冷启动的过程系统允许通过的QPS曲线如下图所示:
默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值。
限流 冷启动https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8
源码
Warmup配置
3. 排队等待
@GetMapping("/testB")
public String testB() {log.info(Thread.currentThread().getName()+"\t ...testB");return "------testB";
}
概述
熔断策略
Sentinel 提供以下几种熔断策略:
注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。
界面展示
1.慢调用比例
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
①编写控制层代码
@GetMapping("/testA")public String testA() {try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}return "------testA";}
②配置熔断规则
③测试
当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
①编写控制层代码
@GetMapping("/testB")
public String testB() {int age = 10/0;return "------testB";
}
②配置熔断规则
③测试
3.异常数
当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
①编写控制层代码
@GetMapping("/testB")
public String testB(){int age = 10/0;return "------testB 测试异常数";
}
②配置熔断规则
③测试
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
1.编写controller控制层代码
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2) {//int age = 10/0;return "------testHotKey";
}//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){return "------deal_testHotKey,o(╥﹏╥)o";
}
2.配置热点规则
3.重启服务测试程序
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
1.8.0 版本开始,defaultFallback 支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。
1.编写控制层方法
//注意: 此处的value不能是/testC,否则请求/testC时会直接调用自定义的兜底方法
@SentinelResource(value = "testC", blockHandler = "backup_testC")
@GetMapping("/testC")
public String testC() {return "请求执行成功......testC";
}//自定义兜底方法
public String backup_testC(BlockException e) {e.printStackTrace();return "自定义兜底数据......testC";
}
2.编写流量控制规则
1.自定义限流处理逻辑
public class CustomerBlockHandler {public static String handleException1(BlockException exception){return "自定义限流处理信息.... CustomerBlockHandler --- 1";}public static String handleException2(BlockException exception){return "自定义限流处理信息.... CustomerBlockHandler --- 2";}
}
2.编写控制层FlowLimitController
@SentinelResource(value = "testD", blockHandler = "handleException1",blockHandlerClass = CustomerBlockHandler.class)
@GetMapping("/testD")
public String testD() {return "请求执行成功......testD";
}
3.重启程序测试
/*** @SentinelResource 与 Hystrix 组件中的@HystrixCommand注解作用是类似的。* value = "byResourceName" 用于设置资源名称,名称不能与方法的映射路径完全一致,否则正常的请求会直接调用降级方法* blockHandler 用于引用降级方法。* blockHandlerClass 用于引用降级方法的处理器类。注意:降级方法必须是static的。否则,无法解析* blockHandler + blockHandlerClass 只处理配置违规,进行降级处理。代码出现异常,不执行的。** blockHandler + fallback 同时存在,配置违规,代码也有异常,这时,走blockHandler配置文件降级处理** exceptionsToIgnore 设置特定异常不需要降级处理,将异常原样抛出。*/@RequestMapping("/fallback/{id}")@SentinelResource(value = "byFallbackName",blockHandler = "handleException2", blockHandlerClass = CustomerBlockHandler.class,fallback = "handleException3", fallbackClass = CustomerBlockHandler.class,exceptionsToIgnore = IllegalArgumentException.class)public String fallback(@PathVariable("id") Long id) {if (id == 4) {throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");}if (id == -1) {CommonResult result = new CommonResult<>(444, "数据不存在", null);throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return "请求成功,获取到数据" + id;}
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发控制) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于慢调用比例、异常比例、异常数 | 基于异常比例 | 基于异常比例、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 RxJava) | Ring Bit Buffer |
动态规则配置 | 支持近十种动态数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
单机限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
集群流控 | 支持 | 不支持 | 不支持 |
流量整形 | 支持预热模式与匀速排队控制效果 | 不支持 | 简单的 Rate Limiter 模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
热点识别/防护 | 支持 | 不支持 | 不支持 |
多语言支持 | Java/Go/C++ | Java | Java |
Service Mesh 支持 | 支持 Envoy/Istio | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、实时监控、机器发现等 | 简单的监控查看 | 不提供控制台,可对接其它监控系统 |
1. 修改:cloudalibaba-sentinel-service18001
2.添加依赖
com.alibaba.csp sentinel-datasource-nacos
3.配置yml文件
server:port: 18001spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719datasource:ds1:nacos:server-addr: localhost:8848dataId: cloudalibaba-sentinel-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flowmanagement:endpoints:web:exposure:include: '*'
4. 添加Nacos业务规则配置
json内容
[{"resource": "/testA","limitApp": "default","grade": 1,"count": 1,"strategy": 0,"controlBehavior": 0,"clusterMode": false }
]
5. 启动18001并访问接口
访问18001的任意接口,如:http://localhost:18001/testA
刷新sentinel发现业务规则有了
6. 快速访问测试接口
http://localhost:18001/testA
流控规则生效,响应默认的兜底数据
7.停止18001再看sentinel
因为应用停止,所以监测不到数据
8. 重新启动18001并访问接口
访问18001的接口:http://localhost:18001/testA
再看sentinel,配置出现了,持久化验证通过
9. 了解
若需要配置其它规则,参照对应类的属性进行json的配置
流控规则:com.alibaba.csp.sentinel.slots.block.flow.FlowRule
熔断/降级规则:com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule
热点规则:com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule
系统规则:com.alibaba.csp.sentinel.slots.system.SystemRule
授权规则:com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule之前的配置可以让我们启动项目就加载nacos配置好的规则,但是在Sentinel 控制台上修改后,nacos中的配置文件不会修改。
如果要做到同步到nacos中,需要对源码进行扩展。