SpringCloudAlibaba
Spring Boot -> Spring Cloud -> Spring Cloud Alibaba
版本说明
服务治理
Nacos
点击到对应版本的spring cloud alibaba的版本 查看对应的nacus中去githup查看对应的版本
-
服务注册
- 配置spring.cloud.nacos.discovery.server-addr:地址
- 在启动类上开启注解 @EnableDiscoveryClient
-
服务发现
-
开启注解 @EnableDiscoveryClient
-
获取所有的服务
- 通过discoveryClient和服务Id获取所有的serviceInstance,然后通过自己的策略获取单个实例,然后调用服务
-
负载均衡获取
- loadBalancerClient.choose(服务Id)
-
获取成功之后在通过服务调用框架调用
-
服务调用
RestTemplate
- 需要初始化RestTemplate,Spring容器中默认没有该实例,Ribbon的调用基于RestTemplate
Ribbon
-
定义
- 不是spring cloud alibaba 的组件,是nexfix提供的组件
-
负载均衡
-
提供了七种算法
-
常用算法
-
默认采用轮询的算法
-
随机算法
-
全局配置
- 注册对应的bean进入pring容器 RandomRule
-
针对单个服务
-
在消费的配置文件中添加
- ${服务Id}.ribbon.NFLoadBalancerRuleClassName:com.netflix.loadbancer.RandomRule
-
-
-
基于权重的算法
-
全局配置
- 1.继承AbstractLoadBalanceRule 重写choose方法
- 2.注册对应的bean进入pring容器 重写的类
-
针对单个服务
- 1.继承AbstractLoadBalanceRule 重写choose方法
- 2.修改消费服务的配置文件 ${服务Id}.ribbon.NFLoadBalancerRuleClassName:指定class的路径
-
-
-
注意
- 在RestTemplate的创建配置中添加@loadBalanced
- 不加@loadBalanced就不能通过http://服务名/调用后缀 查找
-
熔断器
解决的问题
- 雪崩效应:有一个服务错误导致其他服务调用该服务时一直处于阻塞状态,进而导致其他服务也处于阻塞状态,引起服务器资源被占满导致的服务瘫痪。
解决办法
- 设置线程超时
- 设置限流
- 熔断器 Sentinel、Hystrix
Sentinel
-
详细步骤
-
依赖引入
com.alibaba.cloud spring-cloud-starter-alibaba-sentinel org.springframework.boot spring-boot-starter-actuator- spring-cloud-starter-alibaba-sentinel
- spring-boot-starter-actuator
-
开放接口权限
- management.endpoints.web.exposure.include: ‘*’
-
配置sentinel控制台
- spring.cloud.sentinel.transport.dashbord: 控制台项目地址
-
-
流控规则
-
流控模式
-
直接限流
- 超过阈值(QPS)后不可用
-
关联限流
- 与之关联的资源超过阈值(QPS)后,这个资源不可用, a 关联 b ,b超过阈值 则a不可用
-
链路限流
- 1.通过@SentinelResource(“资源名字”) 来标识
- 2.流控对应的标注资源然后设置阈值和入口资源,则通过入口资源路径进入的请求超过阈值后会被限流
- 注意:1.6.3版本以上的sentinel 默认收敛所有的url入口 导致链路限流不生效,需要手动去开启收敛,1.7.0以上的版本官方提供了参数配置是否收敛,需要导入 sentinel-core 和 sentinel-web-servlet,来关闭收敛,还需要写一个配置类
-
-
流控效果
-
快速失败
- 直接返回错误界面
-
Warm up(预热等待)
- 在预热时长内 阈值为设置的1/3
-
排队等待
- 第一次请求失败后,不会直接返回错误界面,会在设置的超时时间过后再次请求,如果成功则返回成功结果,失败则返回错误界面
-
-
-
降级规则
-
降级策略
-
RT
- 单个请求的响应时间超过阈值,则进入准降级状态,接下来 1 S 内连续 5 个请求响应时间均超过阈值,就进行降级,持续时间为时间窗口的值。
-
异常比例
- 每秒异常数量占通过量的比例大于阈值,就进行降级处理,持续时间为时间窗口的值。
-
异常数
- 1 分钟内的异常数超过阈值就进行降级处理,时间窗口的值要大于 60S,否则刚结束熔断又进入下一次熔断了。
-
-
-
热点规则
-
定义
- 热点规则是流控规则的更细粒度操作,可以具体到对某个热点参数的限流,设置限流之后,如果带着限流参数的请求量超过阈值,则进行限流,时间为统计窗口时长。
-
注意
- 必须要添加 @SentinelResource,即对资源进行流控。
-
-
授权规则
-
定义
-
给指定的资源设置流控应用(追加参数),可以对流控应用进行访问权限的设置,具体就是添加白名单和黑名单。
-
如何给请求指定流控应用,通过实现 RequestOriginParser 接口来完成
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;
public class RequestOriginParserDefinition implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String name = httpServletRequest.getParameter(“name”);
if(StringUtils.isEmpty(name)){
throw new RuntimeException(“name is null”);
}
return name;
}
} -
要让 RequestOriginParserDefinition 生效,需要在配置类中进行配置。
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;
@Configuration
public class SentinelConfiguration {@PostConstruct
public void init(){
WebCallbackManager.setRequestOriginParser(new RequestOriginParserDefinition());
}
}
-
-
-
自定义规则异常返回
-
分类
- 限流的异常处理 FlowException
- 降级的异常处理 DegradeException
-
步骤
-
实现 UrlBlockHandler
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class ExceptionHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
httpServletResponse.setContentType(“text/html;charset=utf-8”);
String msg = null;
if(e instanceof FlowException){
msg = “限流”;
}else if(e instanceof DegradeException){
msg = “降级”;
}
httpServletResponse.getWriter().write(msg);
}
} -
配置 WebCallbackManager.setUrlBlockHandler(自定义异常处理类);
@Configuration
public class SentinelConfiguration {@PostConstruct
public void init(){
WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());
}
}
-
-
常用消息中间件
ActiveMQ , RabbitMQ ,RocketMQ ,Kafka
服务网关
Zuul
- 第一代网关 Netflix公司提供的
spring-cloud-gateWay
-
定义
- 第二代网关,Spring官方提供的,基于netty实现的,所以工程中不能出现Servlet的组件
-
路由映射
-
通过配置路由来映射地址
-
步骤
-
1.引入spring.cloud.starter-getway
-
2.配置spring.cloud.gateway.discovery.locator.enabled=true
- 是否与服务发现组件进行结合,通过 serviceId 转发到具体的服务实例。默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
-
3.配置路由映射
spring:
cloud:
gateway:
routes:
#服务ID,自定义
- id: provider_route
#映射路径
uri: http://localhost:8081
#访问的服务前缀
predicates:
- Path=/provider/**
#忽略真实访问的前缀个数
filters:
- StripPrefix=1
-
-
-
注册到nacos来映射地址
-
步骤
- 1.引入spring.cloud.starter-getway
- 2.引入spring-cloud-starter-alibaba-nacos-discovery
- 3.配置spring.cloud.gateway.discovery.locator.enabled=true
-
-
-
限流
-
路由限流
-
步骤
-
1.引入spring-cloud-starter-gateway
-
2.引入sentinel和gataway的适配依赖 sentinel-spring-cloud-gateway-adapter
-
3.定义网关的配置类
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.*;@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}//配置限流的异常处理
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}//配置初始化的限流参数
@PostConstruct
public void initGatewayRules(){
Set rules = new HashSet<>();
rules.add(
new GatewayFlowRule(“provider_route”)
.setCount(1)
.setIntervalSec(1)
);
GatewayRuleManager.loadRules(rules);
}//初始化限流过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}//自定义限流异常页面
@PostConstruct
public void initBlockHandlers(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap();
map.put(“code”,0);
map.put(“msg”,“被限流了”);
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}- 配置限流的异常处理
- 配置初始化的限流参数
- 初始化限流过滤器
- 自定义限流异常返回
-
4.配置路由映射配置
server:
port: 8010
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: provider_route
uri: http://localhost:8081
predicates:
- Path=/provider/**
filters:
- StripPrefix=1
-
-
-
API分组限流
-
步骤
-
1.引入依赖
-
spring-cloud-starter-gateway
- 网关服务
-
spring-cloud-starter-alibaba-nacos-descovery
- nacos服务
-
sentinel-spring-cloud-gateway-adpter
- sentinel和gateway的适配器
-
-
2.修改路由限流的配置类
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.*;@Configuration
public class GatewayConfiguration {private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}//配置限流的异常处理
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}//配置初始化的限流参数
@PostConstruct
public void initGatewayRules(){
Set rules = new HashSet<>();
rules.add(new GatewayFlowRule(“provider_api1”).setCount(1).setIntervalSec(1));
rules.add(new GatewayFlowRule(“provider_api2”).setCount(1).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}//初始化限流过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}//自定义限流异常页面
@PostConstruct
public void initBlockHandlers(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap();
map.put(“code”,0);
map.put(“msg”,“被限流了”);
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}//自定义API分组
@PostConstruct
private void initCustomizedApis(){
Set definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition(“provider_api1”)
.setPredicateItems(new HashSet(){{
add(new ApiPathPredicateItem().setPattern("/provider/api1/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
ApiDefinition api2 = new ApiDefinition(“provider_api2”)
.setPredicateItems(new HashSet(){{
add(new ApiPathPredicateItem().setPattern("/provider/api2/demo1"));
}});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
}- 添加基于 API 分组限流的方法
- 修改初始化的限流参数
-
3.开启服务发现的配置
- spring.cloud.gateway.discovery.locator.enabled=true
-
4.controller的服务请求路径前缀添加
@GetMapping("/api1/demo1")
public String demo1(){
return “demo”;
}@GetMapping("/api1/demo2")
public String demo2(){
return “demo”;
}@GetMapping("/api2/demo1")
public String demo3(){
return “demo”;
}@GetMapping("/api2/demo2")
public String demo4(){
return “demo”;
} -
4.注意
- 如果服务提供方的serverId发生变化,我们也因该改为 discovery 中的服务名。
-
-
-
注意
- 都是针对网关层面的限流,并未像sentinel一样针对的是服务本身
-
-
注意
- 一定不能出现 spring web 的依赖,因为 Gateway 与 Servlet 不兼容。
分布式事务
分布式事务需要考虑的事情
- 业务侵入性(基于注解、XML,补偿逻辑)
- 隔离性(写隔离/读隔离/读未提交,业务隔离/技术隔离)
- TM/TC部署形态(单独部署、与应用部署一起)
- 错误恢复(自动恢复、手动恢复)
- 性能(回滚的概率、付出的代价,响应时间、吞吐)
- 高可用(注册中心、数据库)
- 持久化(数据库、文件、多副本一致算法)
- 同步/异步(2PC执行方式)
- 日志清理(自动、手动)
好的分布式事务宽假应用尽可能具备的特性
- 1.业务改造成本低
- 2.性能损耗低
- 3.隔离性保证完整
Seata
-
定义
- 阿里提供的分布式解决方案
-
整体情况
-
文档地址
- http://seata.io/zh-cn/
-
优点
- 业务少感知
- DB操作简单
-
缺点
- 响应时间长
- DB写操作较多
- 调用链较长
-
特性
- 业务改造成本低
- 隔离性保证完整
-
-
模式
-
AT模式
-
前提
- 基于支持本地 ACID 事务的关系型数据库。
- Java 应用,通过 JDBC 访问数据库。
-
整体机制
-
一阶段:
- 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
-
二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
-
-
-
TCC模式
-
Saga模式
-
Saga
-
特性
- 业务改造成本低
- 性能损耗低
TCC
-
特性
- 性能损耗低
- 隔离性保证完整