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对象,因此会报错

熔断框架比较


