SpringCloud
父工程构建
父工程创建完成执行mvn : install
将父工程发布到仓库方便子工程继承。
提供者支付模块8001
1.建module 2.改POM 3.写YML 4.主启动 5.业务类
构建JSON封装体CommonResult用于给前端返回
热部署Devtools
消费者订单模块80
使用了RestTemplate; RestTemplate提供了多种便捷访问远程Http服务的方法, 是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集。
在配置类中需要@Bean RestTemplate
工程重构
订单模块和支付模块中的实体类是重复的代码,新建一个工程用于重构实体类;
maven clean、install cloud-api-commons工程,以供给cloud-consumer-order80与cloud-provider-payment8001两工程调用。
在别的模块中需要实体类可以将其引入POM
Eureka
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
Eureka采用了CS(Client/Server)的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
Eureka Server提供服务注册服务:各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问:它是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
EurekaServer服务端7001
server:
port: 7001
eureka:
instance:
hostname: locathost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
支付微服务8001进驻EurekaServer
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
@SpringBootApplication
@EnableEurekaClient//<-----添加该注解
public class PaymentMain001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain001.class, args);
}
}
订单微服务80入驻进EurekaServer—同上
Eureka集群原理说明(互相注册,相互守望)
实质:存key服务命取value闭用地址
1 先启动eureka注主册中心
2 启动服务提供者payment支付服务
3 支付服务启动后会把自身信息(比服务地址L以别名方式注朋进eureka
4 消费者order服务在需要调用接口时,使用服务别名去注册中心获取实际的RPC远程调用地址
5 消去者导调用地址后,底屋实际是利用HttpClient技术实现远程调用
6 消费者实癸导服务地址后会缓存在本地jvm内存中,默认每间隔30秒更新—次服务调用地址
支付模块集群配置中的负载均衡
使用@LoadBalanced注解赋予RestTemplate负载均衡的能力(Ribbon的负载均衡功能)
actuator微服务信息完善
主机名称:服务名称修改(也就是将IP地址,换成可读性高的名字)
eureka主页将显示payment8001,payment8002代替原来显示的IP地址。
访问信息有IP信息提示,(就是将鼠标指针移至payment8001,payment8002名下,会有IP地址提示)
服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient//添加该注解
public class PaymentMain001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain001.class, args);
}
}
Eureka自我保护理论知识
某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。
为了EurekaClient可以正常运行,防止与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
支付服务注册8004进zookeeper
zookeeper是一个分布式协调工具,可以实现注册中心功能
#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 127.0.0.1:2181 # 192.168.111.144:2181 #
ZooKeeper的服务节点是临时节点,没有Eureka那含情脉脉。
Consul简介
-
服务发现 - 提供HTTP和DNS两种发现方式
-
健康监测 - 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
-
KV存储 - Key、Value的存储方式
-
多数据中心 - Consul支持多数据中心
-
可视化Web界面
服务提供者8006注册进Consul
###consul服务端口号
server:
port: 8006
spring:
application:
name: consul-provider-payment
####consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
三个注册中心异同点
组件名 | 语言CAP | 服务健康检查 | 对外暴露接口 | Spring Cloud集成 |
---|---|---|---|---|
Eureka | Java | AP | 可配支持 | HTTP |
Consul | Go | CP | 支持 | HTTP/DNS |
Zookeeper | Java | CP | 支持客户端 | 已集成 |
CAP:
- C:Consistency (强一致性)
- A:Availability (可用性)
- P:Partition tolerance (分区容错性)
Ribbon入门介绍
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法和服务调用
Ribbon默认自带的负载规则
- RoundRobinRule 轮询
- RandomRule 随机
- RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重
- WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
OpenFeign
Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign集成了Ribbon
OpenFeign服务调用
接口+注解:微服务调用接口 + @FeignClient
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService
{
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
Feign自带负载均衡配置项
OpenFeign默认等待1秒钟,超过后报错
Hystrix是什么
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix的服务降级熔断限流概念
**服务降级:**服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
**哪些情况会触发降级:**程序运行导常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级
服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。服务的降级 -> 进而熔断 -> 恢复调用链路
**服务限流:**秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
降级容错解决的维度要求
- 超时导致服务器变慢(转圈) - 超时不再等待
- 出错(宕机或程序运行出错) - 出错要有兜底
Hystrix之服务降级支付侧fallback
降级配置 - @HystrixCommand
8001fallback–设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处埋,作服务降级fallback。
业务类启用 - @HystrixCommand——旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@Service
public class PaymentService{
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler"/*指定善后方法名*/,commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
})
public String paymentInfo_TimeOut(Integer id)
{
//int age = 10/0;
try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
return "线程池: "+Thread.currentThread().getName()+" id: "+id+"\t"+"O(∩_∩)O哈哈~"+" 耗时(秒): ";
}
//用来善后的方法
public String paymentInfo_TimeOutHandler(Integer id)
{
return "线程池: "+Thread.currentThread().getName()+" 8001系统繁忙或者运行报错,请稍后再试,id: "+id+"\t"+"o(╥﹏╥)o";
}
}
**主启动类激活–**添加新注解@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//添加到此处
public class PaymentHystrixMain8001{
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args);
}
}
Hystrix之服务降级订单侧fallback
@SpringBootApplication
@EnableFeignClients
@EnableHystrix//添加到此处
public class OrderHystrixMain80{
public static void main(String[] args){
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
@RestController
@Slf4j
public class OrderHystirxController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
//int age = 10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
//善后方法
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
}
Hystrix之全局服务降级DefaultProperties
除了个别重要核心业务有专属,其它普通的可以通过@DefaultProperties(defaultFallback = “”)统一跳转到统一处理结果页面
通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystirxController {
......
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
// @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
// @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
// })
@HystrixCommand//用全局的fallback方法
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
//int age = 10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
// 下面是全局fallback方法
public String payment_Global_FallbackMethod()
{
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
Hystrix之通配服务降级FeignFallback
@Component
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id){
return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id){
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
}
}
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" ,//
fallback = PaymentFallbackService.class)//指定PaymentFallbackService类
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);
}
Hystrix之服务熔断理论
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。
熔断类型:
- 熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。
- 熔断关闭:熔断关闭不会对服务进行熔断。
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
GateWay
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。
SpringCloud Gateway具有如下特性:
- 基于Spring Framework 5,Project Reactor和Spring Boot 2.0进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定Predicate (断言)和Filter(过滤器);
- 集成Hystrix的断路器功能;
- 集成Spring Cloud 服务发现功能;
- 易于编写的Predicate (断言)和Filter (过滤器);
- 请求限流功能;
- 支持路径重写。
我们目前不想暴露8001端口,希望在8001外面套一层9527
server:
port: 9527
spring:
application:
name: cloud-gateway
#############################新增网关配置###########################
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
#uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
#uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
####################################################################
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
过滤器:
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter,Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
log.info("***********come in MyLogGateWayFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null)
{
log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder()
{
return 0;
}
}
Config分布式配置中心介绍
Bus消息总线是什么
Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能。Spring Clud Bus目前支持RabbitMQ和Kafka。
Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。
利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
Stream是什么及Binder介绍
官方定义Spring Cloud Stream是一个构建消息驱动微服务的框架。
应用程序通过inputs或者 outputs 来与Spring Cloud Stream中binder对象交互。
通过我们配置来binding(绑定),而Spring Cloud Stream 的binder对象负责与消息中间件交互。所以,我们只需要搞清楚如何与Spring Cloud Stream交互就可以方便使用消息驱动的方式。
通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。
Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
目前仅支持RabbitMQ、 Kafka。
Sleuth是什么
-
Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案
-
在分布式系统中提供追踪解决方案并且兼容支持了zipkin
spring:
application:
name: cloud-payment-service
zipkin: #<-------------------------------------关键
base-url: http://localhost:9411
sleuth: #<-------------------------------------关键
sampler:
#采样率值介于 0 到 1 之间,1 则表示全部采集
probability: 1
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456