sentinel整合ribbon+openFeign+fallback
启动nacos和sentinel
Ribbon系列
提供者9003/9004
新建cloudalibaba-provider-payment9003/9004
POM
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用通用返回结果类,以及支付Payment实体类 -->
<dependency>
<groupId>com.sgy.cloud2020</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件
端口号改成不同的
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
#Nacos注册中心地址
server-addr: server.com:8848 #nacos
enabled: true
management:
endpoints:
web:
exposure:
include: "*"
主启动
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class,args);
}
}
业务类
@RestController
public class PaymentController {
private static Map<String, Payment> hashMap = new HashMap<>();
@Value("${server.port}")
private String serverPort;
static {
hashMap.put("1",new Payment("1","7e6qcknsr8hza3jmbvglfw9x2to14p0uiyd5"));
hashMap.put("2",new Payment("2","3ho7t0ka648givcbnp21fexj9suml5zrdqwy"));
hashMap.put("3",new Payment("3","ezdwf16ybvpjacru789oxtsl52gh430mqikn"));
hashMap.put("4",new Payment("4","sbuhf6t950gl8c2m1aeqixonyp4dvr7wk3jz"));
}
@GetMapping("/paymentSQL/{id}")
public R<Payment> payment(@PathVariable(value = "id") String id) {
R<Payment> result = new R<>();
return result.ok("from mysql serverPort: " + serverPort, hashMap.get(id));
}
}
测试地址
然后全部启动,当消费者使用ribbon进行调用的使用会进行轮询调用
消费者
新建cloudalibaba-consumer-nacos-order84
pom
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用通用返回结果类,以及支付Payment实体类 -->
<dependency>
<groupId>com.sgy.cloud2020</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
#Nacos注册中心地址
server-addr: server.com:8848 #nacos
enabled: true
sentinel:
transport:
dashboard: server.com:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
# clientIp: 192.168.1.102
service-url:
nacos-user-service: http://nacos-payment-provider
management:
endpoints:
web:
exposure:
include: "*"
主启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
业务类( ApplicationContextConfig)
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
CircleBreakerController的全部源码
package com.sgu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.sgy.springcloud.entities.Payment;
import com.sgy.springcloud.entities.R;
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;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@Resource
private RestTemplate restTemplate;
@GetMapping("/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",
// exceptionsToIgnore = {IllegalArgumentException.class})
public R<Payment> fallback(@PathVariable(value = "id") String id) throws IllegalArgumentException {
R<Payment> result = restTemplate.getForObject(serverUrl + "/paymentSQL/" + id , R.class);
if (id.equals("5")) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//fallback
public R<Payment> handlerFallback(@PathVariable String id,Throwable e) {
Payment payment = new Payment(id,"null");
return new R<>().error(444L,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
//blockHandler
public R<Payment> blockHandler(@PathVariable String id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new R<>().error(444L,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
消费者(业务类CircleBreakerController)
修改后请重启微服务
热部署对java代码级生效及时
对@SentinelResource注解内属性,有时效果不好
目的
fallback管运行异常
blockHandler管配置违规
测试地址
http://localhost:84/consumer/fallback/1
没有任何配置
给客户error页面,不友好
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback") // 没有配置
public R<Payment> fallback(@PathVariable(value = "id") String id) {
R<Payment> result = restTemplate.getForObject(serverUrl + "/paymentSQL/" + id , R.class);
if (id.equals("5")) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//fallback
public R<Payment> handlerFallback(@PathVariable String id,Throwable e) {
Payment payment = new Payment(id,"null");
return new R<>().error(444L,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
//blockHandler
public R<Payment> blockHandler(@PathVariable String id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new R<>().error(444L,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
只配置fallback
编码(那个业务类下面的CircleBreakerController的全部源码)
只配置blockHandler
sentinel 控制太配置限流
fallback和blockHandler都配置
若 blockHandler和 fallback都进行了配置,则被限流降级而抛出 BlockException时只会进入 blockHandler处理逻辑
快速访问
忽略属性…
编码(那个业务类下面的CircleBreakerController的全部源码)
Feign系列
修改84模块
pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
配置文件
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
#Nacos注册中心地址
server-addr: server.com:8848 #nacos
enabled: true
sentinel:
transport:
dashboard: server.com:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
# clientIp: 192.168.1.102
service-url:
nacos-user-service: http://nacos-payment-provider
#激活sentinel对Feign的支持
feign:
sentinel:
enabled: true
management:
endpoints:
web:
exposure:
include: "*"
业务类
带@FeignClient注解的业务接口
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
R<Payment> paymentSQL(@PathVariable("id") String id);
}
PaymentFallbackService实现类
package com.sgu.springcloud.service;
import com.sgy.springcloud.entities.Payment;
import com.sgy.springcloud.entities.R;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallbackService implements PaymentService{
@Override
public R<Payment> paymentSQL(String id) {
return new R<>().error(44444L,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
Controller
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public R<Payment> paymentSQL(@PathVariable(value = "id") String id) {
return paymentService.paymentSQL(id);
}
主启动
添加@EnableFeignClients启动Feign的功能
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderMain84.class,args);
}
}
测试
访问: localhost:84/consumer/paymentSQL/6
测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死
openFeign服务调用时间
修改9003服务提供者
@RestController
public class PaymentController {
private static Map<String, Payment> hashMap = new HashMap<>();
@Value("${server.port}")
private String serverPort;
static {
hashMap.put("1",new Payment("1","7e6qcknsr8hza3jmbvglfw9x2to14p0uiyd5"));
hashMap.put("2",new Payment("2","3ho7t0ka648givcbnp21fexj9suml5zrdqwy"));
hashMap.put("3",new Payment("3","ezdwf16ybvpjacru789oxtsl52gh430mqikn"));
hashMap.put("4",new Payment("4","sbuhf6t950gl8c2m1aeqixonyp4dvr7wk3jz"));
}
@GetMapping("/paymentSQL/{id}")
public R<Payment> payment(@PathVariable(value = "id") String id) throws InterruptedException {
R<Payment> result = new R<>();
TimeUnit.SECONDS.sleep(5);
return result.ok("from mysql serverPort: " + serverPort, hashMap.get(id));
}
}
payment方法延时5s,模拟复杂的业务场景
我的服务明明可以使用,为什么会进行服务降级呢?
这是因为openfegin底层使用了ribbon,服务调用是有默认时间的,超过这个时间就会调用失败
修改消费者的配置文件
#激活sentinel对Feign的支持
feign:
sentinel:
enabled: true
client:
config:
default:
# 设置feign客户端超时时间(OpenFeign默认支持Ribbon)
#简历连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
ConnectTimeout: 5000
#指建立连接后从服务端读取到可用资源所用的时间
ReadTimeout: 10000
openfegin一直服务降级
当我在提供者打上断点,然后通过访问消费者84,调用提供者,发现消费端,会等待10s后进行服务降级,这说明上面的配置生效了,但是当我取消断点后,访问消费者,立刻给我进行了服务降级,然而,提供者是正常被访问的
这到底是什么原因呢?消费者正常调用提供者,消费端openfegin还给我进行服务降级
提供者代码
@GetMapping("/paymentSQL/{id}")
public R<Payment> payment(@PathVariable(value = "id") String id) {
R<Payment> result = new R<>();
log.info("payment服务被调用");
// try {
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
log.info("payment服务被调用完成...");
return result.ok("from mysql serverPort: " + serverPort, hashMap.get(id));
}
后来我把 fallback去掉了,这样报错会直接在页面上打印出来
@FeignClient(value = "nacos-payment-provider")
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public R<Payment> paymentSQL(@PathVariable("id") String id);
}
Cannot construct instance of
com.sgy.springcloud.entities.Payment
(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (PushbackInputStream); line: 1, column: 145] (through reference chain: com.sgy.springcloud.entities.R[“data”])
翻译成汉语
无法构造com.sgy.springcloud.entities.Payment的实例(不存在创建者,如默认构造一样):无法从对象值反序列化(不存在基于委托或基于属性的创建者) 在[来源:(PushbackInputStream);行:1,列:145](通过参考链:com.sgy.springcloud.entities.R [“ data”])
大概意识就是无法创建Payment对象,我们都知道是通过构造方法进行创建的,而我的Payment对象中,只有一个有参构造方法,它需要一个无参构造方法进行构建Payment对象,因此会报错