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字段
- 不包含字段直接调用远程服务
- 包含MOck字段并且值为force打头那么直接调用Mock操作
- 否则优先执行远程调用,如果调用失败且返回的是非业务异常再调用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 ; } }
- 首先找到对应的 可以调用的invoker,并且标记为mock
- 如果无法找到invoker那么直接新建MockInvoker
- 调用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部分的分析结束了。