文档章节

Dubbo优雅服务降级之Stub和回声服务

Mr_Qi
 Mr_Qi
发布于 2017/06/24 12:21
字数 1134
阅读 764
收藏 5

上篇Dubbo优雅服务降级之mock描述了关于mock的细节。此篇就详述一下关于Stub的实现。

在dubbo的官方文档中写道

Mock是Stub的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现RpcException(比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用Stub,可能就需要捕获并依赖RpcException类,而用Mock就可以不依赖RpcException,因为它的约定就是只有出现RpcException时才执行。

Mock通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过Mock数据返回授权失败。 

从上述中的表述可知,stub的功能更为全面,可以说可以操作的范围更加广阔。

首先我们来看一下在Dubbo中核心生成代理类的方法。

com.alibaba.dubbo.rpc.ProxyFactory

我们找到熟悉的spi接口文件


stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

可以看到熟悉的jdk代理和javassist代理(我们常用的包括cglib,jdk代理,javassit代理)

除此之外就是StubProxyFactoryWrapper

如前几篇说描述,wrapper一定会出现在extensionLoader获取扩展点的包装上。

因此可以认为此wrapper将会包装所有从proxy扩展点获取到的代理对象上。

@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
    T proxy = proxyFactory.getProxy(invoker);
    if (GenericService.class != invoker.getInterface()) {
        String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
        if (ConfigUtils.isNotEmpty(stub)) {
            Class<?> serviceType = invoker.getInterface();
            if (ConfigUtils.isDefault(stub)) {
                if (invoker.getUrl().hasParameter(Constants.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 implemention 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
                    URL url = invoker.getUrl();
                    if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
                        url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
                        url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
                        try{
                            export(proxy, (Class)invoker.getInterface(), url);
                        }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 implemention class " + stubClass.getName(), e);
                }
            } catch (Throwable t) {
                LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
                // ignore
            }
        }
    }
    return proxy;
}

从代码中可知,绝大部分和mock的情况一致,通过获取指定的Stub对象(默认以Local结尾)。

我们在回顾一下ReferenceBean引用到远程服务的过程中的最后一步    

// 创建服务代理
return (T) proxyFactory.getProxy(invoker);

由此我们可以认为创建的对象是由Stubwrapper包装过的。

那么此时Stub就拥有了比Mock更早开始或者更晚收尾的时机(基本可以认为是在Spring Aop针对invoke的环绕增强)。

这样服务端开发者也可以定义好Stub和接口jar一起下发给对应的服务使用者(比如参数校验等等,决定是否调用真实的服务端等等)

谈到了代理的创建,顺便也要说一下关于EchoService的创建。

我们在拿到Dubbo服务时,可以将任意服务强制转换为EchoService。

5. 服务消费方,通过将服务强制转型为EchoService,并调用$echo()测试该服务的提供者是可用

如 assertEqauls(“OK”, ((EchoService)memberService).$echo(“OK”));

我们知道了能够强转说明了该服务必然是实现了此接口。那么自然可以猜测是在动态代理时做的手脚。

观察到JavassistProxyFactory和JdkProxyFactory均继承了

public abstract class AbstractProxyFactory implements ProxyFactory {
 
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        Class<?>[] interfaces = null;
        String config = invoker.getUrl().getParameter("interfaces");
        if (config != null && config.length() > 0) {
            String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
            if (types != null && types.length > 0) {
                interfaces = new Class<?>[types.length + 2];
                interfaces[0] = invoker.getInterface();
                interfaces[1] = EchoService.class;
                for (int i = 0; i < types.length; i ++) {
                    interfaces[i + 1] = ReflectUtils.forName(types[i]);
                }
            }
        }
        if (interfaces == null) {
            interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};
        }
        return getProxy(invoker, interfaces);
    }
     
    public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
 
}

可以明显看出该接口会在动态代理时生成,那么具体的实现在哪里呢?

这个需要回到FIlter的机制上来了解dubbo源码系列之filter的今世

在对应的Filter中存在

@Activate(group = Constants.PROVIDER, order = -110000)
public class EchoFilter implements Filter {
 
   public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
      if(inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1 )
         return new RpcResult(inv.getArguments()[0]);
      return invoker.invoke(inv);
   }
 
}

该方法返回了inv的默认第一个参数。该FIlter作用在服务端,并且排序相对较小,可以认为基本上该过滤器的顺序靠后,尽量避免误拦截指定的方法(方法名称需要规范,框架采用$echo )

因此在客户端可以任意调用某个服务端的$echo方法完成校验。

 

© 著作权归作者所有

共有 人打赏支持
Mr_Qi

Mr_Qi

粉丝 280
博文 359
码字总数 369228
作品 0
南京
程序员
私信 提问
dubbo 在服务治理方面的一些策略

本文策略说明全部来自官方文档. 集群容错策略: Failover Cluster 失败自动切换,当出现失败,重试其它服务器。(缺省) 通常用于读操作,但重试会带来更长延迟。 可通过retries="2"来设置重试次...

万里路遥
2016/06/21
244
0
微服务神经元 Neural 4.0.0-RELEASE 发布

分布式服务框架中的神经组织,主要为分布式架构提供放通率控制、流量控制、服务降级、幂等机制、泛化容错、SLA熔断、隔离舱壁、超时控制和慢性尝试功能。专业解决微服务场景下的指挥协调组织...

李景枫
2016/08/16
2.3K
6
abtest-system后台系统设计与搭建

本文来自网易云社区 作者:刘颂 1 项目背景: 2017年5月:客户端提出增加https&dns以及双cdn业务功能 后台配合实现使用disconf配置 针对不同的域名或者请求配置不同的https等信息 2017年7月:...

网易云
2018/09/07
0
0
Dubbo的fallback机制

本地伪装 本地伪装 [1] 通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。 在 spring 配置文件中按以下方式配置: 或 在工程...

宸明
2018/08/15
0
0
服务框架dubbo(一):基础篇

学习博文:https://www.imooc.com/t/6300745 dubbo是一个分布式服务框架,致力于提供高性能透明化RPC远程调用方案,提供SOA服务治理解决方案。 由于dubbo各个分层都是很多扩展, 注册中心有r...

Java搬砖工程师
2018/11/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

eggjs与sequelize简单demo

参考 egg 官方文档 安装 // 依赖npm install --save egg-sequelize mysql2// ts 类型npm install --save @types/sequelize 插件,config/plugin.ts import { EggPlugin } from 'egg';......

Geeyu
46分钟前
1
0
看过上百部片子的这个人教你视频标签算法解析

本文由云+社区发表 随着内容时代的来临,多媒体信息,特别是视频信息的分析和理解需求,如图像分类、图像打标签、视频处理等等,变得越发迫切。目前图像分类已经发展了多年,在一定条件下已经...

腾讯云加社区
今天
2
0
2. 红黑树

定义:红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树(Binary Search Tree)。 要理解红黑树,先要了解什么是二叉查找树。在上一章中,我们学习了什么是二叉树,以及二叉树...

火拳-艾斯
今天
3
0
input的button类型,点击页面跳转

一、input type=button 不做任何操作 例如: <input type="button" class="btn btn-primary" style="width: 30%" value="返回" onclick="window.location.href='/users/list'"></input> onc......

Sunki
今天
1
0
踩坑:js 小数运算出现精度问题

背景 在学习小程序商城源码时发现了这个问题,单价可能出现小数,小数之间运算结果会莫名其妙多出一大串数字,比如下面这样👇。 在此之前我是知道 js 中著名的 0.1 + 0.2 != 0.3 的问题的,...

dkvirus
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部