springboot和spring cloud alibaba版本有对应关系,具体:版本对应
这里使用
springboot:2.3.12.RELEASE
spring-cloud-alibaba:2.2.10-RC1
spring-cloud:Hoxton.SR12
父工程pom,子类模块继承即可
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.chenkegroupId>
<artifactId>spring-cloud-alibaba-tulingartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>spring-cloud-alibaba-tulingname>
<description>spring-cloud-alibaba-tulingdescription>
<packaging>pompackaging>
<properties>
<java.version>1.8java.version>
<spring.cloud.alibaba.version>2.2.10-RC1spring.cloud.alibaba.version>
<spring.cloud.version>Hoxton.SR12spring.cloud.version>
<spring.boot.version>2.3.12.RELEASEspring.boot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
核心功能
注册中心+配置中心+服务管理
nacos下载:nacos下载,这里用2.2.0 win版本
下载后解压,修改nacos\bin目录下startup.cmd中 set MODE=“standalone”
(默认为cluster集群,这里改为单机)
nacos\conf\application.properties 可以修改nacos的启动端口等信息,也可以设置数据源和mysql整合
双击startup.cmd启动,访问http://localhost:8848/nacos/#/login,账号密码都为nacos
新建两个子模块,引入依赖order-nacos 和 stock-nacos,利用nacos实现order调用stock服务的接口
order-nacos 和 stock-nacos的pom
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
order-nacos 主要代码
要使用restTemplate来进行请求
必须加上@LoadBalanced,nacos自身并不具备服务名转地址的功能,需要这个注解。自带轮询的负载均衡算法
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
{
return restTemplateBuilder.build();
}
配置类
server:
port: 8010
spring:
application:
name: order-service
cloud:
nacos:
#nacos地址
server-addr: 127.0.0.1:8848
#哪个用户的
discovery:
#哪个用户的
username: nacos
password: nacos
#命名空间 默认public
namespace: public
controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/getOrder")
public String get()
{
//这里使用服务名来调用,如果没有nacos就要写具体的ip地址,难以维护
return restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
}
}
stock-nacos 主要代码
配置类除了服务名和端口同上一直,提供接口
@RestController
@RequestMapping("/stock")
public class StockController {
@Value("${server.port}")
String port;
@RequestMapping("/reduct")
public String reduct()
{
return "hello,world" + port;
}
}
启动两个stock,在order模块请求时会在客户端自动使用负载均衡算法,在两个stock中切换
上面为大的分组,对应配置文件的namespace: public,适合不同项目时的管理,下面的分组名称适合项目内部的分类。
雪崩保护(nacos的不常用,一般用sentinel):
保护阈值: 设置0-1之间的值 0.6
临时实例: spring.cloud.nacos.discovery.ephemeral =false, 当服务宕机了也不会从服务列表中剔除
如果达到阈值,请求依然会发送到不健康状态的服务上,避免同时太多的请求到达健康的实例导致支撑不住
常用的轮询方法
使用
@Bean
//这个就是ribbon的注解
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
{
return restTemplateBuilder.build();
}
就可以用服务名替代ip地址进行访问了,而且有默认的负载均衡算法(轮询)
@RequestMapping("/getOrder")
public String get()
{
return restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
}
配置负载均衡
1.java配置方法
config要放在springboot项目扫描不到的包中,不如不同的负载均衡算法会失效
RandomRuleConfig
@Bean
public IRule getRule()
{
return new RandomRule();
}
WeightedResponseTimeRuleConfig
@Bean
public IRule getRule()
{
return new WeightedResponseTimeRule();
}
使用
@SpringBootApplication
//name是服务名(nacos上),多个@RibbonClient就是针对不同的服务用不同的算法
@RibbonClients(value = {
@RibbonClient(name = "stock-service",configuration = RandomRuleConfig.class)
//,@RibbonClient(name = "stock-xxx",configuration = WeightedResponseTimeRuleConfig.class)
})
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
{
return restTemplateBuilder.build();
}
}
2.配置文件方式
采用nacos的权重,首先在nacos上配置服务的权重,或者在项目中配置。
spring.cloud.nacos.discovery.weight
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
3.自定义负载均衡算法
实现抽象类AbstractLoadBalancerRule
Server choose(Object var1) 代表选择哪个服务
ILoadBalancer 表示从nacos上获取对应服务名的服务
默认都是懒加载,第一次调用才会加载可能会慢,可以修改配置
ribbon:
eager‐load:
# 开启ribbon饥饿加载
enabled: true
# 配置mall‐user使用ribbon饥饿加载,多个使用逗号分隔
clients: mall‐order
Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不
到这是远程方法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接
口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据。它解决了
让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布
式环境开发。
引入依赖(客户端)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
创建接口feign(客户端)
//服务名,path是类上的@RequestMapping,没有可不写
@FeignClient(value = "stock-service",path = "/stock")
public interface StockControllerFeign {
@RequestMapping("/reduct")
String reduct();
}
对应的服务上的实现类
@RestController
@RequestMapping("/stock")
public class StockController {
@RequestMapping("/reduct")
public String reduct()
{
return "hello,world" + port;
}
}
springboot主方法上加上注解起开(创建的接口要可以被扫描到)
@EnableFeignClients
客户端调用服务端接口
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private StockControllerFeign stockControllerFeign;
@RequestMapping("/getOrder")
public String get()
{
return stockControllerFeign.reduct();
}
}
springboot的日志等级默认为info,先修改为debug
//该包名下
logging:
level:
com.chenke.order.feign: debug
配置文件形式(全局配置)
@Configuration
public class FeignLog {
@Bean
public Logger.Level feignLoggerLevel() {
//设置日志等级
return Logger.Level.FULL;
}
}
配置文件形式(部分feign才有日志)
feign:
client:
config:
stock-service: #对应微服务
loggerLevel: FULL
配置类形式(部分feign才有日志)
去掉@Configuration
public class FeignLog {
@Bean
public Logger.Level feignLoggerLevel() {
//设置日志等级
return Logger.Level.FULL;
}
}
在对应的feign的注解上添加configuration
@FeignClient(value = "stock-service",path = "/stock",configuration = FeignLog.class)
feign:
client:
config:
stock-service: #对应微服务
loggerLevel: FULL
connectTimeout: 5000 #连接超时时间,默认2s,A连接到B的时间
readTimeout: 10000 #请求处理超时时间,默认5s,连接之后B处理的时间
配置文件,使用和上面日志的配置差不多
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(5000, 10000);
}
}
在fegin调用远程服务之前进行拦截处理(写在客户端)
public class MyInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//业务逻辑
System.out.println("自定义拦截器启动");
}
}
注入方式(代码)
@Configuration
public class FeignLog {
@Bean
public MyInterceptor getMyInterceptor()
{
return new MyInterceptor();
}
}
注入方式(配置文件)
feign:
client:
config:
stock-service: #对应微服务
loggerLevel: FULL
connectTimeout: 5000 #连接超时时间,默认2s,A连接到B的时间
readTimeout: 10000 #请求处理超时时间,默认5s,连接之后B处理的时间
requestInterceptors[0]: com.chenke.order.config.MyInterceptor
requestInterceptors[1]: com.chenke.order.config.MyInterceptor
注解模式和日志差不多
官方文档
原理是在项目中,会有一个定时器,大概10ms拉取nacos上最新的MD5和本地的MD5做比较,如果不同就会进行跟新
如果要做权限管理,要开启nacos的配置文件中的
nacos.core.auth.caching.enabled=true
引入依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
在项目中必须引入一个bootstrap.properties,可以把nacos的配置都移动到这
# 必须和服务名相同
spring.application.name=order-service
spring.cloud.nacos.server-addr=127.0.0.1:8848
# 如果开启了权限要添加账号密码
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
在nacos上配置文件(默认data id要是服务名.properties)
使用
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
使用yaml文件
这个配置的文件后缀只针对默认的配置文件(和服务同名)
bootstrap.properties 加上
# 默认是properties
spring.cloud.nacos.config.file-extension=yaml
再把nacos上的data id改成 服务名+yaml
只有默认的配置文件(data id相同的)才能用
使用这种格式,然后在bootstrap.properties添加
spring.profiles.active=dev
spring.profiles.active=dev 如果dev不存在,还会使用默认的配置文件。如果dev中某个属性不存在,也会使用默认的配置文件中的值
dev
chen.name=ck
默认文件
chen.age=15
在spring.profiles.active=dev情况下 name和age都能获取到值
可以修改去那个空间获取配置文件(默认public)
spring.cloud.nacos.config.namespace=cc5458d0-77e1-4cd1-950a-291c402bac57
可以修改去哪个组获取配置文件(默认DEFAULT_GROUP)
spring.cloud.nacos.config.group=chenkeGroup
spring.cloud.nacos.config.extension-configs[n].data-id 的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。文件名字可以乱写,但是扩展名必须properties或者yaml/yml
spring.cloud.nacos.config.extension-configs[0].data-id=chenke.properties
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true
等价于
spring.cloud.nacos.config.shared-configs[0].data-id=chenke.properties
spring.cloud.nacos.config.shared-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].refresh=true
但是在文件中如果存在相同的key优先级有所不同,会根据优先级覆盖
profiles>默认>extension-configs(下标越大越优先)>shared-configs(下标越大越优先)
@Component
@RefreshScope
public class User {
@Value("${user.name}")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Value可以直接读取远程配置文件,加上@RefreshScope可以实时刷新远程的修改
官网
限流,每秒只能访问一次
导入依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
<version>1.8.6version>
dependency>
代码形式
@RestController
@RequestMapping("/sentinel")
public class SentinelController {
//等于一个方案名
private static final String RESOURCE_NAME = "hello";
@RequestMapping(value = "/hello")
public String hello() {
Entry entry = null;
try {
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
return "hello";
} catch (BlockException e) {
// 资源访问阻止,被限流或被降级
//进行相应的处理操作
System.out.println("block");
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
}
finally {
if (entry != null) {
entry.exit();
}
}
return "g";
}
/**
* 定义流控规则 初始化bean的
*/
@PostConstruct
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// 一秒一次
rule.setCount(1);
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
详细使用官方
不仅仅可以针对controller,在业务方法上也可以设置
依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
<version>1.8.6version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
<version>1.8.6version>
dependency>
注入切面
@Configuration
public class SentinelConfig {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
使用
@RestController
@RequestMapping("/sentinelAnn")
public class SentinelAnnController {
//等于一个方案名
private static final String SENTINEL_RESOURCE = "SentinelResource";
@RequestMapping(value = "/hello")
@SentinelResource(
value = SENTINEL_RESOURCE,
blockHandler = "qpsHello",
defaultFallback = "throwableHello1",
fallback = "throwableHello"
)
public String hello() {
int i = 1/0;
return "hello";
}
public String qpsHello(BlockException ex)
{
ex.printStackTrace();
return "我被限流了呃";
}
public String throwableHello(Throwable ex)
{
ex.printStackTrace();
return "我异常了,贼炸";
}
public String throwableHello1(Throwable ex)
{
ex.printStackTrace();
return "我异常了,贼炸1";
}
/**
* 定义流控规则 初始化bean的
*/
@PostConstruct
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置受保护的资源
rule.setResource(SENTINEL_RESOURCE);
// 设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// 一秒一次
rule.setCount(1);
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
blockHandler 优先级大于fallback,比如限制了qps为1,但是代码会有算术异常。在多次访问的情况下走blockHandler 。 所有方法都必须为public
官方使用
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
例如:调用服务订单–>用户积分–>库存模块。这里的用户积分就属于弱模块,当积分模块出现问题,可以暂时降级为服务订单–>库存模块,积分模块在后期再进行补偿。
@RestController
@RequestMapping("/degrade")
public class DegradeController {
//等于一个方案名
private static final String SENTINEL_RESOURCE = "degrade";
@RequestMapping(value = "/hello")
@SentinelResource(
value = SENTINEL_RESOURCE,
blockHandler = "deHello"
)
public String hello() {
//模拟积分(弱依赖)模块出问题情况
System.out.println("订单创建成功");
System.out.println("积分调用成功"+1/0);
System.out.println("库存调用成功");
return "成功";
}
//降级后方法
public String deHello(BlockException ex)
{
//舍弃积分(弱依赖)的调用
System.out.println("订单创建成功");
System.out.println("库存调用成功");
return "成功";
}
@PostConstruct
private static void initFlowRules() {
List<DegradeRule> rules = new ArrayList<>();
//再30秒内 最少有两次请求,且异常两次后走降级方法,10秒后恢复
// 熔断恢复后 如果马上又异常,不会进行上面的判断,而是直接继续降级
// count数会多一次,源码用的不是大于等于
DegradeRule rule = new DegradeRule(SENTINEL_RESOURCE)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
.setCount(2) //两次(实际三次)
.setMinRequestAmount(2) //最少两次请求
.setStatIntervalMs(30*1000) //30秒内
.setTimeWindow(10);//熔断降级10秒
rules.add(rule);
DegradeRuleManager.loadRules(rules);
//在每次状态变化的时候回调
EventObserverRegistry.getInstance().addStateChangeObserver("logging",
(prevState, newState, rule1, snapshotValue) -> {
if (newState == CircuitBreaker.State.OPEN) {
// 变换至 OPEN state 时会携带触发时的值
System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
TimeUtil.currentTimeMillis(), snapshotValue));
} else {
System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
TimeUtil.currentTimeMillis()));
}
});
}
}
导入依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
<version>1.8.6version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
<version>1.8.6version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-transport-simple-httpartifactId>
<version>1.8.6version>
dependency>
下载对应的控制台jar包
https://github.com/alibaba/Sentinel/releases
启动jar包,可以编辑成win批处理
java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=chenke -Dsentinel.dashboard.auth.password=123456 -jar D:\huangjing\sentinel-dashboard-1.8.6.jar
pause
启动后在项目的jvm参数中添加
端口和上方客户端端口对应
-Dcsp.sentinel.dashboard.server=127.0.0.1:8858
启动项目后访问接口,即可看到接口访问情况,以及用代码配置的策略
官方文档
依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
项目配置文件添加,客户端的端口信息
spring:
application:
name: order-service
cloud:
nacos:
#nacos地址
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
sentinel:
transport:
dashboard: 127.0.0.1:8858
如果抛出的异常不需要做比较特殊的处理。可以设置统一的异常处理,在接口上就不需要写@SentinelResource注解了,但是这样就没有资源名了,直接根据接口路径配置即可
e.getRule 可以返回详细的控制规则
可以看到所有的接口和配置的资源名 (配置规则要在资源名的那一行配置,这里是自定义的degrade)
QPS:一秒内可以请求多少次(单机阈值),超过的请求抛出异常
并发线程数:针对慢sql等情况。同时允许多少个线程(单机阈值)同时执行。例如一个服务需要10秒才能进行响应,设置了单机阈值为1,只有在一个线程执行完10秒后返回,新的请求才能进入。
直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
例子(限制了每秒1次):
直接:该资源超过限制即触发
关联:例如资源名A为查询的功能,可以配置一个关联的路径B为关联资源(修改),用B来判断规则,如果达到限流A。因为修改和查询同时占用资源,但是修改优先度更高。
链路:可以配置路径,针对这些路径进行限流(没有配置的路径可以随便访问该资源)
设置两个接口,和一个业务类。模拟查询和修改的情况
@RequestMapping(value = "/get")
public String hello() {
return orderService.getName("查询");
}
@RequestMapping(value = "/update")
public String hello1() {
return orderService.getName("修改");
}
配置
如果update达到了规则,则限制get的功能,update不受影响
限流的是get接口,而不是业务实现
配置文件加上
spring.cloud.sentinel.web‐context‐unify: false
如果@SentinelResource写再业务方法上 ,无法通过BlockException异常的统一处理,只能自己编写方法
设置两个接口,和一个业务类。模拟查询和修改的情况
@RequestMapping(value = "/get")
public String hello() {
return orderService.getName("查询");
}
@RequestMapping(value = "/update")
public String hello1() {
return orderService.getName("修改");
}
@Service
//模拟业务调用数据库
public class orderService {
@SentinelResource(value = "getName",blockHandler = "getNameDe")
public String getName(String name)
{
System.out.println("调用数据库。。。。");
return name;
}
public String getNameDe(String name, BlockException ex)
{
return "限流"+name;
}
}
请求一次接口后查看控制台,会出现调用的链路
在簇点链路界面随便点击一个getName进行配置
点击新增后添加规则,然后在流控规则的页面就能看到配置。可以重复上面步骤配置多个链路。
把规则update的删除,就会发现,只有查询会被流控。而修改可以随便访问,限流的业务实现,而不是get或者update接口
例子
@RestController
@RequestMapping("/degrade")
public class DegradeController {
@RequestMapping(value = "/get")
@SentinelResource(value = "hot",blockHandler = "hotHello")
public String hello(Integer id) {
return "成功"+id;
}
public String hotHello(Integer id,BlockException ex) {
return "限流了"+id;
}
}
流控id为1的参数
测试结果:访问id为1的4次后进入了限流方法,而反问id为2需要11次。
单机阈值: 针对所有参数的值进行设置的一个公共的阈值
属于兜底的保护规则,在不清楚什么原因,或者流量毫无规律的情况下设置全局的保护
在调用方也实现远程接口的实现,在@FeignClient的注解fallback后面添加
默认控制台的配置没有持久化,重启就消失,这里使用nacos的配置中心来使用
导入依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
[
{
"resource":"hot",
"controlBehavior":0,
"count":3.0,
"grade":1,
"limitApp":"default",
"strategy":0
}
]
在项目中添加配置文件
server:
port: 8010
spring:
application:
name: order-service
cloud:
nacos:
#nacos地址
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
#sentinel配置~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sentinel:
transport:
dashboard: 127.0.0.1:8858
web-context-unify: false
datasource:
flow-rule: # 可以自定义
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
dataId: sentinel.rule
rule‐type: flow
重启项目即可。但是如果在sentinel上修改了配置并不会同步到nacos,需要手动同步(或者源码扩展)
2PC
AT(Seata解决方案)
TCC
可靠消息最终一致性的方案 MQ
官网,下载地址
1.修改持久化配置,默认是file模式,存在本地。但是在集群模式下无法统一,改成db模式
conf/application.yml 下修改,并配置连接。再新建表(seata\script\server\db 文件地址)
2.seata也要注册到注册中心,要通过注册中心和服务来进行交互。配置文件
3.seata配置注册中心配置中心。(扩展配置)
seata\script\config-center\config.txt 这个文件需要部署到nacos的配置中心上,修改mode为db,并修改数据库相关配置
store.mode=db
store.lock.mode=db
store.session.mode=db
# 关于db的配置也要修改
4.文件中的service.vgroupMapping.default_tx_group=default 代表异地容灾
5.将这个txt配置文件同步到nacos
seata\script\config-center\nacos目录下的nacos-config.sh,无法运行安装一个git
sh nacos-config.sh -u nacos -w nacos
-h ip地址 默认localhost
-p 端口 默认8848
-g 配置分组 默认SEATA_GROUP
-t 租户信息,默认为空,对应nacos命名空间id
-u 账号 如果nacos开启了权限
-p 密码 如果nacos 开启了权限
6.配置 @GlobalTransactional
两个模块,订单和库存
都添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring‐cloud‐starter‐alibaba‐seata</artifactId>
</dependency>
7.各微服务对应数据库中添加undo_log表(seata失败时的逆向sql)
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
1.新建模块gateway,并加入依赖(和mvc的依赖冲突)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
2.修改application.yml配置
server:
port: 8088
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: order_cyz
uri: http://127.0.0.1:8011 #需要路由的路径
predicates:
- Path=/cyz/** #断言,匹配路径
filters:
- StripPrefix=1 #过滤器,去掉第一层的请求
3.访问
http://127.0.0.1:8088/cyz/stock/reduct 等于访问 http://127.0.0.1:8011/stock/reduct
routes:
- id: order_cyz
uri: http://127.0.0.1:8011 #需要路由的路径
predicates:
‐ After=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之前才能请求
‐ Before=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之后才能请求
‐ Between=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai],2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之前才能请求
‐ RemoteAddr=192.168.1.1/24
‐ Cookie=chocolate,ch
‐ Header=X‐Request‐Id, \d+
‐ Host=**.testhost.org
‐ Method=GET
‐ Path=/foo/{segment} #{}占位符 可以匹配reful风格
‐ Query=baz, ba. #xxx:8080?baz=1
routes:
‐id: weight_route1
uri: host1
predicates:
‐Path=/product/**
‐Weight=group3, 1
‐id: weight_route2
uri: host2
predicates:
‐Path=/product/**
‐Weight= group3, 9
自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过
exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
1、 必须spring组件 bean
2. 类必须加上RoutePredicateFactory作为结尾
3. 必须继承AbstractRoutePredicateFactory
4. 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
5. 需要结合shortcutFieldOrder进行绑定
6.通过apply进行逻辑判断 true就是匹配成功 false匹配失败
第六点中全部都是
例如添加请求头
filters:
‐ AddRequestHeader=X‐Request‐color, red #添加请求头
继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。