dubbo源码解析(四十一)集群——Mock
集群——Mock
目標:介紹dubbo中集群的Mock,介紹dubbo-cluster下關于服務降級和本地偽裝的源碼。前言
本文講解兩塊內容,分別是本地偽裝和服務降級,本地偽裝通常用于服務降級,比如某驗權服務,當服務提供方全部掛掉后,客戶端不拋出異常,而是通過 Mock 數據返回授權失敗。而服務降級則是臨時屏蔽某個出錯的非關鍵服務,并定義降級后的返回策略。
源碼分析
(一)MockClusterWrapper
public class MockClusterWrapper implements Cluster {private Cluster cluster;public MockClusterWrapper(Cluster cluster) {this.cluster = cluster;}@Overridepublic <T> Invoker<T> join(Directory<T> directory) throws RpcException {// 創建MockClusterInvokerreturn new MockClusterInvoker<T>(directory,this.cluster.join(directory));}}該類是服務降級的裝飾器類,對Cluster進行了功能增強,增強了服務降級的功能。
(二)MockClusterInvoker
該類是服務降級中定義降級后的返回策略的實現。
1.屬性
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);/*** 目錄*/ private final Directory<T> directory;/*** invoker對象*/ private final Invoker<T> invoker;2.invoke
@Override public Result invoke(Invocation invocation) throws RpcException {Result result = null;// 獲得 "mock" 配置項,有多種配置方式String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();// 如果沒有mockif (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// 直接調用 Mock Invoker ,執行本地 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.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);}// 如果調用失敗,則服務降級result = doMockInvoke(invocation, e);}}}return result; }該方法是定義降級后的返回策略的實現,根據配置的不同來決定不用降級還是強制服務降級還是失敗后再服務降級。
3.doMockInvoke
@SuppressWarnings({"unchecked", "rawtypes"}) private Result doMockInvoke(Invocation invocation, RpcException e) {Result result = null;Invoker<T> minvoker;// 路由匹配 Mock Invoker 集合List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);// 如果mockInvokers為空,則創建一個MockInvokerif (mockInvokers == null || mockInvokers.isEmpty()) {// 創建一個MockInvokerminvoker = (Invoker<T>) new MockInvoker(directory.getUrl());} else {// 取出第一個minvoker = mockInvokers.get(0);}try {// 調用invokeresult = 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; }該方法是執行本地Mock,服務降級。
4.selectMockInvoker
private List<Invoker<T>> selectMockInvoker(Invocation invocation) {List<Invoker<T>> invokers = null;//TODO generic invoker?if (invocation instanceof RpcInvocation) {//Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachement needs to be improved)// 注意隱式契約(盡管描述被添加到接口聲明中,但是可擴展性是一個問題。附件中的做法需要改進)((RpcInvocation) invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString());//directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return.// 如果調用中存在Constants.INVOCATION_NEED_MOCK,則目錄將返回正常調用者列表,否則,將返回模擬調用者列表。try {invokers = directory.list(invocation);} catch (RpcException e) {if (logger.isInfoEnabled()) {logger.info("Exception when try to invoke mock. Get mock invokers error for service:"+ directory.getUrl().getServiceInterface() + ", method:" + invocation.getMethodName()+ ", will contruct a new mock with 'new MockInvoker()'.", e);}}}return invokers; }該方法是路由匹配 Mock Invoker 集合。
(三)MockInvokersSelector
該類是路由選擇器實現類。
1.route
@Override public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,URL url, final Invocation invocation) throws RpcException {// 如果附加值為空,則直接if (invocation.getAttachments() == null) {// 獲得普通的invoker集合return getNormalInvokers(invokers);} else {// 獲得是否需要降級的值String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);// 如果為空,則獲得普通的Invoker集合if (value == null)return getNormalInvokers(invokers);else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {// 獲得MockedInvoker集合return getMockedInvokers(invokers);}}return invokers; }該方法是根據配置來決定選擇普通的invoker集合還是mockInvoker集合。
2.getMockedInvokers
private <T> List<Invoker<T>> getMockedInvokers(final List<Invoker<T>> invokers) {// 如果沒有MockedInvoker,則返回nullif (!hasMockProviders(invokers)) {return null;}// 找到MockedInvoker,往sInvokers中加入,并且返回List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1);for (Invoker<T> invoker : invokers) {if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {sInvokers.add(invoker);}}return sInvokers; }該方法是獲得MockedInvoker集合。
3.getNormalInvokers
private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {// 如果沒有MockedInvoker,則返回普通的Invoker 集合if (!hasMockProviders(invokers)) {return invokers;} else {// 否則 去除MockedInvoker,把普通的Invoker 集合返回List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());for (Invoker<T> invoker : invokers) {// 把不是MockedInvoker的invoker加入sInvokers,返回if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {sInvokers.add(invoker);}}return sInvokers;} }該方法是獲得普通的Invoker集合,不包含mock的。
4.hasMockProviders
private <T> boolean hasMockProviders(final List<Invoker<T>> invokers) {boolean hasMockProvider = false;for (Invoker<T> invoker : invokers) {// 如果有一個是MockInvoker,則返回trueif (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {hasMockProvider = true;break;}}return hasMockProvider; }該方法是判斷是否有MockInvoker。
以上三個類是對服務降級功能的實現,下面兩個類是對本地偽裝的實現。
(四)MockProtocol
該類實現了AbstractProtocol接口,是服務
final public class MockProtocol extends AbstractProtocol {@Overridepublic int getDefaultPort() {return 0;}@Overridepublic <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {throw new UnsupportedOperationException();}@Overridepublic <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {// 創建MockInvokerreturn new MockInvoker<T>(url);} }(五)MockInvoker
本地偽裝的invoker實現類。
1.屬性
/*** 代理工廠*/ private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); /*** mock 與 Invoker 對象的映射緩存*/ private final static Map<String, Invoker<?>> mocks = new ConcurrentHashMap<String, Invoker<?>>(); /*** 異常集合*/ private final static Map<String, Throwable> throwables = new ConcurrentHashMap<String, Throwable>();/*** url對象*/ private final URL url;2.parseMockValue
public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {Object value = null;// 如果mock為empty,則if ("empty".equals(mock)) {// 獲得空的對象value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>) returnTypes[0] : null);} else if ("null".equals(mock)) {// 如果為null,則返回nullvalue = null;} else if ("true".equals(mock)) {// 如果為true,則返回truevalue = true;} else if ("false".equals(mock)) {// 如果為false,則返回falsevalue = 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("{")) {// 是map類型的value = JSON.parseObject(mock, Map.class);} else if (mock.startsWith("[")) {// 是數組類型value = JSON.parseObject(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值
3.invoke
@Override public Result invoke(Invocation invocation) throws RpcException {// 獲得 `"mock"` 配置項,方法級 > 類級String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);if (invocation instanceof RpcInvocation) {((RpcInvocation) invocation).setInvoker(this);}// 如果mock為空if (StringUtils.isBlank(mock)) {// 獲得mock值mock = getUrl().getParameter(Constants.MOCK_KEY);}// 如果還是為空。則拋出異常if (StringUtils.isBlank(mock)) {throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));}// 標準化 `"mock"` 配置項mock = normalizeMock(URL.decode(mock));// 等于 "return " ,返回值為空的 RpcResult 對象if (mock.startsWith(Constants.RETURN_PREFIX)) {// 分割mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();try {// 獲得返回類型Type[] returnTypes = RpcUtils.getReturnTypes(invocation);// 解析mock值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);}// 如果是throw} else if (mock.startsWith(Constants.THROW_PREFIX)) {// 根據throw分割mock = mock.substring(Constants.THROW_PREFIX.length()).trim();// 如果為空,則拋出異常if (StringUtils.isBlank(mock)) {throw new RpcException("mocked exception for service degradation.");} else { // user customized class// 創建自定義異常Throwable t = getThrowable(mock);// 拋出業務類型的 RpcException 異常throw new RpcException(RpcException.BIZ_EXCEPTION, t);}} else { //impl mocktry {// 否則直接獲得invokerInvoker<T> invoker = getInvoker(mock);// 調用return invoker.invoke(invocation);} catch (Throwable t) {throw new RpcException("Failed to create mock implementation class " + mock, t);}} }該方法是本地偽裝的核心實現,mock分三種,分別是return、throw、自定義的mock類。
4.normalizedMock
public static String normalizeMock(String mock) {// 若為空,直接返回if (mock == null) {return mock;}mock = mock.trim();if (mock.length() == 0) {return mock;}if (Constants.RETURN_KEY.equalsIgnoreCase(mock)) {return Constants.RETURN_PREFIX + "null";}// 若果為 "true" "default" "fail" "force" 四種字符串,返回defaultif (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock) || "force".equalsIgnoreCase(mock)) {return "default";}// fail:throw/return foo => throw/returnif (mock.startsWith(Constants.FAIL_PREFIX)) {mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();}// force:throw/return foo => throw/returnif (mock.startsWith(Constants.FORCE_PREFIX)) {mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();}// 如果是return或者throw,替換`為"if (mock.startsWith(Constants.RETURN_PREFIX) || mock.startsWith(Constants.THROW_PREFIX)) {mock = mock.replace('`', '"');}return mock; }該方法是規范化mock值。
5.getThrowable
public static Throwable getThrowable(String throwstr) {// 從異常集合中取出異常Throwable throwable = throwables.get(throwstr);// 如果不為空,則拋出異常if (throwable != null) {return throwable;}try {Throwable t;// 獲得異常類Class<?> bizException = ReflectUtils.forName(throwstr);Constructor<?> constructor;// 獲得構造方法constructor = ReflectUtils.findConstructor(bizException, String.class);// 創建 Throwable 對象t = (Throwable) constructor.newInstance(new Object[]{"mocked exception for service degradation."});// 添加到緩存中if (throwables.size() < 1000) {throwables.put(throwstr, t);}return t;} catch (Exception e) {throw new RpcException("mock throw error :" + throwstr + " argument error.", e);} }該方法是獲得異常。
6.getInvoker
private Invoker<T> getInvoker(String mockService) {// 從緩存中,獲得 Invoker 對象,如果有,直接緩存。Invoker<T> invoker = (Invoker<T>) mocks.get(mockService);if (invoker != null) {return invoker;}// 獲得服務類型Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());// 獲得MockObjectT mockObject = (T) getMockObject(mockService, serviceType);// 創建invokerinvoker = proxyFactory.getInvoker(mockObject, serviceType, url);if (mocks.size() < 10000) {// 加入集合mocks.put(mockService, invoker);}return invoker; }該方法是獲得invoker。
7.getMockObject
public static Object getMockObject(String mockService, Class serviceType) {if (ConfigUtils.isDefault(mockService)) {mockService = serviceType.getName() + "Mock";}// 獲得類型Class<?> mockClass = ReflectUtils.forName(mockService);if (!serviceType.isAssignableFrom(mockClass)) {throw new IllegalStateException("The mock class " + mockClass.getName() +" not implement interface " + serviceType.getName());}try {// 初始化return mockClass.newInstance();} catch (InstantiationException e) {throw new IllegalStateException("No default constructor from mock class " + mockClass.getName(), e);} catch (IllegalAccessException e) {throw new IllegalStateException(e);} }該方法是獲得mock對象。
后記
該部分相關的源碼解析地址:https://github.com/CrazyHZM/i...該文章講解了集群中關于mock實現的部分,到這里為止,集群部分就全部講完了,這是2.6.x版本的集群,那在2.7中對于路由和配置規則都有相應的大改動,我會在之后2.7版本的講解中講到。接下來我將開始對序列化模塊進行講解。
總結
以上是生活随笔為你收集整理的dubbo源码解析(四十一)集群——Mock的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 魔兽世界6.0超级猴子球效率刷地点分享
- 下一篇: 捕鱼大亨怎么玩?捕鱼大亨玩法解析