美文网首页
Dubbo配置参数详解-stub

Dubbo配置参数详解-stub

作者: codeimport | 来源:发表于2020-01-07 15:51 被阅读0次

    Dubbo配置参数详解-stub

    Dubbo是一个由阿里开源的服务治理框架,笔者的公司重度使用Dubbo。Dubbo开源这么多年,配置项已经非常丰富,了解各个配置项的作用也变得非常重要,本系列将从源代码的角度分析Dubbo目前的最新版本(2.7.4)各个常用的配置项的具体含义以及是怎么起作用的。

    画外音:目前Dubbo在开源中国举办的2019年度最受欢迎中国开源软件中排名第3名,支持Dubbo的朋友可以去投票哇。2019年度最受欢迎中国开源软件

    stub是啥?

    stub:存根,rmi需要存根(stub)和骨架(skeleton),stub用于客户端,skeleton用于服务器端。简单来说,你调用provider,相关的服务方不是要提供给你一个jar包吗,那个jar包就是存根。
    画外音:使用过WebService的同学应该很清楚

    stub有啥用?

    一般的存根都是只有接口,不包含实现。但是dubbo提供的这个stub作用有点类似于拦截器,它可以在调用正在的provider之前或之后对请求及结果进行处理。

    stub怎么使用?

    存根主要有两种使用方式,一种是服务提供者写好,并随jar包一起提供给调用方,这种方式可以用来做前置验证,另外一种方式是调用方自己写,这可以用来对结果进行处理;

    如果是服务方提供,Stub一般都是定义在接口的同包目录下,并且使用InterfaceName+Stub的命名方式,这时调用方只要设置stub="true"就可以使用了。

    /**
     * @description:
     * @author: chengang6
     * @create: 2019/5/10 10:38
     **/
    public interface HelloDubboService {
        String say();
        String hello();
        String post(String key);
    }
    
    /**
     * @description: Stub必须实现相同的接口,且跟接口在同一个目录
     * @author: chengang6
     * @create: 2020/1/7 10:37
     **/
    public class HelloDubboServiceStub implements HelloDubboService {
    
        private HelloDubboService helloDubboService;
    
        //必须提供这种类似代理的构造函数,否则会报错
        public HelloDubboServiceStub(HelloDubboService helloDubboService) {
            this.helloDubboService = helloDubboService;
        }
    
        @Override
        public String say() {
            return ">>>>>>>>Stub>>>>say";
        }
    
        @Override
        public String hello() {
            // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
            try {
                return helloDubboService.hello();
            } catch (Exception e) {
                // 你可以容错,可以做任何AOP拦截事项
                return "容错数据";
            }
        }
    
        @Override
        public String post(String key) {
            return ">>>>>>>>Stub>>>>post";
        }
    }
    
        //只要配置stub=true就可以使用存根了
        @Reference(stub = "true")
        private HelloDubboService helloDubboService;
    

    也可以在@Service中配置stub="true",这样就算Consumer端么有配置stub,也会强制Consumer端执行Stub

    @Service(stub = "true")
    public class HelloDubboServiceImpl implements HelloDubboService {
    

    Consumer端也可以自定义Stub,此时不需要遵循InterfaceName+Stub的命名方式,但需要把完整的类名写在参数中,比如

        @Reference(stub = "com.example.dubboconsumer.stub.MyStub")
        private HelloDubboService helloDubboService;
    

    stub的命名规则是什么样的?

    在Reference初始化的时候会判断是否设置了stub,如果设置了该参数会在AbstractInterfaceConfig类的checkStubAndLocal方法中判断该参数的有效性

        /**
         * Legitimacy check of stub, note that: the local will deprecated, and replace with <code>stub</code>
         *
         * @param interfaceClass for provider side, it is the {@link Class} of the service that will be exported; for consumer
         *                       side, it is the {@link Class} of the remote service interface
         */
        void checkStubAndLocal(Class<?> interfaceClass) {
            if (ConfigUtils.isNotEmpty(local)) {
                Class<?> localClass = ConfigUtils.isDefault(local) ?
                        ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
                verify(interfaceClass, localClass);
            }
            if (ConfigUtils.isNotEmpty(stub)) {
                Class<?> localClass = ConfigUtils.isDefault(stub) ?
                        ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);
                verify(interfaceClass, localClass);
            }
        }
    
        private void verify(Class<?> interfaceClass, Class<?> localClass) {
            if (!interfaceClass.isAssignableFrom(localClass)) {
                throw new IllegalStateException("The local implementation class " + localClass.getName() +
                        " not implement interface " + interfaceClass.getName());
            }
    
            try {
                //Check if the localClass a constructor with parameter who's type is interfaceClass
                ReflectUtils.findConstructor(localClass, interfaceClass);
            } catch (NoSuchMethodException e) {
                throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() +
                        "(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
            }
        }
    

    可以看到Dubbo除了提供Stub,还可以使用Local这种方式,但是这种方式Dubbo已经不推荐使用;
    如果stub="true",Dubbo将会在相同接口的包下寻找InterfaceName+Stub这个类,否则将会根据全限定名寻找;并且Stub中必须有一个将接口作为形参的构造函数,否则报错;

    stub是如何生效的?

    Dubbo在为reference生成代理类的时候,都会调用StubProxyFactoryWrapper判断该reference是否需要使用stub,如果是则直接返回stub

    public class StubProxyFactoryWrapper implements ProxyFactory {
            @SuppressWarnings({"unchecked", "rawtypes"})
        public <T> T getProxy(Invoker<T> invoker) throws RpcException {
            T proxy = proxyFactory.getProxy(invoker);
            if (GenericService.class != invoker.getInterface()) {
                URL url = invoker.getUrl();
                String stub = url.getParameter(STUB_KEY, url.getParameter(LOCAL_KEY));
                if (ConfigUtils.isNotEmpty(stub)) {
                    Class<?> serviceType = invoker.getInterface();
                    if (ConfigUtils.isDefault(stub)) {
                        if (url.hasParameter(STUB_KEY)) {
                            stub = serviceType.getName() + "Stub";
                        } else {
                            stub = serviceType.getName() + "Local";
                        }
                    }
                    try {
                        Class<?> stubClass = ReflectUtils.forName(stub);
                        if (!serviceType.isAssignableFrom(stubClass)) {
                            throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + serviceType.getName());
                        }
                        try {
                            Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
                            proxy = (T) constructor.newInstance(new Object[]{proxy});
                            //export stub service
                            URLBuilder urlBuilder = URLBuilder.from(url);
                            if (url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT)) {
                                urlBuilder.addParameter(STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
                                urlBuilder.addParameter(IS_SERVER_KEY, Boolean.FALSE.toString());
                                try {
                                    export(proxy, (Class) invoker.getInterface(), urlBuilder.build());
                                } catch (Exception e) {
                                    LOGGER.error("export a stub service error.", e);
                                }
                            }
                        } catch (NoSuchMethodException e) {
                            throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implementation class " + stubClass.getName(), e);
                        }
                    } catch (Throwable t) {
                        LOGGER.error("Failed to create stub implementation class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
                        // ignore
                    }
                }
            }
            return proxy;
        }
        
    }
    

    问题

    如果stub是在服务端配置,而没有在consumer端配置,并且consumer启动在前,这时StubProxyFactoryWrapper将会判断reference不需要stub,当provider启动后,就算@Service配置了stub="true",consumer也不会调用stub。

    总结:

    • stub一般是服务提供方提供,并随jar包一起提供给consumer;
    • 可以设置@Service(stub="true"),这样就能强制consumer端使用stub;
    • stub是在consumer端执行的,可以做前置验证,这样可以过滤非法请求,提高性能;

    相关文章

      网友评论

          本文标题:Dubbo配置参数详解-stub

          本文链接:https://www.haomeiwen.com/subject/mhwractx.html