1. 程式人生 > >36 放出[自己消費的dubbo服務]作為服務

36 放出[自己消費的dubbo服務]作為服務

前言

本週 因為其他專案的釋出, 造成了我們這邊的一個專案的一些 dubbo 服務的呼叫似乎是沒有生效, 然後 造成了不小的業務困擾 

我們專案 會放出一個dubbo服務[稱之為 save] 給業務系統[biz1, biz2, biz3, .., bizN]呼叫, 但是 最近的一次 專案釋出上線之後, 我們這邊卻發現 有一部分的業務系統 呼叫 save業務 沒有生效??, 這是怎麼回事 

然後 我看了一下 在zookker上面我們專案放出去的服務, 發現現在 不僅僅是我們專案 在提供服務, 然後 其中的一個業務系統 居然也在提供服務[save] ?? 

然後 直覺上, 我覺得 可能就是這個提供的服務所引起的, 然後 通知相關的同事 調整了一下, 然後 後面在觀察, 發現 似乎問題 解決

了 

測試

然後 週末的時候, 藉著閒暇的時間 準備看下 這個問題 

按照 上面生產環境的給定的場景, 我本地起了三個服務, Controller, ServiceImpl, IndirectServiceImpl 

Controller : 消費服務 

ServiceImpl : 暴露服務 

IndirectServiceImpl  : 消費 ServiceImpl 的服務 並暴露 消費到的 服務

然後 來進行測試, 測試結果為 如果 消費方呼叫到了 IndirectServiceImpl  的服務的話, 會丟擲如下異常 

-  [DUBBO] Got unchecked and undeclared exception which called by 192.168.17.1. service: com.hx.helloDubbo.service.UserService, method: hello, exception: com.alibaba.dubbo.rpc.RpcException: Failed to invoke remote proxy method hello to registry://192.168.0.190:2181/com.alibaba.dubbo.registry.RegistryService?application=HelloDubboProvider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.17.1%3A20882%2Fcom.hx.helloDubbo.service.UserService%3Fanyhost%3Dtrue%26application%3DHelloDubboProvider%26dubbo%3D2.5.3%26interface%3Dcom.hx.helloDubbo.service.UserService%26methods%3Dhello%2CaddFriends%26pid%3D11224%26revision%3D0.0.1%26side%3Dprovider%26timestamp%3D1541849608567&pid=11224&registry=zookeeper&timestamp=1541849608566, cause: Method [hello] not found., dubbo version: 2.5.3, current host: 192.168.17.1
com.alibaba.dubbo.rpc.RpcException: Failed to invoke remote proxy method hello to registry://192.168.0.190:2181/com.alibaba.dubbo.registry.RegistryService?application=HelloDubboProvider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.17.1%3A20882%2Fcom.hx.helloDubbo.service.UserService%3Fanyhost%3Dtrue%26application%3DHelloDubboProvider%26dubbo%3D2.5.3%26interface%3Dcom.hx.helloDubbo.service.UserService%26methods%3Dhello%2CaddFriends%26pid%3D11224%26revision%3D0.0.1%26side%3Dprovider%26timestamp%3D1541849608567&pid=11224&registry=zookeeper&timestamp=1541849608566, cause: Method [hello] not found.
	at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:76)
	at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
	at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:64)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:60)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:112)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
	at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:108)
	at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)
	at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
	at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
	at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:724)
Caused by: com.alibaba.dubbo.common.bytecode.NoSuchMethodException: Method [hello] not found.
	at com.alibaba.dubbo.common.bytecode.Wrapper$1.invokeMethod(Wrapper.java:68)
	at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
	at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
	... 25 more

然後 因為預設有重試機制, 並且下一次重試 不會選擇已經選擇過的 provider, 因此 第二次重試 消費的是 ServiceImpl 拿到正確的結果, 然後 繼續走後面的業務流程 

如果幾次重試 都沒有成功, 消費者 也會記錄最後一次呼叫失敗的相關的資訊, 和上面 IndirectServiceImpl 拿到的異常資訊差不多

但是 放到我們的生產環境, 是沒有重試機制的, 因此 如果消費者選擇了 IndirectServiceImpl  放出去的服務的話, 相當於 save業務 是沒有處理的 

然後 我也翻了一下 生產環境的 $IndirectServiceImpl $Controller

的日誌, 發現 部分沒有處理 save業務 的地方 

確實 是有相關異常的日誌 

IndirectServiceImpl暴露的服務

暴露服務的時候, 實際處理業務的 ref 為 消費 ServiceImpl 的proxy物件 

對於這類動態建立[繼承 ClassGenerator$DC]的類, 獲取其頂級超類, 如果頂級超類為 java.lang.Object, 返回 OBJECT_WRAPPER

我們這裡的 proxy0, 顯然是屬於這種情況, proxy0 的程式碼 可以參見, 之前另外一篇文章的 : [$ref:32 簡單瞭解下dubbo的流程]"5. proxy0 : JavassistProxyFactory:35"

OBJECT_WRAPPER 的程式碼如下 

public abstract class Wrapper {
    
    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    private static final String[] OBJECT_METHODS = new String[]{"getClass", "hashCode", "toString", "equals"};

    private static final Wrapper OBJECT_WRAPPER = new Wrapper() {

        public String[] getMethodNames() {
            return OBJECT_METHODS;
        }

        public String[] getDeclaredMethodNames() {
            return OBJECT_METHODS;
        }

        public String[] getPropertyNames() {
            return EMPTY_STRING_ARRAY;
        }

        public Class<?> getPropertyType(String pn) {
            return null;
        }

        public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException {
            throw new NoSuchPropertyException("Property [" + pn + "] not found.");
        }

        public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException {
            throw new NoSuchPropertyException("Property [" + pn + "] not found.");
        }

        public boolean hasProperty(String name) {
            return false;
        }

        public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException {
            if ("getClass".equals(mn)) return instance.getClass();
            if ("hashCode".equals(mn)) return instance.hashCode();
            if ("toString".equals(mn)) return instance.toString();
            if ("equals".equals(mn)) {
                if (args.length == 1) return instance.equals(args[0]);
                throw new IllegalArgumentException("Invoke method [" + mn + "] argument number error.");
            }
            throw new NoSuchMethodException("Method [" + mn + "] not found.");
        }
    };
    
}

然後 當消費者 呼叫 IndirectServiceImpl 的服務, 走 invokeMethod, 找對應的 save 方法走 save業務, 然後 顯然 就丟擲了上面 我們 IndirectServiceImpl 端所丟擲的異常 

首先是 OBJECT_WRAPPER 丟擲最原始的異常, 然後 經過 AbstractProxyInvoker 封裝了一次, 然後過 ExceptionFilter 在封裝了一次 輸出日誌, 得到我們 所看見的異常 

ServiceImpl 暴露的服務

暴露服務的時候, 實際處理業務的 ref 為 真正處理業務UserServiceImpl 的例項

然後封裝出來的 Wrapper 為 Wrapper1 [Wrapper$idx], Wrapper1 的程式碼可以 參見 [$ref:32 簡單瞭解下dubbo的流程]"2. Wrapper1 : ServiceConfig:426; ReferenceConfig:269; JavassistProxyFactory:40"

## 附上 [32 簡單瞭解下dubbo的流程] 的連結

完