5.Spring Cloud Ailibaba Sentinel支持OpenFeign、RestTemplate(远程调用)及动态数据源

目录


Spring Cloud Alibaba专栏目录(点击进入…)



OpenFeign(远程调用)

Sentinel默认适配了OpenFeign组件

1.导入openfeign starter依赖

加入openfeign starter依赖,使sentinel starter中的自动化配置类生效

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

2.配置application.yml(打开sentinel对feign的支持)

feign:
  sentinel:
    enabled: true

3.接口中使用@FeignClient远程调用

@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
    @GetMapping(value = "/echo/{str}")
    String echo(@PathVariable("str") String str);
}

class EchoServiceFallback implements EchoService {
    @Override
    public String echo(@PathVariable("str") String str) {
        return "echo fallback";
    }
}

class FeignConfiguration {
    @Bean
    public EchoServiceFallback echoServiceFallback() {
        return new EchoServiceFallback();
    }
}

Feign对应的接口中的资源名策略定义:httpmethod:protocol://requesturl。@FeignClient注解中的所有属性,Sentinel都做了兼容

EchoService接口中方法echo对应的资源名为GET:http://service-provider/echo/{str}


@FeignClient注解

@FeignClient用于创建声明是API接口,该接口是RESTful风格的。Feign被设计成插拔式的,可注入其他组件和Feign一起使用。最典型的是如果Ribbon可用,Feign会和Ribbon相结合进行负载均衡

简单理解:分布式架构服务之间,各子模块系统内部通信的核心。一般在一个系统调用另一个系统的接口时使用
在这里插入图片描述

FeignClient注解被三个元注解修饰

元注解描述
@Target(ElementType.TYPE)表示FeignClient的作用目标在接口上
@Retention(RetentionPolicy.RUNTIME)注解表明该注解会在Class字节码中存在,在运行时可以通过反射获取到
@Documented表明该注解被包含在javadoc中

(1)name、value和serviceId(微服务名称)

从源码可以得知,name是value的别名,value也是name的别名。两者的作用是一致的,name指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。
其中,serviceId和value的作用一样,用于指定服务ID,已经废弃。

实例:通过name或者value指定服务名,然后根据服务名类型(GET、DLETE)调用/one服务

@Component
//@FeignClient(value = "service-test")
@FeignClient(name = "service-test")
public interface UserFeignClient {
    @RequestMapping(value = "/app/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);

    @RequestMapping(value = "/app/one", method = RequestMethod.DELETE)
    public String deleteId(@PathVariable("id") Long id);
}

(2)path(服务前缀)

path属性定义当前FeignClient的统一前缀。方便在该FeignClient中的@RequestMapping中书写value值

假如存在一系列的用户管理服务,如下:

/app/service/userManager/get
/app/service/userManager/insert
/app/service/userManager/update
/app/service/userManager/delete

每次都在@RequestMapping注解中编写全服务名称,就有点多余。因此可以设置FeignClient的path路径为“/app/service/userManager”,简化@RequestMapping的编写

@Component
@FeignClient(value = "service-test",path = "/app/service/userManager")
public interface UserFeignClient {
    //正确服务地址:/app/service/one
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);
}

当调用findById()方法时将请求http://service-test/app/service/userManager/one服务


(3)qualifier(修改服务名称,然后引用)

用来指定@Qualifier注解的值,该值是该FeignClient的限定词,可以使用改值进行引用

@Component
//@FeignClient(value = "service-test")
@FeignClient(qualifier = "myService",value = "service-test")
public interface UserFeignClient {
    @RequestMapping(value = "/app/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);

    @RequestMapping(value = "/app/one", method = RequestMethod.DELETE)
    public String deleteId(@PathVariable("id") Long id);
}

在使用“myService”标识注入服务

@RestController
public class IndexController {
    @Autowired
    @Qualifier("myService")
    private UserFeignClient userFeignClient;
}

(4)url(指定服务地址)

url属性一般用于调试程序,允许手动指定@FeignClient调用的地址

指定服务调用的地址为http://localhost:8762

@Component
//@FeignClient(value = "service-test")
@FeignClient(value = "service-test",path = "/app/service",url = "http://localhost:8080")
public interface UserFeignClient {
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);

    @RequestMapping(value = "/one", method = RequestMethod.DELETE)
    public String deleteId(@PathVariable("id") Long id);
}

(5)decode404

当发生http 404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException

实例:访问在服务“service-hi”上面没有的服务/one(服务实际地址为:/app/service/one)

@Component
//@FeignClient(value = "service-test")
@FeignClient(value = "service-test")
public interface UserFeignClient {
    //正确服务地址:/app/service/one
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);
}

调用上面的sayHiFormClientOne()方法时,Spring Boot将给出如下错误信息:
在这里插入图片描述

为了在调用服务抛出404错误时,返回一些有用的信息。可以将decode404参数设置为true

@Component
//@FeignClient(value = "service-test")
@FeignClient(value = "service-test",decode404 = true)
public interface UserFeignClient {
    //正确服务地址:/app/service/one
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);
}

此时调用sayHiFromClientOne()方法时,返回如下错误信息:
在这里插入图片描述
设置decode404=true,需要通过设置configuration去配置decode


(6)Configuration(配置类)

Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract。

实例:自定义configuration配置类,简单自定义一个Decoder,该Decoder配合decod404=true使用;当服务调用抛出404错误时,将自动调用自定义的Decoder,输出一个简单的字符串

(1)定义一个Controller,提供给一个REST服务/one

@RestController
public class IndexController {
    @Autowired
    private UserFeignClient userFeignClient;

    @RequestMapping("/one/data")
    public String getIndex(@RequestParam Long id){
        return userFeignClient.findById(id);
    }
}

(2)编写FeignClient类UserFeginClient

@Component
//@FeignClient(value = "service-test")
@FeignClient(value = "service-test",decode404 = true)
public interface UserFeignClient {
    //正确服务地址:/app/service/one
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public String findById(@PathVariable("id") Long id);
}

(3)编写MyConfiguration配置类

@Configuration
public class MyDecoderConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Decoder feignDecoder() {
        return new MyDecoder();
    }
}

(4)编写自定义的Decoder类MyDecoder

public class MyDecoder implements Decoder {
    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
        return "响应内容response:"+response +",type ="+type;
    }
}

当调用服务/one/data发生404时,输出如下信息
在这里插入图片描述


(7)fallback和fallbackFactory(服务熔断降级)

feign的注解@FeignClient:fallbackFactory与fallback方法不能同时使用,这个两个方法其实都类似于Hystrix的功能,当网络不通时返回默认的配置数据

参数描述
fallback定义容错的处理类。当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
fallbackFactory工厂类,用于生成fallback类示例,通过这个属性可以实现每个接口通用的容错逻辑,减少重复的代码

熔断只是作用在服务调用这一端,在Feign中已经依赖了Hystrix所以在maven配置上不用做任何改动

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SpringcloudconsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudconsumerApplication.class, args);
    }
}

application.properties添加这一条:

feign.hystrix.enabled=true
使用fallback

(1)访问服务接口
写一个访问“server-test”服务的接口,同时在@FeignClient注解中使用fallback默认返回方法(断容器,fallback = ClientFallback.class)

@FeignClient(name="server-test", fallback = ClientFallback.class)
public interface UserFeignClient {
    // 两个坑:1. @GetMapping不支持   2. @PathVariable得设置value
    @RequestMapping(value="/simple/{id}", method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id);
}

(2)定义一个类,实现@FeignClient注解的接口
定义HystrixClientFallback类,并实现UserFeignClient类,当网络不通或者访问失败时,返回固定/默认内容

@Component
public class ClientFallback implements UserFeignClient{
    @Override
    public User findById(Long id) {
        User user = new User();
        user.setId(0L);
        return user;
    }
}

(3)Controller中测试服务接口
调用“server-test”服务的接口

@RestController
public class IndexController {
    @Autowired
    private UserFeignClient userFeignClient;

    @GetMapping("/movie/{id}")
    public User findById(@PathVariable("id") Long id) {
        return this.userFeignClient.findById(id);
    }
}
使用fallbackFactory

(1)服务接口
写feignClient客户端,使用feignClient注解的fallbackFactory方法

@FeignClient(name="server-test", fallbackFactory = ClientFallbackFactory.class)
public interface UserFeignClient {
    // 两个坑:1. @GetMapping不支持   2. @PathVariable得设置value
    @RequestMapping(value="/simple/{id}", method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id);
}

(2)定义一个类继承服务接口
定义一个类继承UserFeignClient接口(可以省略,使用匿名内部类new UserFeignClient())

public interface HystrixClientWithFallbackFactory
extends UserFeignClient {}

(3)定义一个类实现FallbackFactory<服务接口>类,并实现create()方法
HystrixClientFallbackFactory实现FallbackFactory类,并使用内部匿名方法类,继续UserFeignClient

@Component
public class HystrixClientFallbackFactory implements FallbackFactory<UserFeignClient> {
    @Override
    public UserFeignClient create(Throwable throwable) {
//这步可以换成直接new接口:new UserFeignClient()
          return new HystrixClientWithFallbackFactory () { 
            @Override
            public User findById(Long id) {
                return null;
            }

        };
    }
}

(4)Controller、Service中调用UserFeignClient接口

@RestController
public class MovieController {
    @Autowired
    private UserFeignClient userFeignClient;

    @GetMapping("/movie/{id}")
    public User findById(@PathVariable("id") Long id) {
        return this.userFeignClient.findById(id);
    }
}

(5)调用结果
当开启“server-test”服务,返回数据
在这里插入图片描述

当关闭“server-test”服务,返回数据
在这里插入图片描述


(8)primary(伪代理)

是否将伪代理标记为主Bean,默认为true


RestTemplate(远程调用)

Spring Cloud Alibaba Sentinel支持对RestTemplate的服务调用使用Sentinel进行保护,在构造 RestTemplate bean的时候需要加上@SentinelRestTemplate注解,然后正常注入RestTemplate使用即可。

注意:应用启动的时候会检查@SentinelRestTemplate注解对应的限流或降级方法是否存在,如不存在会抛出异常。

@Bean
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@SentinelRestTemplate注解中ExceptionUtil的handleException属性对应的方法声明如下:

public class ExceptionUtil {
    public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
        // ...
    }
}

@SentinelRestTemplate注解属性(属性不强制填写)

(1)限流(blockHandler,blockHandlerClass)
(2)降级(fallback,fallbackClass)

其中blockHandler或fallback属性对应的方法必须是对应blockHandlerClass或fallbackClass属性中的静态方法。该方法的参数跟返回值跟org.springframework.http.client.ClientHttpRequestInterceptor#interceptor方法一致,其中参数多出了一个BlockException参数用于获取Sentinel捕获的异常

当使用RestTemplate调用被Sentinel熔断后,会返回RestTemplate request block by sentinel信息,或者也可以编写对应的方法自行处理返回信息

SentinelClientHttpResponse提供了用于构造返回信息,Sentinel RestTemplate限流的资源规则提供两种粒度:
    httpmethod:schema://host:port/path:协议、主机、端口和路径
    httpmethod:schema://host:port:协议、主机和端口

例子:以https://www.taobao.com/test这个url并使用GET方法为例。对应的资源名有两种粒度,分别是GET:https://www.taobao.com以及GET:https://www.taobao.com/test


动态数据源

Sentinel Properties内部提供了TreeMap类型的datasource属性用于配置数据源信息

配置4个数据源:
配置方式参考了Spring Cloud Stream Binder的配置,内部使用了TreeMap进行存储,comparator为String.CASE_INSENSITIVE_ORDER

d1、ds2、ds3、ds4是ReadableDataSource的名字,可随意编写。后面的file、zk、nacos 、apollo就是对应具体的数据源。它们后面的配置就是这些具体数据源各自的配置

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
#spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
#spring.cloud.sentinel.datasource.ds1.file.data-type=custom
#spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter
#spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade

spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181
spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority

spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test
spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow

数据源配置项

每种数据源都有两个共同的配置项:data-type、converter-class以及rule-type

(1)data-type

表示Converter类型,Spring Cloud Alibaba Sentinel默认提供两种内置的值,分别是json和xml(默认json)
如果不想使用内置的json或xml这两种Converter,可以填写custom,表示自定义Converter,然后再配置converter-class配置项,该配置项需要写类的全路径名

比如:spring.cloud.sentinel.datasource.ds1.file.converter-class=
org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter


(2)rule-type

表示该数据源中的规则属于哪种类型的规则(flow、degrade、authority、system、param-flow、gw-flow、gw-api-group)

注意:当某个数据源规则信息加载失败的情况下,不会影响应用的启动,会在日志中打印出错误信息。默认情况下,xml格式是不支持的。需要添加jackson-dataformat-xml依赖后才会自动生效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值