提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
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/** #断言,
上一篇:[附源码]java毕业设计ssm实验教学资源管理系统
下一篇:WinHex(二)