美文网首页
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