环境搭建
nacos 2.0.0
官方文档地址:https://nacos.io/zh-cn/docs/what-is-nacos.html
下载链接:https://pan.baidu.com/s/1mm0cnJepEVUJ7ZcoeDZCtg
提取码:kunt
sentinel-dashboard-1.8.1
官方文档地址:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
下载链接:https://pan.baidu.com/s/1aDLQYtMissPH1Z1AoJPpbw
提取码:yna3
启动方式
1.启动nacos:
进入nacos的bin目录执行命令
.\startup.cmd -m standalone
进入管理界面
管理界面
http://localhost:8848/nacos/#/login
账号:nacos
密码:nacos
2.启动sentinel
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\sentinel\sentinel-dashboard-1.8.1.jar
管理界面
http://localhost:8718/#/login
账号:sentinel
密码:sentinel
项目搭建集成
消费端:
cloudalibaba-consumer-nacos-order84
服务端:
cloudalibaba-consumer-nacos-payment9003
cloudalibaba-consumer-nacos-payment9004
服务端:
1.引入pom
<dependencies>
<!--nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<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>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>
<scope>test</scope>
</dependency>
</dependencies>
2.修改yml
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置nacos地址
management:
endpoints:
web:
exposure:
include: '*'
3.修改启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class,args);
}
}
4.修改controller
简单demo没有连数据库,id不等于100就返回内容,如果id等于100的话data就返回null
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/payment")
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/{id}")
public CommonResult echo(@PathVariable("id") Long id) {
CommonResult<Payment> result = new CommonResult<>();
if(id!=100){
Payment payment = new Payment();
payment.setId(id);
payment.setSerial("202104113113");
result = new CommonResult(
200,"success:"+serverPort,payment);
}else {
result = new CommonResult(
200,"success:"+serverPort,null);
}
return result;
}
}
消费端
1.修改pom
<dependencies>
<!--nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos后续持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- sentinel-->
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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>
<scope>test</scope>
</dependency>
</dependencies>
2.修改yml
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置nacos地址
sentinel:
transport:
dashboard: localhost:8718 #sentinel地址
port: 8719
# nacos配置持久化
datasource:
ds1:
nacos:
server-addr: 127.0.0.1:8848 #nacos地址
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
#消费者访问微服务服务端名称
service-url:
nacos-user-service: http://nacos-payment-provider
#激活sentinel对feign的支持
feign:
sentinel:
enabled: true
3.目录结构
4.修改启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
5.配置RestTemplate+负载
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced//开启负载均衡的功能
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
6.服务熔断降级配置详解
1. 不配置服务熔断
@GetMapping(value = "/fallback/{id}")
@SentinelResource(value = "fallback")//没有配置
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult 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;
}
根据上面demo逻辑
id为4的时候会报非法参数异常
id为100的时候会报空指针异常
2.配置fallback(管理代码业务异常)
配置fallback之后,代码的业务逻辑出现异常之后,不直接把异常抛给用户,而是用一个捕获异常的方法,称之为兜底服务,你出错,我来兜底,为你的报错买单,给用户友好提示
fallback的参数就是兜底的方法名,这里是handlerFallback方法
@GetMapping(value = "/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult 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;
}
// 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数
public CommonResult handlerFallback(Long id,Throwable exception){
return new CommonResult(444,"handlerFallback异常兜底服务"+exception.getMessage()+" 服务端服务不可用");
}
这时候我们调用接口发现,当id为4非法参数异常的时候,没有向之前那样,直接返回报错信息,而是返回了我们自定义的异常信息。
3.配置blockHandler(配合Sentinel控制台配置)
配置接口流控规则,1s访问一次
@GetMapping(value = "/fallback/{id}")
@SentinelResource(value = "fallback")//没有配置
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult 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;
}
当我们频繁点击,超过1s一次的限制之后,页面会返回异常,当我们不想让它直接抛出异常,而是给友好提示,这时候就需要配置blockHandler搭配页面配置使用
修改
@SentinelResource(value = "fallback",blockHandler = "blockHandler")//blockHandler只负责Sentinel控制台配置
添加blockHandler指向的方法
// 服务流量控制处理,参数最后多一个 BlockException,其余与原函数一致。
public CommonResult blockHandler(Long id,BlockException exception){
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务端服务不可用");
}
再来测试,1s中调用多次,这次超过接口调用限制之后,使用了我们自己的异常信息。
4.举一反三
如果两者都配置则 被限流降级而抛出BlockException时只会进入blockHandler
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
exceptionsToIgnore排除异常
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", exceptionsToIgnore={IllegalArgumentException.class})
这里大家可以自己去尝试一下,不在演示。
5.openfeign服务调用
1.controller
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> payment_ok(@PathVariable("id") Long id){
return paymentService.payment_ok(id);
}
2.service
@Component
@FeignClient(value = "nacos-payment-provider",fallback = PaymentServiceImpl.class)
public interface PaymentService {
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> payment_ok(@PathVariable("id") Long id);
}
3.serviceImpl
注意
//import org.springframework.stereotype.Component;
@Component
public class PaymentServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> payment_ok(Long id) {
return new CommonResult<>(-99999,"服务降级返回,PaymentServiceImpl");
}
}
@FeignClient(value = "nacos-payment-provider",fallback = PaymentServiceImpl.class)
代码详解
value表示的服务名,这里也就是上面服务端注册进nacos的服务名称,fallback指的是,当服务端接口出现了异常的时候,指向自己的兜底接口,我们这里直接指向了实现类,当关闭服务端之后,调用controller测试,服务端关闭,走了我们自己的实现类方法,保证了系统的稳定,没有因为服务端的异常影响到自己。
6.sentinel持久化
当sentinel重新启动时,sentinel dashboard中原来的数据将会全部消失,这样就需要重新定义限流规则,无疑是不可取的。所以需要将sentinel中定义的限流规则保存到配置中心里面。
1.关键pom
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.yml
# nacos配置持久化
datasource:
ds1:
nacos:
server-addr: 127.0.0.1:8848 #nacos地址
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3.nacos添加配置
参数 | 描述 | 描述 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
[
{
"resource": "GET:http://nacos-payment-provider/payment/{id}",
"count": 1,
"grade": 1,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 0
}
]