spring cloud体系总结
基本介绍:
微服务架构:
一种架构模式,提倡将单一应用程序划分成一组小的服务,服务之间相互协调、相互配合。
每个服务允许在独立的进程,服务与服务之间采用轻量级的通信机制相互协作(比如HTTP)。每个服务围绕具体业务构建,并且能够独立部署到生产环境。
组成:
服务注册与发现eureka\alibaba
服务调用ribbon
服务熔断hystrix
负载均衡
服务降级
服务消息队列spring cloud bus
配置中心管理Spring cloud config
服务网关zuul
服务监控
全链路追踪
自动化构建部署
服务定时任务调度
版本选择:
springboot和springCloud之间有版本限制,详见官网。
推荐cloud Hoxton.SR1,boot2.2.2.RELEASE,cloud alibaba 2.1.0.RELEASE
组件升级与停用:
服务注册中心:
Eureka -> Zookeeper、consul、Nacos(推荐)
服务调用:
Ribbon、LoadBalancer
Feign -> OpenFeign
服务降级:
Hystrix -> resilience4j、Alibaba Sentinel(推荐)
服务网关:
zuul -> Zuul2、spring gateway
服务配置:
Config -> apollo、Nacos(推荐)
服务总线:
Bus -> Nacos(推荐)
新建流程:
1.定义父工程,修改父POM文件,定义dependencyManagement这种。
2.建module,改POM,写yml,主启动类,业务类、测试。
业务类包含写sql、entity、dao(写接口、mapper)、service、controller
热部署DevTools:
1.添加devTools maven依赖
2.添加plugin spring-boot-maven-plugin。
3.启用idea的自动build配置(Compiler)
4.更新Registry配置 ctrl + shift + alt + /
服务之间调用:
springboot可以用restTemplate调用
多个服务同时启动:
idea的run dashboard
配置在idea的workspace.xml
共同模块:
抽离为单独的module,然后其他module在pom里面引入即可。
服务注册与发现:
服务治理:
每个服务之间的依赖比较复杂,管理麻烦,所以需要服务治理来管理服务之间的依赖关系。
可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
概述:
有一个注册中心,当服务启动的时候,将当前服务的信息比如ip等以别名的方式注册到注册中心上。
而另一个服务,以别名的方式去注册中心获取实际的通信地址,再调用RPC。
Eureka:
概述:
netfix出品
架构:
image-20220216214219093Eureka Server提供服务注册服务,各个服务向它注册,注册表会存储所有的可用服务节点的信息,在界面直观看到。
Eureka Client通过注册中心进行访问,Java客户端,用于定时发送心跳到server。同时具有轮询负载算法的负载均衡功能用于查询(查询有缓存)。
启动server:
1.引入maven依赖。
spring-cloud-starter-netflix-eureka-server
2.配置application.yml
3.启用:
@EnableEurekaServer
启动client:
1.引入maven依赖。
spring-cloud-starter-netflix-eureka-client
2.配置application.yml
3.启用:
@EnableEurekaClient
集群原理:
相互注册
集群搭建:
1.建两个module。
2.改pom。
3.修改映射配置(方便通过ip映射为多个域名)
4.写yml(相互注册url,页面可以观察到DS replicas)
5.启动server
6.修改微服务的eureka地址为多个
7.RestTemplate添加注解@LoadBalanced,识别请求的微服务域名并实现负载均衡请求。
Ribbon和Eureka整合后Consumer可以用该方法,不再需要关心ip port和负载。
actuator提示信息完善:
主机名称:
instance:
instance-id: payment02
访问信息有IP信息提示:
instance:
prefer-ip-address: true
服务发现:
概述:
通过discoveryClient可以拿到已注册的所有服务
示例:
@Resource
private DiscoveryClient discoveryClient;
discoveryClient.getServices() // 拿到所有的服务
discoveryClient.getInstances(String serviceId) //拿到该服务所有的实例,返回List<ServiceInstance>
instance.getServiceId()
instance.getHost()
instance.getPort()
instance.getUri()
保护模式:
概述:
主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护,一旦进入保护模式,Eureka不再删除服务注册表中的数据。
属于CAP理论的AP分支(牺牲了强一致性)。
防止Eureka client可以正常运行,只是与EurekaServer网络不通的情况下,将client服务删除。
原理:
server在一定时间内没有收到某个微服务的心跳,那么将会注销该服务(默认90秒)。
当EurekaServer节点在短时间内丢失过多客户端时(比如网络分区故障),那么就会进入保护模式。
配置:
eureka:
server:
enable-self-preservation: false //关闭自我保护机制,不可用服务会立刻剔除
eviction-interval-timer-in-ms: 2000 //剔除间隔
instance:
lease-renewal-interval-in-seconds: 1 // client发送心跳间隔
lease-expiration-duration-in-seconds: 2 // server收到最后一次心跳后等待时间上限,超时将剔除。
zookeeper:
概述:
分布式协调工具,可以实现注册中心功能。
与eureka的区别:
原理:
eureka集群是peer-to-peer集群,机器地位对等。
zk是leader+follower两种角色,leader负责读
一致性保证:
eureka是ap模型
服务发现的时效性:
zk时效性更好,秒级
eureka默认配置了缓存导致分钟级别
容量:
zk大规模时,服务上下线需要瞬间推荐数据到所有的其他服务实例,瞬间大量占用zk所在节点的网络带宽
优化:
只将新服务推送给指定的服务,减小推送量
eureka需要心跳检测所有的服务,每台机器压力比较大。
优化:
用redis cluster的分片思想,分开检测
使用流程:
1.引入依赖。
sprint-cloud-starter-zookeeper-discovery
2.配置:
cloud:
zookeeper:
connect-string: ip:port
3.启动注册:
@EnableDiscoveryClient,比EnableEurekaClient更广泛。
4.通过restTemplate + @LoadBalancer即可请求。
实现原理:
临时节点,服务心跳超时后立刻删除。
consul:
概述:
一套开源的分布式服务发现和配置管理系统,go语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用构建服务网格。
总之提供了一种完整的服务网格解决方案。
CP方案。
功能:
服务发现:提供http和dns两种发现方式。
健康监测:支持http、tcp、docker、shell等
KV存储:
多数据中心:
可视化web界面
下载安装:
consul agent -dev
访问http://localhost:8500
注册流程:
1.引入依赖。
sprint-cloud-starter-consul-discovery
2.配置:
cloud:
consul:
host:
port:
discovery:
service-name
3.启动注册:
@EnableDiscoveryClient
4.通过restTemplate + @LoadBalancer即可请求其他服务。
服务调用:
负载均衡:
将用户的请求分配到多个服务上,从而达到HA。
常见nginx,lvx
Ribbon:
概述:
spring cloud ribbon基于netfix ribbon实现的一套客户端负载均衡工具。
主要功能是提供客户端的负载均衡算法和服务调用,提供一系列完善的配置项如连接超时、重试等。
在配置文件中列出所有的loadbanlancer机器,ribbon会基于规则去连接服务。
不像nginx是服务器负载均衡,ribbon是本地负载均衡,获取注册中心的服务列表后,缓存到本地JVM,再进行负载均衡。
工作流程:
1.选择eurekaserver,优先选择同一个区域内负载较少的server。
2.再根据用户指定的策略,在从server获取的服务列表选择一个地址。
使用:
1.引入依赖。
spring-cloud-starter-netflix-eureka-client内置了ribbon
2.restTemplate使用
RestTemplate:
常用方法:
getForOjbect(url, class)
getForEntity(url, class) 获取到的entity可以拿到响应头、状态码、响应体等。
postForObject(url, model, class)
postForEntity(url, model, class)
负载算法:
概述:
ribbon的interface IRule接口下一共有
RoundRobinRule、
RandomRule、
RetryRule(先按照RoundRobin,失败再重试)、
BestAvailableRule先过滤掉多次访问故障而处于跳闸的服务,然后选择一个负载小的服务、
AvailableFilteringRule先过滤掉故障实例,再选择并发小的实例。
实现类。
RoundRobinRule下还有子类WeightedResponseTimeRule,对RoundRobin的拓展,响应速度越快的实例权重越大。
指定负载均衡算法:
概述:
不能放在ComponentScan扫描的包下面,否则所有服务都会共享。
流程:
1.新建包。
2.新建配置类:
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule();
}
}
3.配置:
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
表示访问这个服务使用自定义的rule
轮询算法原理:
概述:
rest接口第几次请求数%服务器集群总数量 = 实际调用服务器位置下标,重启后rest从1开始。
源码:
使用自旋锁获取下一个index
do {
current = this.nextServerCyclicCounter.get();
next = (current + 1) % modulo;
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));
自定义算法;
流程:
1.去掉Localbalanced
2.编写LoadBalancer接口,定义getInstance方法(拓展:还可以定义get url的方法,减少controller的代码)
3.实现类MyLB:
a.利用discoveryClient获取serverId的所有Instance
b.利用自旋锁实现轮询算法(或者其他算法),获取到index
c.返回具体的instance
4.controller利用该自定义lb实例的方法拿到Instance,getUri获取到url后,调用即可。
Feign:
概述:
一个声明式WebService客户端,使用方法是定义一个服务接口然后添加注解。
支持可插拔式的编码器和解码器。
可以与Eureka和Ribbon组合使用以支持负载均衡。
作用:
ribbon + restTemplate可以实现http请求。
在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。
(比如不同的controller要写restTemplate,要getForObject拿到响应结果。而这些都可以封装成客户端,这样不同的provider对应不同的客户端,无需关注restTemplate)
所以,feign在此基础上做了进一步封装,定义和实现依赖服务接口的定义。
在Feign的实现下,只需创建一个接口并使用注解的方式来配置即可,简化了使用ribbon的手动封装服务调用客户端的工作量。
与Ribbon的关系:
利用Ribbon维护了服务列表信息,并且通过它的负载算法实现了客户端的负载均衡。
使用方式:
使用feign的注解定义接口,调用接口就可以调用服务注册中心的服务。
OpenFeign:
概述:
在Feign的基础上支持了SpringMvc的注解,如@RequestMapping等等,OpenFeign的@FeignClient可以解析RequestMapping注解下的接口,并通过动态代理的方式产生实现类,
实现类中做负载均衡并调用其他服务。
更符合controller调用service的习惯。
使用流程:
1.新建项目。
2.写POM,引入spring-cloud-starter-openfeign
3.配置yml,主要配置eureka
4.主启动类,@EnableFeignClients
5.写service接口,添加注解@Component和@FeignClient(value = "CLOUD-PAYMENT-SERVICE"),注意顺序。
添加方法,传参和返回参数都和目标服务的controller对应的方法一致。
6.正常写controller,注入service并通过它拿到目标数据即可。
超时控制:
概述:
默认1s超时,由ribbon控制
yml配置:
ribbon:
ReadTimeout: 5000 //建立连接后从服务器读取到资源的所用时间
ConnectTimeout: 5000 // 建立连接所用的时间
日志设置:
级别:
NONE:默认,不显示任何日志。
BASIC:仅记录请求方法、URL、响应状态码以及执行时间。
HEADERS:除了BASIC定义的信息之外,还有请求和响应的头信息。
FULL:除了HEADERS定义的信息之外,还有请求和响应的正文以及元数据。
如何配置:
1.config类:
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
2.配置文件:
logging:
level:
com.czl.cloud.service.PaymentFeignService: debug //以什么级别日志监控哪个接口。
服务降级:
概述:
分布式应用,不可避免某些服务出现级联故障,拖延下游,造成雪崩效应。
Hystrix:
概述:
用于处理分布式系统的延迟和容错的开源库。
当某个服务发生故障之后,通过断路器的故障监控,向调用方返回一个符合预期、可处理的备选响应,而不是长时间等待或者抛出异常,保证线程空闲,从而避免雪崩。
作用:
服务降级
服务熔断
服务限流
近实时监控。
服务降级:
概述:
程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级
使用流程:
1.引入依赖spring-cloud-starter-netflix-hystrix
2.yml配置eureka即可
3.使用注解HystrixCommand
@HystrixCommand(fallbackMethod="paymentInfoTimeOutHandler", // paymentInfoTimeOutHandler方法的参数为注解的方法的参数。
commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000")})
controller或者是service等需要降级的方法
4.主启动类注解@EnableCircuitBreaker。
5.请求测试,当运行异常、超时、服务熔断触发服务降级、线程池/信号量打满后,执行fallback方法。
常用配置:
execution.timeout.enabled 超时启用,默认true
execution.isolation.strategy 可选thread(线程池线程执行,并发数量由线程池决定,HystrixCommand默认)、
semaphore(调用的线程来执行,并发数量由信号量限制,HystrixObservableCommand默认)
execution.isolation.semaphore.maxConcurrentRequests 信号量上限,默认10
execution.isolation.thread.timeoutInMilliseconds 默认1000
搭配Feign流程:
1.引入依赖spring-cloud-starter-netflix-hystrix
2.yml配置feign支持hystrix。
feign:
hystrix:
enabled: true // feign开启熔断
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 //feign开启熔断后,HystrixCommand中的超时时间不起作用,必须配置全局default超时时间(配置其他commandKey也没用)
3.主启动类注解@EnableHystrix
4.写service
5.controller使用注解HystrixCommand
优化:
与业务service逻辑分开:
写一个fallBack类,继承service接口实现所有方法,然后service注解@FeignClient(value="",fallBack=fallBack类),这样就能分开。
yml配置openFeign支持hystrix。
fallBack类要Component注解。
controller不能再添加HystrixCommand注解。
共同的异常处理逻辑可以抽离为单独的一个:
比如放在类上@DefaultProperties(defaultFallback = "defaultHandler")
bug:
配置后,yml配置的default超时时间也不生效了。方法单独的fallbackMethod连default超时时间也不生效了。
服务熔断:
概述:
Hystrix监控微服务间的调用状况,当失败的次数到达一定后,启动熔断机制。
熔断机制:
当出现不可用或者响应时间太长后,进行服务的降级,降级达到阈值后,进而熔断该节点服务微服务的调用,这时再有请求的时候,不会调用主逻辑(区别),直接调用降级fallback,快速返回错误的响应信息。
当断路器打开后,hystrix打开一个休眠时间窗,降级逻辑成为主逻辑,到期后会断路器进入半开状态,释放一次请求到原来的主逻辑上,
如果请求正常返回,那么断路器将会闭合。
如果这次请求还有问题,断路器继续进入打开状态,休眠时间窗重新计时。
重要参数:
1.快照时间窗circuitBreaker.sleepWindowInMilliseconds:
统计请求错误的时间范围
2.请求总数阈值circuitBreaker.requestVolumeThreshold:
在快照时间窗内,必须满足请求阈值才可能熔断。默认20。
3.错误百分比阈值circuitBreaker.errorThresholdPercentage:
当请求总数在快照时间内超过了阈值,且超过了该百分比的情况下,断路器打开。
示例:
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback",
commandProperties = {
@HystrixProperty(name="circuitBreaker.enabled", value = "true"),
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value = "60"),
})
服务限流:
概述:
execution.isolation.strategy决定服务的上限是线程数量还是信号量
hystrix单独维护了线程池来执行方法。
tomcat配置(适合服务降级,限流不生效):
server:
tomcat:
max-threads: 10 // 默认200
hystrix配置:
coreSize 默认10
maximumSize 默认10
maxQueueSize 默认-1,使用SynchronousQueue,否则LinkedBlockingQueue。不支持动态修改。
queueSizeRejectionThreshold默认5,队列元素数量到达该数量后,拒绝请求。即使还没到达maxQueueSize。支持动态修改。必须要设置maxQueueSize。
keepAliveTimeMinutes默认1分钟
allowMaximumSizeToDivergeFromCoreSize默认false,开启后coreSize~maxSize的线程才会创建。
yml示例(不支持注解方法,所有注解共享):
hystrix:
threadpool:
default:
coreSize: 2
maxQueueSize: 10
queueSizeRejectionThreshold: 5
工作流程图:
image-20220221222613508监控与可视化:
概述:
hystrix会持续记录所有通过hystrix发起的请求的执行信息。
只能看到最近一段时间的请求情况。(区别于grafana+prometheus这种方案)
使用:
1.新建项目。
2.引入依赖。spring-cloud-starter-netflix-hystrix-dashboard和spring-boot-starter-actuator
3.配置yml(配置port即可)
4.启动类注解@EnableHystrixDashboard,启动,访问http://localhost:9001/hystrix
5.其他微服务提供类配置:
a.注意必须引入依赖spring-boot-starter-actuator
b.application类配置本地监控的serverlet url地址
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
c.在dashboard网页上配置该url地址http://localhost:7002/hystrix.stream
概念:
实心圆:
颜色代表了健康程度,绿色、黄色、橙色、红色
大小代表了实例的请求量
服务网关:
功能:
动态路由,自动感知服务增减机器
灰度发布
鉴权认证
性能监控
系统日志
数据缓存
限流熔断
选型:
zuul并发能力不强,dubbo用kong、openresty等技术
大厂一般用Java、servlet、netty自研网关系统。
zuul:
概述:
netfix,负责处理来自device和web的请求,分发到后端服务上。
基于servlet2.5阻塞IO的API gateway,请求线程会被阻塞直到完成。
路由配置:
路由访问映射:
查看路由信息:
过滤器:
Gateway:
概述:
在spring生态系统上构建的API网关服务,基于Sprignboot2.x,spring webflux,Project Reactor(底层是高性能框架netty)等技术。
旨在提供一种简单有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如熔断、限流、重试等。
支持非阻塞IO、websocket(servlet3.x以上版本才支持非阻塞,而webflux不需要依赖servlet,而是netty来实现异步非阻塞)
功能:
反向代理(动态路由,匹配任何请求属性,可以指定断言predicate和filter过滤器)、
鉴权、
服务发现(集成spring cloud服务发现功能)、
流量控制、
熔断(集成hystrix的断路器功能)、
日志监控等。
架构:
image-20220221224112912外部请求 -> nginx负载均衡(Keepalived+nginx主备实现负载高可用(lvs性能更好)) -> 多个网关实例 -> 各种微服务
概念:
路由:由ID、目标URL组成
predicate断言:一般根据请求头或者参数,断言成功则路由。
过滤器:可以对请求在路由前后修改。
工作流程:
1.接收到请求。
2.在GateWay handler Mapping找到对应的路由,发送到handler。
3.执行过滤器逻辑。比如pre可以做参数校验、权限校验、流量监控、日志输出、协议转换等,post可以修改响应体/头,日志输出,流量监控等
4.发送到下游服务。
示例:
1.pom引入spring-cloud-starter-gateway依赖。
2.yml配置eureka,注册自己到配置中心。
3.启动类开启@EnableEurekaClient
4.配置路由(yml文件)
cloud:
routes:
- id: xxx
uri: http://localhost:8001 // 匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** // 断言,路径匹配的进行路由,通过http://gateway_ip:port/xxxx进行访问
5.访问
配置路由方式:
1.yml文件配置config。
2.config类配置
routes.route("name", r->r.path("/xxx").uri("http://xxx/xxx")).build()
动态路由:
概述:
可以实现类似ribbon的服务动态发现和负载均衡。
开启的情况下,gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发。
配置:
cloud:
gateway:
discovery:
enabled: true
routes:
- id: xxx
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/**
断言Predicates:
概述:
一组匹配规则,匹配成功才进行路由,可以多个。
底层使用webflux进行匹配。
分类:
After规定时间后的请求,时间格式是ZoneDateTime.now(),不匹配返回404。可以用于指定时间上线接口。
Before
Between
Cookie判断指定key对应的value是否符合正则匹配。
Header判断指定header对应的value
Host
Method
Path
Query请求参数
ReadBodyPredicateFactory
RemoteAddr
Weight
CloudFoundryRouteService
过滤器:
概述:
可用于修改请求和响应
分类:
GatewayFilter
GlobalFilter
常用过滤器:
AddRequestParameter=key,value
自定义过滤器:
实现GlobalFilter,Ordered
可以用于鉴权、日志输出
示例:
@Component
@Slf4j
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("==========come in my filter" + new Date());
HttpCookie username = exchange.getRequest().getCookies().getFirst("username");
if (!username.getValue().equals("czl")) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
灰度发布:
开关放在数据库,发布服务当前有version字段区分,根据url来判断走哪个分支,或者随机走
少量服务上线后,观察是否运行正常。
配置中心:
概述:
统一管理配置。
动态变更配置。
SpringCloud Config:
概述:
分为服务端和客户端
服务端:一个独立的微服务应用,为客户端提供获取配置信息。
客户端:从配置中心获取和加载配置信息,采用git管理版本。
功能:
统一管理配置。
不同环境不同配置,支持dev/test/prod/beta/release
动态更新。
配置信息可以通过api接口访问。
服务端搭建流程:
1.新建github项目
2.本地clone下来
3.新建项目,引入依赖spring-cloud-config-server
4.配置:
cloud:
server:
git:
uri:
search-paths:
- springcloud-config
label: master
5.主启动类注解@EnableConfigServer
6.访问http://localhost:3344/master/config-dev.yml
配置访问格式:
/{label}/{application}-{profile}.yml
/{application}-{profile}.yml 默认master
/{application}/{profile}/{label}
客户端使用:
1.新建项目,引入依赖spring-cloud-config
2.配置bootstrap.yml文件
介绍:
bootstrap.yml文件是系统级的,优先级更高。
spring cloud会创建一个bootstrap context,作为soring应用的application context的父上下文,初始化的时候,负责从外部源加载配置。
配置:
spring:
cloud:
config:
label: master
name: config //读取配置文件名称
profile: dev //读取文件后缀,三个拼接为具体的配置
uri: http://localhost:3344 # 配置中心地址
3.主启动类正常配置
4.使用,通过@Value("${xxx.xxx}")即可访问配置文件
动态配置刷新:
手动版:
1.pom引入actuator监控
2.修改yml文件,暴露监控端口
management:
endpoints:
web:
exposure:
include: "*"
3.controller添加@RefreshScope
4.手动发送post请求刷新curl -X POST http://localhost:3355/actuator/refresh,避免重启。
自动触发更新:
通过消息队列广播
消息总线:
概述:
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个公用的消息主题,并让所有的微服务连接上来。
主题产生的消息会被所有的实例监听和消费,所以称为消息总线。
SpringCloud Bus:
概述:
用来将分布式系统节点与轻量级消息系统链接起来的框架。能管理和传播分布式系统间的消息,可用于广播状态变更,事件推送等,也可当作微服务间的通信通道。
搭配SpringCloud Config,实现配置的自动更新。
支持消息队列kafka和rabbitmq。
基本原理:
configClient实例监听MQ中同一个topic(默认是SpringCloud Bus),当一个服务刷新数据的时候,它会把这个信息放入topic中,这样其他监听的服务就能得到通知,然后更新。
触发更新:
方式1:
利用消息总线触发一个客户端的端点,从而刷新所有客户端的配置。
缺点:
破坏了微服务的单一职责性,微服务本身不应该负责刷新。
破坏了微服务各节点的对等性。
微服务迁移的时候,网络地址可能会变更,如果要想自动刷新,需要更多的修改。
方式2:
利用消息总线触发一个服务端ConfigServer的端点,从而刷新刷新所有客户端的配置
流程:
1.config server引入依赖spring-cloud-starter-bus-amqp
2.config server新增rabbitmq配置,以及暴露bus刷新配置的端点
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
3.客户端添加同样的依赖,rabbitmq配置和暴露端点
4.更新配置后,post请求configserver的uri,curl -X POST http://localhost:3344/actuator/bus-refresh,从而触发其他微服务更新。
定点通知:
请求地址http://localhost:config的port/path/{destination},其中destination为application name + port
SpringCloud Stream:
概述:
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
binder负责与具体的消息中间件交互。
应用程序通过inputs或者outputs与stream的binder对象交互。
支持发布订阅、消费组、消息分区三个核心概念。
概念:
Binder:很方便的连接中间件,屏蔽差异。
Channel:通道,队列queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过channel对队列进行配置。
注解:
@EnableBinding将配置的channel和exchange(topic)绑定在一起。
@StreamListening监听队列,用于消费者的队列的消息接收。
发送流程:
1.引入依赖spring-cloud-starter-stream-rabbit
2.配置binder
spring:
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 自定义名称
type: rabbit # 消息组件类型
envionment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host:
port:
username:
password:
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: xxx # 使用的exchange名字
content-type: application/json
binder: defaultRabbit # 要绑定的消息服务的名称
3.主启动类正常
4.编写发送类:
@EnableBinding(Source.class)
public class MessageProviderImpl implements IMessageProvider {
@Resource
public MessageChannel output;
@Override
public void send() {
output.send(MessageBuilder.withPayload(UUID.randomUUID().toString()).build());
}
}
接收流程:
1.引入依赖spring-cloud-starter-stream-rabbit
2.配置binder,将output变为input即可。
3.编写接收类
@Component
@EnableBinding(Sink.class)
public class xxx{
@StreamListening(Sink.INPUT) // 后台运行。
public void input(Message<String> message) {
println(message.getPayload())
}
}
消费组:
概述:
同一组的消费者通过轮询的方式消费信息。
配置:
bindings: # 服务的整合处理
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: groupA
持久化:
概述:
分组默认会持久化。
请求链路跟踪:
概述:
请求链路的跟踪
SpringCloud Sleuth:
概述:
提供了一套完整的服务跟踪的解决方案
在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin。
概念:
一条链路通过Trace Id唯一标识,Span标识发起的请求信息,各span通过parent id关联。
流程:
1.启动zipkin server,打开监控页面。
wget https://search.maven.org/remote_content?g=io.zipkin&a=zipkin-server&v=LATEST&c=exec
java -jar xxx.jar
访问http://localhost:9411/zipkin/
2.引入pom依赖spring-cloud-starter-zipkin,内置了sleuth
3.配置yml:
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1 //采样率
4.其他不变,正常请求即可。
页面上能看到application name的Trace。
SpringCloud alibaba:
概述:
服务限流降级sentinel
服务注册与发现nacos
分布式配置管理nacos
消息驱动能力RocketMQ
分布式事务seata
阿里云对象存储OSS
分布式任务调度schedulexrX
阿里云短信服务
Nacos:
概述:
name configuration service
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
注册中心 + 配置中心的组合。eureka + config + bus
下载安装:
github下载
点击bin目录下的cmd运行,或者sh startup.sh -m standalone
访问http://localhost:8848/nacos ,账号密码nacos
注册中心:
概述:
支持AP(保留不健康实例)和CP(移除不健康实例)模型。
AP适合场景:
不需要存储服务级别的信息,减弱一致性。比如springcloud和dubbo
CP适合场景:
如果需要存储服务级别信息。比如k8s和dns。此时以raft协议运行,注册实例之前必须注册服务。
注册流程:
1.父pom引入spring cloud alibaba,模块pom引入依赖spring-cloud-starter-alibaba-nacos-discovery。
2.配置yml:
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
management: # 监控
endpoints:
web:
exposure:
include: '*'
3.主启动类@EnableDiscoveryClient
发现和负载均衡流程:
1.引入依赖spring-cloud-starter-alibaba-nacos-discovery(集成了Ribbon,具有负载均衡的效果,可以搭配restTemplate)。
2.主启动类@EnableDiscoveryClient
3.调用restTemplate,同时@LoadBalanced
其他写法:引入openFeign,里面也包含了Ribbon,这样就不用写restTemplate
配置中心:
client流程:
1.pom引入spring-cloud-starter-alibaba-nacos-config
2.配置bootstrap.yml
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
3.配置application.yml:
spring:
profiles:
active: dev
4.主启动类@EnableDiscoveryClient
5.controller注解@RefreshScope
6.nacos页面增加配置
其中dataid的命名格式为${prefix}-${spring.profile.active}.${file-extension},prefix为application name
通过profiles.active读取不同的配置。
7.测试读取即可。
动态更新:
自动支持
命名空间:
默认public。
主要用来隔离。比如不同的开发环境。
配置:
config:
server-addr: localhost:8848
file-extension: yaml
namespace: 填写具体的uuid
分组:
默认DEFAULT_GROUP
用来划分微服务。
配置:
config:
server-addr: localhost:8848
file-extension: yaml
group: xxx
数据持久化:
概述:
配置和CP场景下注册的信息需要持久化。
默认采用了嵌入式数据库derby作为持久化。
多节点情况下需要用mysql。
配置mysql:
创建数据库nacos_config,执行conf/nacos-mysql.sql脚本,创建相关的库表
修改conf/application.properties配置文件,填写mysql相关信息。
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123
Cluster集群:
概述:
一个微服务可以包含多个cluster。
尽量让同一个机房的微服务相互调用。
架构:
image-20220222020938037nginx + 多个nacos + mysql
搭建流程:
1.搭建mysql
2.配置application.properties
3.修改配置cluster.conf,配置多个节点的ip:port
4.修改启动脚本sh,使得支持配置port
5.配置nginx
6.微服务yml配置nginx的代理后地址
sentinel:
概述:
以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
特征:
丰富的应用场景:秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。
您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
组成:
核心库(Java Client)不依赖任何框架/库
控制台(Dashboard)基于Spring Boot开发,可以直接运行
安装Dashboard:
下载github jar包运行即可。
访问http://localhost:8080,账号密码sentinel
client使用流程:
1.引入依赖spring-cloud-starter-alibaba-sentinel
2.配置yml
sentinel:
transport:
dashboard: localhost:8080
port: 8719
3.正常写业务代码,然后访问一次,触发sentinel懒加载。
4.页面即可添加流控等配置
流控规则:
阈值类型:QPS、线程数(调用该api的线程)
流控模式:
直接(api达到限流条件时,直接限流)
关联:当关联的资源(path)达到阈值时,限流自己
链路:多个请求调用了同一个微服务
流控效果:
直接失败:返回Blocked by sentinel
预热:一开始阈值为 设置阈值/coldFactor(默认3),经过预热时长后达到设置的阈值。
排队等待:超过阈值的请求排队等待,可以设置超时时间。漏桶算法。
降级规则:
RT:
平均响应时间。当平均响应时间超出设置的RT,且1s内通过的请求>=5,两个条件同时满足后触发降级。
接下来的设置的时间窗口内,自动熔断请求,过后关闭断路器。
RT最大4900,最大的需要通过-Dcsp.sentinel.statistic.max.rt=xxx生效
异常比例:
每秒的QPS>=5且异常比例超过阈值时,触发。
异常数:
异常数(分钟统计)超过阈值时,触发降级。触发降级后,对资源的调用都自动熔断,抛出异常。(没有半开状态去检查是否继续打开断路器)
热点规则:
概述:
统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源进行限流。
使用流程:
1.编写controller
@GetMapping("/testHotKey")
@SentinelResource(value="对应页面上的资源名",blockHandler="自定义方法名") //溢出后执行blockHandler方法
public String testHotKey(@RequestParam(required = false) String p1, @RequestParam(required = false) String p2) {
return "testHotKey";
}
2.编写溢出处理方法
public String 自定义方法名(String p1,String p2,BlockException exception)
3.页面配置资源名、参数下标索引(自定义方法里面的参数下标,从0开始)、参数例外项(指定值配置单独的阈值)
系统规则:
概述:
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,
通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。
入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
模式:
Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
SentinelResource注解:
概述:
替代HystrixCommand
参数:
value:资源名,用于页面填写
blockHandler:限流后执行,填写方法名(默认同一个class下)
blockClass:指定handler所在的class
fallback:异常后执行
ExceptionToIgnore:忽略的异常
使用流程:
1.引入依赖。
2.编写controller,使用注解
@GetMapping("/getResource")
@SentinelResource(value = "getResource", blockHandler = "dealHandler")
public String getResource(){
return "get resource";
}
3.页面根据value资源名或url path来配置流控规则。
优化:
1.与业务逻辑分离,放在单独的class下,注意方法必须static。
@SentinelResource(value = "getResource", blockHandler = "dealHandler", blockHandlerClass = MyHandler.class)
Sentinel + Ribbon:
使用restTemplate的方法添加SentinelResource注解
示例:
@SentinelResource(value = "fallback", fallback = "fallbackHandler") 当发生异常后执行handler。
如果没有配置fallback,且不超出熔断限流条件的情况下,页面会报错(超出了执行blockHandler)。
Sentinel+ OpenFeign:
流程:
1.引入依赖spring-cloud-starter-openfeign。
2.yml配置sentinel支持openfeign
feign:
sentinel:
enabled: true
3.主启动类注解@EnableFeignClients
4.service注解@FeignClient(value="", fallback=继承该service的异常处理子类)(和sentinel没关系)
feign不加fallback,@SentinelResource再加,才是利用sentinel。
5.controller添加SentinelResource注解
规则持久化:
概述:
将规则持久化到nacos。
步骤:
1.引入sentinel-datasource-nacos依赖
2.yml配置nacos的地址
sentinel:
transport:
dashboard: localhost:7080 #配置Sentinel dashboard地址
port: 7719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3.nacos配置中心页面配置sentinel规则
[
{
"resource": "getResource",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
4.sentinel刷新就能看到规则
5.微服务重启后只需请求一次url即可触发
实时监控:
引入依赖,客户端便会主动连接 Sentinel 控制台, 在实时监控面板即可看到
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>
分布式事务:
概述:
跨数据库事务
常见方案:
XA、TCC、可靠消息最终一致性方案、最大努力通知方案、sega
TCC:
多个服务绑定成一个TCC,每个服务的接口都要提供try、confirm、cancel接口,分布式框架负责调用具体哪个接口
开源框架有ByteTcc、Himly、阿里fescar、seata(推荐,自动对接口实现回滚,针对特定类型sql操作(INSERT,UPDATE,DELETE,SELECT_FOR_UPDATE)等进行特殊解析。)
性能瓶颈:
链路中的各个服务都需要跟TC这个角色进行频繁的网络通信
seata-server需要基于mysql或者文件等来存储事务状态。对TC背后的db分库分表
可靠消息最终一致性:
概述:
借助消息队列,消费成功后回调接口,失败重复处理,可以达到最终一致性。
适合异步的场景。
流程:
1.先发送half-message到mq
2.执行操作,成功后commit,失败rollback到mq
3.下游根据mq的消息处理
mq可靠消息服务:
rocketMq,或者自己实现一套有ack的消息队列即可。
Seata:
概述:
一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
提供了 AT、TCC、SAGA 和 XA 事务模式
概念:
全局唯一的事务ID:Transaction ID,XID
TC (Transaction Coordinator):
事务协调者,seata server。维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager):
事务管理器,使用了GlobalTransactional的方法。定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager):
资源管理器,管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
整体流程:
1.TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
2.XID在微服务调用链路的上下文中传播
3.RM向TC注册分支事务,将其纳入XID对应全局事务的管辖。
4.TM向TC发起针对XID的全局提交或回滚协议(事务一阶段)
5.TC调度XID下管辖的全部分支事务完成提交或回滚请求(事务二阶段)。
下载安装:
下载
修改配置conf/file.conf:事务组名称service.vgroup_mapping.my_test_tx_group、日志存储模式改为db、数据库连接信息
建seata库,执行conf/db_store.sql
修改registry.conf配置文件,配置具体的注册中心。
启动nacos后启动seata-server
使用流程:
1.每个数据库创建回滚日志表,conf/db_undo_log.sql
2.引入依赖spring-cloud-starter-alibaba-seata、spring-cloud-starter-alibaba-nacos-discovery、spring-cloud-starter-openfeign等
3.yml配置seata的tx-service-group,配置其他,比如dataSource
cloud:
alibaba:
seata:
tx-service-group: tx_group # 自定义事务组名称需要与seata-server中的对应
4.复制file.conf、registry.conf文件到每个module的resources目录下
5.正常写controller,service部分调用多个微服务。
6.service的方法添加@GlobalTransactional(name="",rollbackFor=Exception.class)
AT模式:
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
1.解析SQL语义,扎到业务sql要更新的业务数据,在业务数据被更新前,将其保存成before image。
2.执行业务sql,更新业务数据。在业务数据更新后,保存成after image。(位于undo_log表)
3.生成行锁(lock_table)。
二阶段:
提交异步化,非常快速地完成。
只需将一阶段保存的快照数据和行锁删掉,完成数据清理。
回滚通过一阶段的回滚日志进行反向补偿。
利用before image还原业务数据,但还原前首先要校验脏写,对比数据库当前业务数据和after image。
完全一致说明没有脏写,可以还原数据。不一致说明有脏写,需要人工处理。
常见问题:
1.数据库没有产生undo_log表记录,发生异常也没有回滚,branch_table没有对应的branch。
解决:
取消数据源的自动创建,使用seata对数据源进行代理
其他事项:
访问量QPS:
统计方式:
1.接口做metrics统计,存到内存当中,开发接口返回结果
2.prometheus api统计
压测工具,测试QPS、延迟时间TP99/TP95/TP50、平均响应延时
访问量扩大10倍:
网关:增加机器,前面通过nginx实现负载均衡
服务实例:增加10倍,对eureka机器的心跳检测增加10倍,需要纵向提高机器的配置
数据库:集群、读写分离、缓存
接口防重幂等性:
1.数据库唯一索引,防止重插入,不适合更新操作
2.基于redis实现接口的防重框架,适合扣减库存、累加积分等更新操作,主要是拦截器基于接口的唯一参数存到redis判断是否存过,需要考虑业务语义
云原生:
服务自愈
弹性伸缩
服务隔离
自动化部署
灰度发布
流量治理