Dubbo优雅服务降级之mock


声明:本文转载自https://my.oschina.net/qixiaobo025/blog/1005476,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

dubbo作为国内互联网最常用的Java开源服务治理框架,在提供了远程调用的同时也提供了服务降级功能。

首先可以考虑一下服务降级的需求===》考虑在系统服务调用失败时可以返回指定消息而不是异常

通常来说选用dubbo的Mock功能可以实现。

在上一篇中描述到MockClusterWrapper几乎是必须的操作,实质上Mock也是通过其生成的MockClusterInvoker来实现。

public Result invoke(Invocation invocation) throws RpcException {    Result result = null;                 String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();        if (value.length() == 0 || value.equalsIgnoreCase("false")){            //no mock            result = this.invoker.invoke(invocation);        } else if (value.startsWith("force")) {            if (logger.isWarnEnabled()) {               logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " +  directory.getUrl());            }            //force:direct mock            result = doMockInvoke(invocation, null);        } else {            //fail-mock            try {               result = this.invoker.invoke(invocation);            }catch (RpcException e) {          if (e.isBiz()) {             throw e;          } else {             if (logger.isWarnEnabled()) {                  logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " +  directory.getUrl(), e);               }             result = doMockInvoke(invocation, e);          }       }        }        return result; }

在判断对应URL中mock字段

  1. 不包含字段直接调用远程服务
  2. 包含MOck字段并且值为force打头那么直接调用Mock操作
  3. 否则优先执行远程调用,如果调用失败且返回的是非业务异常再调用Mock操作

调用Mock操作分为如下几步

@SuppressWarnings({ "unchecked", "rawtypes" })    private Result doMockInvoke(Invocation invocation,RpcException e){       Result result = null;        Invoker<T> minvoker ;                 List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);       if (mockInvokers == null || mockInvokers.size() == 0){          minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());       } else {          minvoker = mockInvokers.get(0);       }       try {          result = minvoker.invoke(invocation);       } catch (RpcException me) {          if (me.isBiz()) {             result = new RpcResult(me.getCause());          } else {             throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());          } //             } catch (Throwable me) {          throw new RpcException(getMockExceptionMessage(e, me), me.getCause());       }       return result;     } /**     * 返回MockInvoker     * 契约:     * directory根据invocation中是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是一个normal invoker 还是一个 mock invoker     * 如果directorylist 返回多个mock invoker,只使用第一个invoker.     * @param invocation     * @return     */    private List<Invoker<T>> selectMockInvoker(Invocation invocation){        //TODO generic invoker?        if (invocation instanceof RpcInvocation){            //存在隐含契约(虽然在接口声明中增加描述,但扩展性会存在问题.同时放在attachement中的做法需要改进            ((RpcInvocation)invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString());            //directory根据invocation中attachment是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是normal invokers or mock invokers            List<Invoker<T>> invokers = directory.list(invocation);            return invokers;        } else {            return null ;        }    }
  1. 首先找到对应的 可以调用的invoker,并且标记为mock
  2. 如果无法找到invoker那么直接新建MockInvoker
  3. 调用invoker,当调用MockInvoker发生下列行为
public Result invoke(Invocation invocation) throws RpcException {        String mock = getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);        if (invocation instanceof RpcInvocation) {           ((RpcInvocation) invocation).setInvoker(this);        }        if (StringUtils.isBlank(mock)){           mock = getUrl().getParameter(Constants.MOCK_KEY);        }                 if (StringUtils.isBlank(mock)){           throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));        }        mock = normallizeMock(URL.decode(mock));        if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())){            RpcResult result = new RpcResult();            result.setValue(null);            return result;        } else if (mock.startsWith(Constants.RETURN_PREFIX)) {            mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();            mock = mock.replace('`', '"');            try {                Type[] returnTypes = RpcUtils.getReturnTypes(invocation);                Object value = parseMockValue(mock, returnTypes);                return new RpcResult(value);            } catch (Exception ew) {                throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: "+ url , ew);            }        } else if (mock.startsWith(Constants.THROW_PREFIX)) {            mock = mock.substring(Constants.THROW_PREFIX.length()).trim();            mock = mock.replace('`', '"');            if (StringUtils.isBlank(mock)){                throw new RpcException(" mocked exception for Service degradation. ");            } else { //用户自定义类                Throwable t = getThrowable(mock);          throw new RpcException(RpcException.BIZ_EXCEPTION, t);            }        } else { //impl mock             try {                 Invoker<T> invoker = getInvoker(mock);                 return invoker.invoke(invocation);             } catch (Throwable t) {                 throw new RpcException("Failed to create mock implemention class " + mock , t);             }        }    }

将mock参数对应的值进行解析,分为下列几种

//mock=fail:throw //mock=fail:return //mock=xx.Service private String normallizeMock(String mock) {    if (mock == null || mock.trim().length() ==0){       return mock;    } else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())){       mock = url.getServiceInterface()+"Mock";    }    if (mock.startsWith(Constants.FAIL_PREFIX)) {         mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();     } else if (mock.startsWith(Constants.FORCE_PREFIX)) {         mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();     }    return mock; }

其实分为force或者fail打头的类型,如果不是则直接使用对应<Interface>Mock的类型为Mock类(方便自定义相关返回以及降级)。

为了便于操作提供了force或者fail指令

public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {     Object value = null;     if ("empty".equals(mock)) {         value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>)returnTypes[0] : null);     } else if ("null".equals(mock)) {         value = null;     } else if ("true".equals(mock)) {         value = true;     } else if ("false".equals(mock)) {         value = false;     } else if (mock.length() >=2 && (mock.startsWith("\"") && mock.endsWith("\"")             || mock.startsWith("\'") && mock.endsWith("\'"))) {         value = mock.subSequence(1, mock.length() - 1);     } else if (returnTypes !=null && returnTypes.length >0 && returnTypes[0] == String.class) {         value = mock;     } else if (StringUtils.isNumeric(mock)) {         value = JSON.parse(mock);     }else if (mock.startsWith("{")) {         value = JSON.parse(mock, Map.class);     } else if (mock.startsWith("[")) {         value = JSON.parse(mock, List.class);     } else {         value = mock;     }     if (returnTypes != null && returnTypes.length > 0) {         value = PojoUtils.realize(value, (Class<?>)returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null);     }     return value; }

mock可以返回false,null等,常用mock=fail:return null

那么就可以实现在一些不重要的服务挂掉的时候强制返回null,如果客户端对应兼容此操作自然可以在客户端不报错,继续正常的业务流程(如果精细化控制可以使用Mock类)

private Invoker<T> getInvoker(String mockService){        Invoker<T> invoker =(Invoker<T>) mocks.get(mockService);    if (invoker != null ){       return invoker;    } else {           Class<T> serviceType = (Class<T>)ReflectUtils.forName(url.getServiceInterface());            if (ConfigUtils.isDefault(mockService)) {                mockService = serviceType.getName() + "Mock";            }                         Class<?> mockClass = ReflectUtils.forName(mockService);            if (! serviceType.isAssignableFrom(mockClass)) {                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());            }                    if (! serviceType.isAssignableFrom(mockClass)) {                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());            }            try {                T mockObject = (T) mockClass.newInstance();                invoker = proxyFactory.getInvoker(mockObject, (Class<T>)serviceType, url);                if (mocks.size() < 10000) {                    mocks.put(mockService, invoker);                }                return invoker;            } catch (InstantiationException e) {                throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);            } catch (IllegalAccessException e) {          throw new IllegalStateException(e);       }    }    }

Mock类的调用也很简单,直接调用对应类型名的对应Interface的构造函数===》此处没有依赖注入

至此Mock部分的分析结束了。

本文发表于2017年06月24日 04:36
(c)注:本文转载自https://my.oschina.net/qixiaobo025/blog/1005476,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2472 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1