sentinel整合ribbon+openFeign+fallback

启动nacos和sentinel

Ribbon系列

1610803441132.png

提供者9003/9004

新建cloudalibaba-provider-payment9003/9004

POM

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  5. </dependency>
  6. <!-- 引入自己定义的api通用包,可以使用通用返回结果类,以及支付Payment实体类 -->
  7. <dependency>
  8. <groupId>com.sgy.cloud2020</groupId>
  9. <artifactId>cloud-api-commons</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-web</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-test</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-devtools</artifactId>
  23. <scope>runtime</scope>
  24. <optional>false</optional>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-actuator</artifactId>
  29. </dependency>
  30. </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));
    }
}

测试地址

http://localhost:9003/paymentSQL/1

1610803441158.png

然后全部启动,当消费者使用ribbon进行调用的使用会进行轮询调用

消费者

新建cloudalibaba-consumer-nacos-order84

pom

1610803441191.png

<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

1610803441220.png

没有任何配置

给客户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);
    }

}

1610803441259.png

只配置fallback

编码(那个业务类下面的CircleBreakerController的全部源码)

1610803441289.png

只配置blockHandler

sentinel 控制太配置限流

1610803441321.png

1610803441351.png

快速连续访问,http://localhost:84/consumer/fallback/4

1610803441381.png

fallback和blockHandler都配置

若 blockHandler和 fallback都进行了配置,则被限流降级而抛出 BlockException时只会进入 blockHandler处理逻辑

1610803441417.png

快速访问

1610803441446.png

忽略属性…

编码(那个业务类下面的CircleBreakerController的全部源码)

1610803441487.png

浏览器访问 http://localhost:84/consumer/fallback/5

1610803441576.png

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消费侧自动降级,不会被耗死

1610803441604.png

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,模拟复杂的业务场景

访问消费者:http://localhost:84/consumer/paymentSQL/4

1610803441633.png

我的服务明明可以使用,为什么会进行服务降级呢?

这是因为openfegin底层使用了ribbon,服务调用是有默认时间的,超过这个时间就会调用失败

修改消费者的配置文件

#激活sentinel对Feign的支持
feign:
  sentinel:
    enabled: true
  client:
    config:
      default:
        # 设置feign客户端超时时间(OpenFeign默认支持Ribbon)
        #简历连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
        ConnectTimeout: 5000
        #指建立连接后从服务端读取到可用资源所用的时间
        ReadTimeout: 10000

openfegin一直服务降级

当我在提供者打上断点,然后通过访问消费者84,调用提供者,发现消费端,会等待10s后进行服务降级,这说明上面的配置生效了,但是当我取消断点后,访问消费者,立刻给我进行了服务降级,然而,提供者是正常被访问的

这到底是什么原因呢?消费者正常调用提供者,消费端openfegin还给我进行服务降级

1610803441664.png

1610803441687.png

提供者代码

@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);
}

1610803441723.png

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

1610803441763.png

熔断框架比较

1610803441812.png

1610803441850.png