芝法酱躺平攻略(15)SpringCloud下微服务间通信——resttemplate,openfeign,resilience4j

一、前言

提到微服务,就不得不说服务间的通信。
服务间的通信常见有4种方式。最基础的是restTemplate,是一种同步的服务间调用;其次是webclient,这是一个异步服务间调用的库;成框架的库,常用的有openfeign和dubbo。
本期介绍这几种方式的简单使用,并对比他们的优劣。

二、restTemplate

restTemplate事Spring框架自带的一种同步调用的库,在早年十分流行。

2.1 pom引用

在芝法酱躺平攻略(1)的framework-common,和framework-web-common我们其实已经加入了相关引用,这里展示相关的引用

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2-extension-spring5</artifactId>
        </dependency>
         <!-- ******************httpclient****************************-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

2.2 RestTemplateConfig

为了不在每次使用时都写一遍初始化,我们可以创建一个RestTemplate的bean,注入到service中使用。
但由于这种方式并不是现在流行的方式,故此处只做简单的配置

@Configuration
public class RestTemplateConfig {

    @Bean(name = "appRestClient")
    public RestTemplate getRestClient(FastJsonHttpMessageConverter pFastJsonHttpMessageConverter) {

        RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder()
                .messageConverters(pFastJsonHttpMessageConverter)
                .setConnectTimeout(Duration.ofSeconds(2))
                .setReadTimeout(Duration.ofSeconds(5));
        return restTemplateBuilder.build();
    }

}

注意FastJsonHttpMessageConverter pFastJsonHttpMessageConverter在芝法酱躺平攻略(1)中配置过相关的bean。

2.3 使用

建一个模块test,建一个模块tools。在tools中添加一个MathApi的controller,在test模块添加一个TestApi的controller
MathApi

@Api(tags = "test-测试接口")
@RequestMapping("/api/math")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class MathApi {

    BigInteger getFibonacii(int n){
        BigInteger n1 = new BigInteger("1");
        BigInteger n2 = new BigInteger("1");
        if(n==1){
            return n1;
        }else if(n==2){
            return n2;
        }else {
            BigInteger res = new BigInteger("1");
            for(int i=3;i<=n;i++){
                res = n1.add(n2);
                n1 = n2;
                n2 = res;
            }
            return res;
        }
    }

    @Operation(summary = "斐波那契数列")
    @GetMapping("/fibonacii/{n}")
    BigInteger fibonacii(@PathVariable(name = "n") int n){
        return getFibonacii(n);
    }

    @Operation(summary = "a+b")
    @GetMapping("/a_plus_b")
    Double aPlusB(@RequestParam(name = "a") double a,
                  @RequestParam(name = "b") double b){
        return a+b;
    }
}

TestApi

@Api(tags = "test-测试接口")
@RequestMapping("/api/test")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class TestApi {

    private final AppProperty mAppProperty;
    private final RestTemplate mRestTemplate;

    @Operation(summary = "hello测试")
    @GetMapping("/hello")
    public String sayHello(){
        return mAppProperty.getMyName();
    }

    @Operation(summary = "斐波那契数列测试")
    @GetMapping("/fibonacii/{n}")
    RestResponse<BigInteger> fibonacii(@Parameter(description = "n") @PathVariable(name = "n") int n){
        RestResponse<BigInteger> rtn = mRestTemplate.getForObject("http://localhost:8010/api/math/fibonacii/{n}",RestResponse.class,n);
        return rtn;
    }

    @Operation(summary = "a+b测试")
    @GetMapping("/a_plus_b")
    RestResponse<Double> aPlusB(@Parameter(description = "a") @RequestParam(name = "a") double a,
                                @Parameter(description = "b") @RequestParam(name = "b") double b){
        RestResponse<Double> rtn = mRestTemplate.getForObject("http://localhost:8010/api/math/a_plus_b?a={a}&b={b}",RestResponse.class,a,b);
        return rtn;
    }
}

三、 OpenFeign

openFeign是微服务框架下一个非常常用的库,可以和nacos结合,通过服务名调用对应接口,并附带负载均衡熔断降级的功能。

3.1 pom引用

        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

3.2 openfeign接口

package indi.zhifa.recipe.bailan5.busy.feign;

@FeignClient(value = "tools",fallback = MathFeignHystrix.class)
public interface IMathFeignCaller {
    @GetMapping("api/math/fibonacii/{n}")
    RestResponse<BigInteger> fibonacii(@PathVariable(name = "n") int n);

    @GetMapping("api/math/a_plus_b")
    RestResponse<Double> aPlusB(@RequestParam(name = "a") double a, @RequestParam(name = "b") double b);

    @GetMapping("api/math/timeoutTest/{t}")
    RestResponse<String> timeoutTest(@PathVariable(name = "t") int t);
}
@Component
public class MathFeignHystrix implements IMathFeignCaller{

    @Override
    public RestResponse<BigInteger> fibonacii(int n) {
        return RestResponse.error("服务器正忙");
    }

    @Override
    public RestResponse<Double> aPlusB(double a, double b) {
        return RestResponse.error("服务器正忙");
    }

    @Override
    public RestResponse<String> timeoutTest(int t) {
        return RestResponse.error("服务器正忙");
    }
}

3.3 TestApi

@Api(tags = "test-测试接口")
@RequestMapping("/api/test")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class TestApi {

    private final AppProperty mAppProperty;
    private final IMathFeignCaller mIMathFeignCaller;

    @Operation(summary = "hello测试")
    @GetMapping("/hello")
    public String sayHello(){
        return mAppProperty.getMyName();
    }

    @Operation(summary = "斐波那契数列测试")
    @GetMapping("/fibonacii/{n}")
    RestResponse<BigInteger> fibonacii(@Parameter(description = "n") @PathVariable(name = "n") int n){
        RestResponse<BigInteger> rtn = mIMathFeignCaller.fibonacii(n);
        return rtn;
    }

    @Operation(summary = "a+b测试")
    @GetMapping("/a_plus_b")
    RestResponse<Double> aPlusB(@Parameter(description = "a") @RequestParam(name = "a") double a,
                                @Parameter(description = "b") @RequestParam(name = "b") double b){
        RestResponse<Double> rtn = mIMathFeignCaller.aPlusB(a,b);
        return rtn;
    }

    @Operation(summary = "超时测试")
    @GetMapping("/timeOutTest/{t}")
    RestResponse<String> timeOutTest(@Parameter(description = "t") @PathVariable(name = "t") int t){
        RestResponse<String> rtn = mIMathFeignCaller.timeoutTest(t);
        return rtn;
    }

}

3.4 MathApi

@Api(tags = "test-测试接口")
@RequestMapping("/api/math")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class MathApi {

    BigInteger getFibonacii(int n){
        BigInteger n1 = new BigInteger("1");
        BigInteger n2 = new BigInteger("1");
        if(n==1){
            return n1;
        }else if(n==2){
            return n2;
        }else {
            BigInteger res = new BigInteger("1");
            for(int i=3;i<=n;i++){
                res = n1.add(n2);
                n1 = n2;
                n2 = res;
            }
            return res;
        }
    }

    @Operation(summary = "斐波那契数列")
    @GetMapping("/fibonacii/{n}")
    BigInteger fibonacii(@PathVariable(name = "n") int n){
        return getFibonacii(n);
    }

    @Operation(summary = "a+b")
    @GetMapping("/a_plus_b")
    Double aPlusB(@RequestParam(name = "a") double a,
                  @RequestParam(name = "b") double b){
        return a+b;
    }

    @Operation(summary = "超时测试")
    @GetMapping("/timeout/{t}")
    RestResponse<String> timeoutTest(@PathVariable(name = "t") int t) throws InterruptedException {
        Thread.sleep(t);
        return RestResponse.ok("睡了"+t/1000.0+"s,我睡醒了");
    }

}

四、 熔断降级-resilience4j

4.1 熔断降级的需求

在⾼并发访问下,⽐如天猫双11,流量持续不断的涌⼊,服务之间的相互调⽤频率突然增加,引发系统负载过⾼,这时系统所依赖的服务的稳定性对系统的影响⾮常⼤,⽽且还有很多不确定因素引起雪崩,如⽹络连接中断,服务宕机等。⼀般微服务容错组件提供了限流、隔离、降级、熔断等⼿段,可以有效保护我们的微服务系统。
相关内容可以参考这篇博客

4.2 熔断器配置

配置属性默认值描述
failureRateThreshold50以百分⽐配置失败率阈值。当失败率等于或⼤于阈值时,断路器状态并关闭变为开启,并进⾏服务降级。
slowCallRateThreshold100以百分⽐的⽅式配置,断路器把调⽤时间⼤于slowCallDurationThreshold的调⽤视为慢调⽤,当慢调⽤⽐例⼤于等于阈值时,断路器开启,并进⾏服务降级。
slowCallDurationThreshold60000[ms]配置调⽤时间的阈值,⾼于该阈值的呼叫视为慢调⽤,并增加慢调⽤⽐例。
permittedNumberOfCallsInHalfOpenState10断路器在半开状态下允许通过的调⽤次数。
maxWaitDurationInHalfOpenState0断路器在半开状态下的最⻓等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会⼀直处于半开状态,直到所有允许通过的访问结束。
slidingWindowTypeCOUNT_BASED配置滑动窗⼝的类型,当断路器关闭时,将调⽤的结果记录在滑动窗⼝中。滑动窗⼝的类型可以是count-based或time-based。如果滑动窗⼝类型是COUNT_BASED,将会统计记录最近slidingWindowSize次调⽤的结果。如果是TIME_BASED,将会统计记录最近 slidingWindowSize秒的调⽤结果。
slidingWindowSize100配置滑动窗⼝的⼤⼩。
minimumNumberOfCalls100断路器计算失败率或慢调⽤率之前所需的最⼩调⽤数(每个滑动窗⼝周期)。例如,如果minimumNumberOfCalls为10,则必须⾄少记录10个调⽤,然后才能计算13失败率。如果只记录了9次调⽤,即使所有9次调⽤都失败,断路器也不会开启。
waitDurationInOpenState60000 [ms]断路器从开启过渡到半开应等待的时间。
automaticTransition FromOpenToHalfOpenEnabledfalse如果设置为true,则意味着断路器将⾃动从开启状态过渡到半开状态,并且不需要调⽤来触发转换。创建⼀个线程来监视断路器的所有实例,以便在WaitDurationInOpenstate之后将它们转换为半开状态。但是,如果设置为false,则只有在发出调⽤时才会转换到半开,即使 在waitDurationInOpenState之后也是如此。这⾥的优点是没有线程监视所有断路器的状态。
recordExceptionsempty记录为失败并因此增加失败率的异常列表。除⾮通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。 如果指定异常列表,则所有其他异常均视为成功,除⾮它们被ignoreExceptions显式忽略。
ignoreExceptionsempty被忽略且既不算失败也不算成功的异常列表。任何与列表之⼀匹配或继承的异常都不会被视为失败或成功,即使异常是 recordExceptions的⼀部分。
recordExceptionthrowable -> trueBy default all exceptions are recored as failures. ⼀个⾃定义断⾔,⽤于评估异常是否应记录为失败。 如果异常应计为失败,则断⾔必须返回true。如果出断⾔返回false,应算作成功,除⾮ignoreExceptions显式忽略异常。
ignoreExceptionthrowable -> falseBy default no exceptions is ignored. ⾃定义断⾔来判断⼀个异常是否应该被忽略异常,则谓词必须返回 true。如果异常应算作失败,则断⾔必须返回 false

4.3 yml配置

为了测试断路器,把tools的阈值设低些

spring:
  datasource:
	......
  redis:
    ......
  cloud:
    circuitbreaker:
      resilience4j:
        enabled: true
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 10000
resilience4j:
  circuitbreaker:
    configs:
      default:
        #以百分⽐配置失败率阈值。当失败率等于或⼤于阈值时,断路器状态并关闭变为开启,并进⾏服务降级。
        failureRateThreshold: 50
        #以百分⽐的⽅式配置,断路器把调⽤时间⼤于slowCallDurationThreshold的调⽤视为慢调⽤,当慢调⽤⽐例⼤于等于阈值时,断路器开启,并进⾏服务降级。
        slowCallRateThreshold: 90
        #配置调⽤时间的阈值,⾼于该阈值的呼叫视为慢调⽤,并增加慢调⽤⽐例。
        slowCallDurationThreshold: 100000
        #断路器在半开状态下允许通过的调⽤次数。
        permittedNumberOfCallsInHalfOpenState: 5
        #断路器在半开状态下的最⻓等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会⼀直处于半开状态,直到所有允许通过的访问结束。
        maxWaitDurationInHalfOpenState: 600000
        #配置滑动窗⼝的类型,当断路器关闭时,将调⽤的结果记录在滑动窗⼝中。滑动窗⼝的类型可以是count-based或time-based。如果滑动窗⼝类型是COUNT_BASED,将会统计记录最近slidingWindowSize次调⽤的结果。如果是TIME_BASED,将会统计记录最近 slidingWindowSize秒的调⽤结果。
        slidingWindowType: COUNT_BASED
        #配置滑动窗⼝的⼤⼩。
        slidingWindowSize: 100
        #断路器计算失败率或慢调⽤率之前所需的最⼩调⽤数(每个滑动窗⼝周期)。例如,如果minimumNumberOfCalls为10,则必须⾄少记录10个调⽤,然后才能计算失败率。如果只记录了9次调⽤,即使所有9次调⽤都失败,断路器也不会开启。
        minimumNumberOfCalls: 5
        #断路器从开启过渡到半开应等待的时间。
        waitDurationInOpenState: 60000
        #不开启监视线程
        automaticTransitionFromOpenToHalfOpenEnabled: false
    instances:
      tools:
        registerHealthIndicator: true
        baseConfig: default
        minimumNumberOfCalls: 3
        slowCallDurationThreshold: 2000
        permittedNumberOfCallsInHalfOpenState: 1
  #超时配置
  timelimiter:
    configs:
      default:
        #超时时间
        timeoutDuration: 10000
    instances:
      tools:
        timeoutDuration: 2s
swagger:
  ......

enum-memo:
  enum-packages:
    - indi.zhifa.recipe.bailan5.busy.entity.enums

mybatis-plus:
  type-enums-package: indi.zhifa.recipe.bailan.**.entity.enums

redis:
  cache-expire: 120


4.4 pom引用

由于之前已经加入了spring-cloud-gatway的版本管理spring-cloud-dependencies,不需要再写版本号
加入如下依赖即可

        <!--熔断降级-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

4.4 修改IMathFeignCaller

resilience4j与feign配合的方式,是在feign接口上另加注解

@FeignClient(value = "tools",fallback = MathFeignHystrix.class)
@CircuitBreaker(name = "tools")
public interface IMathFeignCaller {
    @GetMapping("api/math/fibonacii/{n}")
    RestResponse<BigInteger> fibonacii(@PathVariable(name = "n") int n);

    @GetMapping("api/math/a_plus_b")
    RestResponse<Double> aPlusB(@RequestParam(name = "a") double a, @RequestParam(name = "b") double b);

    @GetMapping("api/math/timeout/{t}")
    RestResponse<String> timeoutTest(@PathVariable(name = "t") int t);
}

4.5 修改异常处理

    @ResponseBody
    @ExceptionHandler(CallNotPermittedException.class)
    public RestResponse callNotPermittedExceptionHandle(Exception e) {
        log.info("触发断路器", e);
        return RestResponse.error("触发断路器 "+e.toString());
    }

4.6 测试

使用浏览器访问http://localhost:8001/api/test/timeout/3000,然后多刷新几次,可以看到如下信息
在这里插入图片描述

五、关于dubbo

从官网上看,Spring Cloud Dubbo 从 2021.0.1.0 起已被移除出主干,不再随主干演进,故暂时不再研究该技术。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值