代码 gitee仓库地址
https://gitee.com/lu-yi1104/spring-cloud_-study.git/
https://blog.csdn.net/weixin_43691773/article/details/109167048
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
依赖 父工程已经导入了
https://github.com/alibaba/Nacos
中文地址
https://nacos.io/zh-cn/
下载解压
修改startup.cmd MODE 由cluster改为单机版
启动
访问 localhost:8848/nacos 出现界面代表成功 默认账号密码 nacos/nacos
新建模块 9001
跟着官网来
https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_spring_cloud_alibaba_nacos_discovery
pom文件
在父工程pom文件加入 springcloud alibaba的依赖
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
9001 pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>com.luyi.cloud</artifactId>
<groupId>com.luyi.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-provider-payment9001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
yml文件
server:
port: 9001
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置Nacos地址
# 暴露要监控的
management:
endpoints:
web:
exposure:
include: '*'
主启动类
package com.luyi.sprinhcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author 卢意
* @create 2020-11-06 11:25
*/
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class, args);
}
}
controller层
package com.luyi.sprinhcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 卢意
* @create 2020-11-06 11:27
*/
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPyment(@PathVariable("id") Integer id){
return "nacos register ,serverPort: "+serverPort+"\t id: "+id;
}
}
测试 首先启动nacos注册中心
再启动9001
为了演示nacos的负载均衡、参照9001侯建9002 (也可以直接拷贝9001的虚拟端口映射)
启动
新建order83模块
pom文件与上面相同
yml文件
server:
port: 83
Spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-privider
主启动类
package com.luyi.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author 卢意
* @create 2020-11-06 11:56
*/
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain83.class, args);
}
}
新建上下文配置类
package com.luyi.springcloud.alibaba.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author 卢意
* @create 2020-11-06 13:34
*/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced// resttemplate 结合ribbon做负载均衡的时候 要加入注解
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
controller类
package com.luyi.springcloud.alibaba.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author 卢意
* @create 2020-11-06 13:37
*/
@RestController
@Slf4j
public class OrderNacosController {
@Resource
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@GetMapping(value = "/consumer/payment/nacos/{id}")
public String paymeentInfo(@PathVariable("id") Long id){
return restTemplate.getForObject(serverUrl+"/payment/nacos/"+id,String.class );
}
}
新建配置中心 3377
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>com.luyi.cloud</artifactId>
<groupId>com.luyi.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-config-nacos-client3377</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
# nacos 配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos服务注册中心地址
config:
server-addr: localhost:8848 # Nacos作为服务中心的地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
# ${spring.application.name}-${spring.profile.active}.${Spring.cloud.nacos.config.file.extension}
application.yml
spring:
profiles:
active: dev # 表示开发环境
主启动类
package com.luyi.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author 卢意
* @create 2020-11-06 17:04
*/
@EnableDiscoveryClient
@SpringBootApplication
public class NacosClientConfigMain3377 {
public static void main(String[] args) {
SpringApplication.run(NacosClientConfigMain3377.class, args);
}
}
重复投入
package com.luyi.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 卢意
* @create 2020-11-06 17:10
*/
@RestController
@RefreshScope //支持Nacos动态刷新功能
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo(){
return configInfo;
}
}
${spring.application.name}
${spring.profile.active}
${Spring.cloud.nacos.config.file.extension}
启动发现报错了 我丢 但是不要紧张阳哥 也报了
原因
解决 吧yml 都改成yaml
新建dev 配置DataID
修改application.yml
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
编辑startup脚本
1.4的nacos -p 已经有了 所以我换成port
nginx配置
启动三个nacos
启动nginx
这里我测试失败了 应该是版本问题 1.4版本的太新了 伪集群会出现绑定的问题 有时间再去试一下 1.3版本的吧
https://github.com/alibaba/Sentinel/releases
本次不搞七搞八的 V1.7就好了
localhost:8080
用户密码 sentinel/sentinel
新建模块8491
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>com.luyi.cloud</artifactId>
<groupId>com.luyi.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-sentinel-service8401</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
yml文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
# Nacos作为服务中心的地址
server-addr: localhost:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用 自动+1寻找未被占用的端口
port: 8719
#监控
management:
endpoints:
web:
exposure:
include: '*'
主启动类
package com.luyi.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author 卢意
* @create 2020-11-22 13:48
*/
@SpringBootApplication
@EnableDiscoveryClient // 可以注册进注册中心
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}
controller层
package com.luyi.springcloud.alibaba.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 卢意
* @create 2020-11-22 13:50
*/
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA()
{
return "------------------testA";
}
@GetMapping("/testB")
public String testB()
{
return "------------------testB";
}
}
启动nacos 启动sentinel 在启动8401
sentinel页面还没有东西 因为 它是懒加载模式
访问两次后 有流量通过 就会显示了 Sentinel正在监控8401
系统一次只能处理${单机阈值}个线程数 超过则新进来的线程请求失败
设置资源路口 /testA /testB是统一个资源路口 testA被限制失败之后 testB也会失败
例如 开始设置的阈值为10 设置时间为5秒 冷加载因子(threshold迷人值是3)最初的阈值就是10/3 大致为3 五秒 之后 慢慢阈值升到10
sentinel的断路器是没有半开状态的 而Hystrix有
平均响应时间0.2秒 等待回复时间1秒
新建testD
测试
设置异常率为20% 超过该异常率 则熔断降级
这边是100%的异常率
压测
停止则正常报错
启动五次时 就会熔断 过1分钟才会正常报错
根据请求传入的参数进行限流
首先这是默认相应
第0个参数 的访问次数超过QPS的阈值 触发熔断降级
p2一直点则没事
不使用 b
如果将 blockHandler去掉
异常会显示到页面 blockHandler是一个兜底的方法
总的系统规则 QPS 对cloudalibaba-sentinel-service系统中的所有请求应用都生效
修改 8401的pom文件
<!--引入自己定义的api通用包, 可以使用Payment支付Entity-->
<dependency>
<groupId>com.luyi.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
新建一个Controller类
package com.luyi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author 卢意
* @create 2020-11-22 17:33
*/
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value="byResource",blockHandler = "handleException")
public CommonResult byResource(){
return new CommonResult(200,"按资源名称限流OK",new Payment());
}
public CommonResult handleException(BlockException exception){
return new CommonResult(404,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
注意选择下面那个 资源名称
新增一个业务没有兜底的方法 使用系统默认
package com.luyi.springcloud.alibaba.myHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.luyi.consumer.api.entity.CommonResult;
/**
* @author 卢意
* @create 2020-11-23 13:50
*/
public class CustomerBlockHandler {
public static CommonResult handlerException1(BlockException exception){
return new CommonResult(444,"按客户自定义,global handlerException-----1");
}
public static CommonResult handlerException2(BlockException exception){
return new CommonResult(444,"按客户自定义,global handlerException-----12");
}
}
二号方法兜底
启动8401
正常访问一次http://localhost:8401/rateLimit/customerBlockHandler 让他被sentinel监控
sentinel新增流控规则
详细看官网吧
sentinel整合ribbon+openfeign+fallback
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入自己定义的api通用包, 可以使用Payment支付Entity-->
<dependency>
<groupId>com.luyi.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
yml文件
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
# Nacos\u4F5C\u4E3A\u670D\u52A1\u4E2D\u5FC3\u7684\u5730\u5740
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: '*'
主启动类
package com.luyi.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author 卢意
* @create 2020-11-23 14:30
*/
@SpringBootApplication
@EnableDiscoveryClient
public class Payment9003 {
public static void main(String[] args) {
SpringApplication.run(Payment9003.class, args);
}
}
Controller层
package com.luyi.springcloud.alibaba.controller;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
/**
* @author 卢意
* @create 2020-11-23 14:31
*/
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
public static HashMap<Long,Payment> hashMap=new HashMap<>();
static {
hashMap.put(1L, new Payment(1L,"199811046510"));
hashMap.put(2L, new Payment(2L,"199811046511"));
hashMap.put(3L, new Payment(3L,"199811046512"));
}
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id")Long id){
Payment payment=hashMap.get(id);
CommonResult<Payment> result=new CommonResult(200,"from mysql ,serverport: "+serverPort ,payment);
return result;
}
}
只有3条记录 当Id等于4时为null
新建 消费者 84
pom文件和上面一样
yml文件
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
# 默认端口 加入8719被占用 会自动+1扫描直到找到没有被占用的端口
port: 8719
service-url:
nacos-user-service: http://nacos-payment-provider
RestTemplate配置类
package com.luyi.springcloud.alibaba.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author 卢意
* @create 2020-11-23 14:50
*/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
controller类
package com.luyi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author 卢意
* @create 2020-11-23 14:51
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL="http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback") //没有配置
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result=restTemplate.getForObject(SERVICE_URL, "/payment/"+id,
CommonResult.class,id);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
}else if(result.getData()==null){
throw new NullPointerException("NullPointerException,该Id没有对应记录.空指针异常.....");
}
return result;
}
}
启动84 9003,9004
测试 业务类和 负载均衡无问题
没有设置blockHandler
id3以上都是异常页面
修改业务类代码
package com.luyi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author 卢意
* @create 2020-11-23 14:51
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL="http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result=restTemplate.getForObject(SERVICE_URL+"/payment/"+id,
CommonResult.class,id);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
}else if(result.getData()==null){
throw new NullPointerException("NullPointerException,该Id没有对应记录.空指针异常.....");
}
return result;
}
//本例是fallback的兜底
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常类handlerFallback,Exception内容: "+ e.getMessage(),payment);
}
}
修改业务类
package com.luyi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author 卢意
* @create 2020-11-23 14:51
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL="http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
@SentinelResource(value = "fallback",blockHandler = "blockHandler") // blockHandler 只负责sentinel控制台配置违规
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result=restTemplate.getForObject(SERVICE_URL+"/payment/"+id,
CommonResult.class,id);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
}else if(result.getData()==null){
throw new NullPointerException("NullPointerException,该Id没有对应记录.空指针异常.....");
}
return result;
}
//本例是fallback的兜底
/*public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常类handlerFallback,Exception内容: "+ e.getMessage(),payment);
}*/
//本例是blockHandler的兜底
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"blockHandler-sentinel限流,无此流水,BlockException: "+ e.getMessage(),payment);
}
}
修改业务类
package com.luyi.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author 卢意
* @create 2020-11-23 14:51
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL="http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
//@SentinelResource(value = "fallback",blockHandler = "blockHandler") // blockHandler 只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler") //都配置
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result=restTemplate.getForObject(SERVICE_URL+"/payment/"+id,
CommonResult.class,id);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
}else if(result.getData()==null){
throw new NullPointerException("NullPointerException,该Id没有对应记录.空指针异常.....");
}
return result;
}
//本例是fallback的兜底
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常类handlerFallback,Exception内容: "+ e.getMessage(),payment);
}
//本例是blockHandler的兜底
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水,BlockException: "+ e.getMessage(),payment);
}
}
新增流控规则
限流
fallback
点快了又走 blockHandler 流控规则
feign 接口+注解
package com.luyi.springcloud.alibaba.service;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author 卢意
* @create 2020-11-23 15:58
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallBackService.class)
public interface PaymentService {
@GetMapping("/payment/{id} ")
public CommonResult<Payment> payment(@PathVariable("id") Long Id);
}
兜底 实现类
package com.luyi.springcloud.alibaba.service;
import com.luyi.consumer.api.entity.CommonResult;
import com.luyi.consumer.api.entity.Payment;
import org.springframework.stereotype.Component;
/**
* @author 卢意
* @create 2020-11-23 16:02
*/
@Component
public class PaymentFallBackService implements PaymentService{
@Override
public CommonResult<Payment> payment(Long id) {
return new CommonResult<>(444444,"服务降级返回,----PaymentFallBackService",new Payment(id, "errorSerial"));
}
}
其实只要有持久化的文件都能保存 如redis等等 官方推荐nacos
修改 8401
pom文件 新增 下面依赖
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
application.yml文件加入下列配置
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
[
{
"resource":"/byResource",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]
一次业务操作需要跨多少个数据源或需要跨多少个系统进行远程调用,就会产生分布式事务问题
1.1.0
修改file.conf
创建数据库seata
创建表
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
修改register.conf
启动nacos 在启动SeaTa
Create DATABASE seata_order;
CREATE DATABASE seata_storage;
CREATE DATABASE seata_account;
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = INNODB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>com.luyi.cloud</artifactId>
<groupId>com.luyi.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>seata-order-service2001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.1.0</version>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入自己定义的api通用包, 可以使用Payment支付Entity-->
<dependency>
<groupId>com.luyi.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
Order 实体类
package com.luyi.springcloud.alibaba.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @author 卢意
* @create 2020-11-23 19:24
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private Long id;
private Long userId;
private Long ProductId;
private Integer count;
private BigDecimal money;
/**
* 订单状态: 0创建中 1已完成
*/
private Integer status;
}
CommonResult.class
package com.luyi.springcloud.alibaba.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 卢意
* @create 2020-11-23 19:48
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
private T data;
public CommonResult(Integer code, String message){
this(code,message,null);
}
}
application.yml
server:
port: 2001
spring:
application:
name: seata-order-service
cloud:
alibaba:
seata:
# 自定义事务组名称需要与seata-server中对应
tx-service-group: luyi_tx_group
nacos:
discovery:
server-addr: localhost:8848
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_order
username: root
password: 123456
feign:
hystrix:
enabled: false
logging:
level:
io:
seata: info
mybatis:
mapper-locations: classpath:mapper/*.xml
新建 file.conf
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
# the client batch send request enable
enableClientBatchSendRequest = false
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThreadPrefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
# service configuration, only used in client side
service {
#transaction service group mapping
# vgroupMapping.my_test_tx_group = "default"
vgroupMapping.luyi_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
#client transaction configuration, only used in client side
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackOnConflict = true
}
reportRetryCount = 5
tableMetaCheckEnable = false
reportSuccessEnable = false
sqlParserType = druid
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
logTable = "undo_log"
}
log {
exceptionRate = 100
}
}
## transaction log store, only used in server side
store {
## store mode: file、db
mode = "db"
## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
minConn = 1
maxConn = 10
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
}
}
## server configuration, only used in server side
server {
recovery {
#schedule committing retry period in milliseconds
committingRetryPeriod = 1000
#schedule asyn committing retry period in milliseconds
asynCommittingRetryPeriod = 1000
#schedule rollbacking retry period in milliseconds
rollbackingRetryPeriod = 1000
#schedule timeout retry period in milliseconds
timeoutRetryPeriod = 1000
}
undo {
logSaveDays = 7
#schedule delete expired undo_log in milliseconds
logDeletePeriod = 86400000
}
#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
maxCommitRetryTimeout = "-1"
maxRollbackRetryTimeout = "-1"
rollbackRetryTimeoutUnlockEnable = false
}
## metrics configuration, only used in server side
metrics {
enabled = false
registryType = "compact"
# multi exporters use comma divided
exporterList = "prometheus"
exporterPrometheusPort = 9898
}
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
group = "SEATA_GROUP"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
dao接口及实现
package com.luyi.springcloud.alibaba.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author 卢意
* @create 2020-11-23 19:52
*/
@Mapper
public interface Order {
// 新建订单
void create(Order order);
// 更新订单状态 0变1
void update(@Param("userId") Long userId,@Param("status") Integer status);
}
数据库内容每错
openfeign会报超时异常
状态为0
库存被扣了
账户也被扣钱了
测试没有问题
debug看看
分支号不同 xid相同
rollback_info格式化查看一下
已经将前置和后置快照保存好,便于数据更新和回滚恢复