zoukankan      html  css  js  c++  java
  • dubbo rpc filter实现剖析(一)

    2.6.3版本,之前读的是2.4.9版本
    本篇主要阐述dubbo rpc的filter的实现,包括作用,用法,原理,与Spring Cloud在这些能力的对比。

    共提供了多少个?是哪些?发布时默认装配了哪些给他自身的扩展点机制?

    从类与接口关系分析的结果文档中可以看到共20个:
    241 Filter
    --241.1 CacheFilter
    --241.2 MonitorFilter
    --241.3 AccessLogFilter
    --241.4 ActiveLimitFilter
    --241.5 ClassLoaderFilter
    --241.6 CompatibleFilter
    --241.7 ConsumerContextFilter
    --241.8 ContextFilter
    --241.9 DeprecatedFilter
    --241.10 EchoFilter
    --241.11 ExceptionFilter
    --241.12 ExecuteLimitFilter
    --241.13 GenericFilter
    --241.14 GenericImplFilter
    --241.15 TimeoutFilter
    --241.16 TokenFilter
    --241.17 TpsLimitFilter
    --241.18 FutureFilter
    --241.19 TraceFilter
    --241.20 ValidationFilter

    从发布的jar中的META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter中发现除了TpsLimitFilter之外,其余的都装上了。
    cache=com.alibaba.dubbo.cache.filter.CacheFilter
    validation=com.alibaba.dubbo.validation.filter.ValidationFilter
    echo=com.alibaba.dubbo.rpc.filter.EchoFilter
    generic=com.alibaba.dubbo.rpc.filter.GenericFilter
    genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
    token=com.alibaba.dubbo.rpc.filter.TokenFilter
    accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
    activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
    classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
    context=com.alibaba.dubbo.rpc.filter.ContextFilter
    consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
    exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
    executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
    deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
    compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
    timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
    trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
    future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
    monitor=com.alibaba.dubbo.monitor.support.MonitorFilter

    这些filter都有什么作用?如何使用?实现原理是什么?Spring Cloud是否也提供了这些能力?有什么差异?

    CacheFilter

    作用

    缓存调用结果,比如配置在consumer端, 比如我们通过id查某个用户的信息,对于特定的一个id,在consumer端第一次调用时会给provider端发请求,后面再调用时,直接用consumer端缓存的结果返回,你不再发请求给provider端。

    使用方式

    consumer侧的配置:

    <dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" filter="cache">
    <dubbo:parameter key="cache" value="lru" />
    </dubbo:reference>

    能支持的全部cache定义在META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory 当然你也可以遵循dubbo扩展点机制进行扩展。
    默认提供三种:
    threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
    lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory
    jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory

    实现原理

    缓存的key是你远程方法调用时传递的所有参数按规则组装成字符串作为key。
    具体规则就是: 基本类型 直接拼接,复合类型转成json字符串后再拼接。

    实现原理也不是很复杂,根据invoker的url找到其对应的cache对象,再跟据上述缓存的key找到缓存的结果。
    有个不是太要紧的小问题,因为是根据invoker的url找到其对应的cache对象的,又因为invoker的url中含有remote.timestamp参数,所以你如果启用了consumer侧的缓存,consumer一直在服务状态,此时provider服务做了重启,那么consumer侧的缓存失效,会重新调用provider端。

    ThreadLocalCache 是ThreadLocal配合HashMap实现
    LRUCache 是继承自LinkedHashMap,同时结合ReentrantLock实现的线程安全的lru cache(最近最少使用),LinkedHashMap自带lru性质,通过构造参数控制,默认是fifo。
    jache是封装的JCache API (JSR 107)。

    与Spring Cloud对比

    Spring Cloud提供了consumer侧的缓存能力,Hystrix组件支持用requestCache.enabled配置是否启用缓存,也支持用cacheKeyMethod注解指定getkey方法。

    ValidationFilter

    作用

    在consumer和provider端提供了校验能力

    使用方式

    假设你要对consumer端进行校验,在配置文件中配置如下:

    <dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" filter="validation">
        <dubbo:parameter key="validation" value="JValidator" />
        
        <!-- 配置一个实现了javax.validation.spi.ValidationProvider<T>接口校验器 -->
        <dubbo:parameter key="jvalidation" value="org.hibernate.validator.HibernateValidator" />
    </dubbo:reference>
    

    filter要是配置多个的话,用逗号拼接,但是逗号前后不能有空格。
    此处使用了hibernate的validator ,在你需要校验的接口方法上加校验注解即可,示例如下:

    public User queryUser(@Range(min=0,message="用户id值不能小于0")int id);
    

    当consumer端调用时传递了校验不通过的参数时,会收到ConstraintViolationException的异常。

    实现原理

    dubbo对接了javax.validation.Validation,hibernate等都有对其对接的实现,按需使用即可。也就是说dubbo自己不做具体校验的事情。

    与Spring Cloud对比

    spring在很早就支持validator。

    EchoFilter

    作用

    在provider端提供回声服务的服务端的实现。

    使用方式

    这个filter略有特殊,无需在provider端的dubbo:service标签的filter中去配置,只要你在consumer做了echo回声调用,他都会产生作用,调试的时候也能看到能走到EchoFilter中。

    consumer端的示例代码:

    public class HelloClientTest
    {
        @Autowired
        private HelloService helloService;
    
        @SuppressWarnings("static-access")
        @Test
        public void testSayHello()
        {
            System.out.println(((EchoService)helloService).$echo("aaaa"));
        }
    }
    

    就是把你的service类强转成EchoService,至于为什么能强转,可以参见之前写的文章 reference bean发起调用

    实现原理

    直接看EchoFilter代码,很简单,不再多说。

    与Spring Cloud对比

    Spring Cloud貌似没有这个能力。

    GenericFilter

    作用

    先讨论一个问题,rpc,最简单的场景是consumer端调用provider端的一个服务,这个服务双方都遵循一个接口实现,按最简单的dubbo的demo玩法,是需要consumer和provider两段都要有这个接口声明的(包括接口参数的类型的相关类),比如:
    org.simonme.dubbo.demo.provider.service.UserService.queryUser(int) 这是一个查询用户的服务接口,但是,如果consumer端没有这个服务的接口声明及其相关联的bean类,也就是如果仅仅在provider端能找到这个接口类,在consumer工程里压根没有这个类,那是否还能进行调用?
    dubbo是可以的。 就是通过在proivder侧用GenericFilter达成目的。

    使用方式

    需要在consumer端声明这个bean的时候,加上generic="true"配置即可。

    <dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" generic="true">
    </dubbo:reference>
    

    generic还支持nativejava和bean两个可选的选项。nativejava对应byte[]类型的参数,bean对应com.alibaba.dubbo.common.beanutil.JavaBeanDescriptor类型的参数。

    此外在使用这个服务时,形式略有不同:

        @Autowired
        private GenericService userService;
        
        @SuppressWarnings("static-access")
        @Test
        public void testSayHello()
        {
            Object result = userService.$invoke("queryUser", new String[] { "int" },  new Object[]{100});
            System.out.println(result);
            System.out.println(result.getClass());
        }
    

    GenericService 表示你这个未知的服务类型,用$invoke这个特殊方法发起的你的服务方法调用。调用完之后,dubbo会把结果以hashmap的形式返回给你。比如,我这里是个User的bean,User有id和name字段,那么返回的map中就与id和value两个key,其对应的值就是bean的这两个字段的字段值。
    为了便于理解,贴一下UserService的代码:

    public interface UserService
    {
        public User queryUser(@Range(min=0,message="用户id值不能小于0")int id);
    }
    

    上面这个写法,解释了怎么在consumer端没有org.simonme.dubbo.demo.provider.service.UserService接口声明的时候,调用他的queryUser方法。provider端无需改动,和正常写rpc服务一样配置即可。
    用法也可以参见官方文档,使用泛化调用

    实现原理

    直接看GenericFilter代码,通过方法名(必须是$invoke),参数个数等判断是否命中,很简单,不再多说。

    与Spring Cloud对比

    Spring Cloud可以算有这个能力,因为Spring Cloud是http json的,http json天然不需要调用端有接口声明。

    GenericImplFilter

    作用

    与 GenericFilter  类似,是一个相对的东西,他是用在provider端没有服务接口声明类时,使用的。使用方法可以参见官方文档实现泛化调用。不再多说。

    TokenFilter

    作用

    官方文档说法是:

    通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。

    使用方式

    provider端:在provider上配置token值。 TokenFilter会在provider侧校验。

    <dubbo:service interface="org.simonme.dubbo.demo.provider.service.UserService" ref="m00001.app001.xx.userService" timeout="600000" token="123456">
    

    consumer端可以用编程的方式获取后塞,

    RpcContext.getContext().setAttachment("token", "a37b6115-c171-43cd-b65c-38b636ee96cc");
    

    或者通过配置parameter,

    <dubbo:parameter key="token" value="123456" />
    

    或者啥都不要处理,默认consumer会从provider服务url中解析到。provider的url中会含有token字段。

    实现原理

    如果 token 配置的是true, 那么在provider export服务时,ServiceConfig会生成UUID,这个其实不是由注册中心生成的。
    当然token也支持配置固定密码。
    比对过程:

    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {// 是 true或者default字段串就是表示默认
            map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            map.put(Constants.TOKEN_KEY, token);
        }
    }
    

    consumer端如果没有通过上述代码的方式或者parameter配置的方式传送token,那么consumer端会在调用时,先将从注册中心拿到的provider端的url中部分参数转换成attachment给consumer端用,这个部分参数就包括token。具体代码在DubboInvoker中,如下:

    public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
        super(serviceType, url, new String[]{Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});// 此处会调用父类方法进行需要的参数从url转到attachment中
        this.clients = clients;
        // get version.
        this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
        this.invokers = invokers;
    }
    

    当provider端使用注册中心,consumer试图不带token进行直接消费时,会被拒绝。 当consumer端也是连注册中心时,哪怕不送显式送token(实际上dubbo会自动送)也可以正常调用。但是如果consumer端用了注册中心,且显式送了token,那么就要送对。否则报错。

    与Spring Cloud对比

    Spring Cloud的注册中心eureka也是支持密码验证的。

    AccessLogFilter

    作用

    用在provider端打印rpc请求日志,支持打到指定文件,支持异步。

    使用方式

    在provider侧配置名为accesslog的filter,若需要指定路径,则将accesslog参数设置成具体路径即可,默认需要将其配置成true。

    <dubbo:service interface="org.simonme.dubbo.demo.provider.service.UserService" ref="m00001.app001.xx.userService" filter="accesslog" 
        timeout="600000" token="123456">
        <dubbo:parameter key="accesslog" value="true" />
    
    </dubbo:service>
    

    实现原理

    参见AccessLogFilter 代码,比较简单。

    与Spring Cloud对比

    zuul网关也支持。
    zuul.debug.request=true #如果设置了这个,默认所有的请求都会debug
    zuul.include-debug-header: true #打印头
    未设置zuul.debug.request=true,可以用zuul_host:zuul_port/路径?debug=true debug你的指定请求

    ActiveLimitFilter

    作用

    在consumer端实现并发数控制,能支持到方法级。

    使用方式

    在consumer侧配置

    <dubbo:reference interface="com.foo.BarService">
        <dubbo:method name="sayHello" actives="10" />
    </dubbo:service>
    

    实现原理

    ConcurrentHashMap 配合 AtomicInteger AtomicLong完成。相关代码参见 ActiveLimitFilter 与 RpcStatus。

    与Spring Cloud对比

    Spring Cloud也支持。

    ExecuteLimitFilter 与之类似,只是用在了provider端。

    ClassLoaderFilter

    作用

    保持 调用下层invoker前后的ClassLoader一致

    ContextFilter

    作用

    在provider端,对一些dubbo自己使用的保留key进行过滤,防止别人误传。

    实现原理

    参见代码即可,比较简单。

    与Spring Cloud对比

    N/A

  • 相关阅读:
    wcf布到服务器下载文件大小限制问题
    动态修改母版页中的DIV标签中的LI的A的CLASS属性
    c# 分页的方法
    Java学习笔记-异常处理-有风险的行为
    ASP.NET Core中的静态文件
    在ASP.NET Core中进行配置
    ASP.NET Core中的中间件和请求管道
    Kestrel:ASP.NET Core的Web服务器
    ASP.NET Core Program.cs
    ASP.Net Core Startup类
  • 原文地址:https://www.cnblogs.com/simoncook/p/9589354.html
Copyright © 2011-2022 走看看