日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

dubbo之服务本地暴露

發布時間:2023/12/20 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dubbo之服务本地暴露 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面

源碼 。

服務提供者是標記了@Service注解的類,想要被服務消費者使用,必須將服務暴露出去,即讓服務消費者拿到封裝服務信息的com.alibaba.dubbo.common.URL對象字符串,當前有三種服務暴露方式:

遠程暴露:即將服務信息注冊到遠端注冊中心,如配置<dubbo:service scope="remote" />。 本地暴露:JVM內部調用,因為信息已經在內存中,通過內存可以直接獲取調用信息,因此叫做本地暴露,如配置<dubbo:service scope="local">。 不暴露:不暴露服務,可以忽略這種方式,如配置<dubbo:service scope="none">。

本文來分享的是本地暴露,相關的源碼在模塊dubbo-rpc-injmv中,如下圖:

在dubbo之服務提供者配置 一文中,我們其實分析了部分服務暴露的內容,大家可以看下,本文為了承接,會有部分內容的重疊,就從方法com.alibaba.dubbo.config.ServiceConfig.doExportUrls來開始分析。

1:doExportUrls

源碼如下:

class FakeCls {private void doExportUrls() {// 2022-01-21 18:25:43List<URL> registryURLs = loadRegistries(true);// 循環所有的協議暴露服務到所有的注冊中心地址// 協議:protocols,即<dubbo:protocol>設置// 服務:通過<dubbo:service>設置// 注冊中心地址:registryURLs,通過<dubbo:registry>指定for (ProtocolConfig protocolConfig : protocols) {// 2022-01-21 18:34:22doExportUrlsFor1Protocol(protocolConfig, registryURLs);}} }

2022-01-21 18:25:43處獲取配置的所有注冊中心地址,具體參考1.1:loadRegistries。2022-01-21 18:34:22處是將服務按照指定的協議注冊到注冊中心,具體參考1.2:doExportUrlsFor1Protocol。

1.1:loadRegistries

本文講解的時本地服務暴漏,不會使用到這里的信息,但是為了完整性,放在這里,對這部分感興趣的朋友可以參考dubbo之服務遠程暴露 文章分析。

1.2:doExportUrlsFor1Protocol

將服務按照指定的協議注冊到注冊中心,分為遠程暴漏和本地暴漏,其中本地暴漏不會注冊服務到注冊中心,因為是同一個JVM,信息可以直接從JVM中獲取到,因為本文重點分析的是本地服務暴漏,所以關于遠程暴漏的相關源碼會選擇性忽略,關于這部分的分析,可以才參考dubbo之服務遠程暴露 文章分析。源碼如下:

class FakeCls {private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {// 協議的名稱,如<dubbo:protocol name="dubbo" port="20826">,這里就是dubbo// 協議:就是暴漏自己的方法String name = protocolConfig.getName();// 沒有則默認使用dubboif (name == null || name.length() == 0) {name = "dubbo";}//*** 省略構建URL相關代碼 ***//// 獲取scope,如果是本地暴漏的話配置如:<dubbo:service interface="dongshi.daddy.service.scopelocal.ScopeLocalService" ref="scopeLocalService" scope="local"/>// 在文章開頭也提到了可配置為remote,代表遠程暴漏,none代表不爆露String scope = url.getParameter(Constants.SCOPE_KEY);// 如果是配置scope="none",不進行任何操作,此時不進行暴漏,即不對外使用if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {// 如果是scope不是remote則使用本地服務暴漏if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {// 2022-01-22 12:25:51exportLocal(url);}// 如果是scope不是local則使用遠程服務暴漏if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {// *** 省略遠程服務暴漏邏輯 *** //}}// 添加暴漏服務urlthis.urls.add(url);} }

2022-01-22 12:25:51處是本地服務暴漏,具體參考1.3:exportLocal。

1.3:exportLocal

源碼如下:

class FakeCls {private void exportLocal(URL url) {// url.getProtocol():一般是dubbo // Constants.LOCAL_PROTOCOL:injvm// 為什么做這個判斷???if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {// 構建local的URL,如// injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?accesslog=true&anyhost=true&application=dongshidaddy-provider&bean.name=dongshi.daddy.service.scopelocal.ScopeLocalService&bind.ip=192.168.2.107&bind.port=20826&dubbo=2.0.2&generic=false&interface=dongshi.daddy.service.scopelocal.ScopeLocalService&methods=sayHi&owner=dongshidaddy&pid=6324&scope=local&side=provider&timestamp=1642823993714URL local = URL.valueOf(url.toFullString()).setProtocol(Constants.LOCAL_PROTOCOL).setHost(LOCALHOST).setPort(0);// public static final String SERVICE_IMPL_CLASS = "service.classimpl";// url.getServiceKey():dongshi.daddy.service.scopelocal.ScopeLocalService// getServiceClass(ref:class dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl// 將服務類的信息存儲到StaticContext中StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref));// 2022-01-22 17:21:22Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));exporters.add(exporter);logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");}} }

該小節以下部分稍微有點繞,大家吃耐心,不懂的話,多看幾遍!!!

2022-01-22 17:21:22處protocol定義為private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();,可以看到是獲取自適應擴展類 ,其中從Protocol接口也可以看出來,源碼如下:

/*** Protocol. (API/SPI, Singleton, ThreadSafe)*/ @SPI("dubbo") public interface Protocol {int getDefaultPort();@Adaptive<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;@Adaptive<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;void destroy(); }

可以看到export方法是標注了@Adaptive 注解的,此處protocol
是Protocol#Adaptive,這個很好理解,因為獲取就是動態生成的自適應子類,通過其調用真正的擴展實現類,那么想要知道調用的到底是誰就需要知道生成的代碼到是什么樣子的,我們可以通過如下的步驟來獲取其內容:

在com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClass中的代碼ClassLoader classLoader = findClassLoader();添加條件變量"code.contains("Protocol$Adaptive")",然后再次運行程序,就可以停止在這里,獲取code的內容了。

如下是我獲取的內容:

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");com.alibaba.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);} }

我們重點關注其中的export方法,可以看到是調用url.getProtocol()作為目標擴展類的名稱,那么是什么值呢?我們的url為injvm://127.0.0.1/...可以看到協議是injvm,那么對應的擴展類是誰呢,可以從文件META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol中找到答案,其中key為injvm的的配置項內容是injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol,因此最終調用的擴展類類是com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol,但是真的是這樣嗎?我們來debug看一下,如下圖:

從圖中可以看出,還分別調用了QosProtocolWrapper,ProtocolListenerWrapper,ProtocolFilterWrapper,這是Protocol的Wrapper類,我們從META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol中可以看出來,如下圖:

關于Wrapper詳細可以參考dubbo之SPI Wrapper分析 。

最終調用過程為Protocol$Adaptive->QosProtocolWrapper->ProtocolListenerWrapper->ProtocolFilterWrapper->InjvmProtocol。具體的我們在2:Protocol分析。

2:Protocol

源碼如下:

@SPI("dubbo") public interface Protocol {// 獲取當前協議在沒有配置端口時的默認端口號int getDefaultPort();// 暴漏service供遠程調用// 1:協議對象需要在收到一個請求后記錄遠程源的的地址,通過API RpcContext.getContext().setRemoteAddress()// 2:該方法必須具備冪等性(idempotent [a?'demp?t?nt]),即通過該方法調用一次或者是多次來暴漏一個URL沒有任何差別// 3:Invoker實例需要被框架傳入進來,protoco擴展類需要用到,如自適應時使用// 返回值:Exporter<T>,引用的是被暴漏的service,之后如果是需要取消暴漏的話需要用到@Adaptive<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;@Adaptive<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;void destroy(); }

2.1:Protocol$Adaptive

如何獲取該類信息可以參考1.3:exportLocal。

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {public void destroy() {// 因為沒有標注@Adaptive注解,所以直接拋出java.lang.UnsupportedOperationExceptionthrow new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}// 因為沒有標注@Adaptive注解,所以直接拋出java.lang.UnsupportedOperationExceptionpublic int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");com.alibaba.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);} }

2.2:ProtocolListenerWrapper

源碼如下:

class FakeCls {@Overridepublic <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {// 以下遠端暴漏才會執行,這里可以忽略,因為url是injvm://打頭if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {return protocol.export(invoker);}// 2022-01-23 19:29:28return new ListenerExporterWrapper<T>(protocol.export(invoker),Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));} }

2022-01-23 19:29:28處protocol.export(invoker)繼續調用裝飾的protocol類,這里調用的就是ProtocolFilterWrapper,關于該類參考2.3:ProtocolFilterWrapper。Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY))處是使用keyexporter.listener,從url獲取值從而獲取要激活的ExporterListener擴展類。ListenerExporterWrapper構造函數源碼如下:

class FakeCls {public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {} }

該監聽器的作用是用來監聽Exporter暴漏完畢和取消暴漏完畢。

2.3:ProtocolFilterWrapper

主要用于給Invoker增加Filter過濾器鏈,在調用真正的服務方法之前會調用過濾器Filter的邏輯,具體參考2.3.1:export。

2.3.1:export

源碼如下:

class FakeCls {@Overridepublic <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {// 此處要求協議是registry://,即遠程暴露,這里是injvm://,所以可以忽略if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {return protocol.export(invoker);}// 2022-01-24 16:02:53// 這里的protocol就是InJvmProtocolreturn protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));}}

2022-01-24 16:02:53處buildInvokerChain添加Filter鏈,具體參考2.3.2:buildInvokerChain。

2.3.2:buildInvokerChain

源碼如下:

class FakeCls {private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {Invoker<T> last = invoker;// 獲取激活的Filter擴展類集合List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);if (!filters.isEmpty()) {for (int i = filters.size() - 1; i >= 0; i--) {final Filter filter = filters.get(i);final Invoker<T> next = last;// 將Filter封裝成Invoker,調用invoke方法時,內部鏈式調用下一個Invoker,最后一個Invoker就是目標服務類方法的Invokerlast = new Invoker<T>() {@Overridepublic Class<T> getInterface() {return invoker.getInterface();}@Overridepublic URL getUrl() {return invoker.getUrl();}@Overridepublic boolean isAvailable() {return invoker.isAvailable();}@Overridepublic Result invoke(Invocation invocation) throws RpcException {// 這行代碼比較關鍵,將next作為參數調用Filter類方法,在Filter類方法內部我們就可以通過invoker.invoke來繼續向下調用了,最終調用到真正服務類方法return filter.invoke(next, invocation);}@Overridepublic void destroy() {invoker.destroy();}@Overridepublic String toString() {return invoker.toString();}};}}return last;} }

越靠后的Filter越先執行先執行,執行順序如filter1->filte2->filter3->...->服務類方法。

2.4:InjvmProtocol

該類是Injvm協議的實現類,我們還是從入口方法export開始。

2.4.1:export

源碼如下:

class FakeCls {@Overridepublic <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);} }

主要是創建了InjvmExporter對象。關于該對象,具體參考3:Exporter。

3:Exporter

該接口用于基于相關協議來暴露服務,接口源碼如下:

public interface Exporter<T> {// 獲取內部的InvokerInvoker<T> getInvoker();// 取消暴露void unexport(); }

主要類圖如下:

接下來我們從類AbstractExporter開始來看以下。

3.1:AbstractExporter

源碼如下:

public abstract class AbstractExporter<T> implements Exporter<T> {protected final Logger logger = LoggerFactory.getLogger(getClass());// 內部的Invokerprivate final Invoker<T> invoker;// 是否沒有暴露的標記private volatile boolean unexported = false;public AbstractExporter(Invoker<T> invoker) {if (invoker == null)throw new IllegalStateException("service invoker == null");// 必須是接口if (invoker.getInterface() == null)throw new IllegalStateException("service type == null");// 必須有暴露的URLif (invoker.getUrl() == null)throw new IllegalStateException("service url == null");this.invoker = invoker;}@Overridepublic Invoker<T> getInvoker() {return invoker;}// 取消暴露,其實就是調用getInvoker().destroy();@Overridepublic void unexport() {if (unexported) {return;}unexported = true;getInvoker().destroy();}@Overridepublic String toString() {return getInvoker().toString();} }

3.2:InjvmExporter

AbstractExporter的子類,源碼如下:

class InjvmExporter<T> extends AbstractExporter<T> {// 服務鍵,一般是服務接口的全限定類名稱private final String key;// 已經暴露的Exporterprivate final Map<String, Exporter<?>> exporterMap;InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {super(invoker);this.key = key;this.exporterMap = exporterMap;exporterMap.put(key, this);}// 取消暴露@Overridepublic void unexport() {super.unexport();exporterMap.remove(key);} }

3.3 ListenerExporterWrapper

具有監聽功能的Exporter的Wrapper類,源碼如下:

public class ListenerExporterWrapper<T> implements Exporter<T> {private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class);private final Exporter<T> exporter;// 注冊的暴露監聽器private final List<ExporterListener> listeners;public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {if (exporter == null) {throw new IllegalArgumentException("exporter == null");}this.exporter = exporter;this.listeners = listeners;// 構造函數執行,代表服務暴露了,執行對應的監聽器的暴露方法exportedif (listeners != null && !listeners.isEmpty()) {RuntimeException exception = null;for (ExporterListener listener : listeners) {if (listener != null) {try {listener.exported(this);} catch (RuntimeException t) {logger.error(t.getMessage(), t);exception = t;}}}if (exception != null) {throw exception;}}}@Overridepublic Invoker<T> getInvoker() {return exporter.getInvoker();}@Overridepublic void unexport() {// 取消暴露,執行監聽器的unexported方法try {exporter.unexport();} finally {if (listeners != null && !listeners.isEmpty()) {RuntimeException exception = null;for (ExporterListener listener : listeners) {if (listener != null) {try {listener.unexported(this);} catch (RuntimeException t) {logger.error(t.getMessage(), t);exception = t;}}}if (exception != null) {throw exception;}}}} }

ExporterListener參考4:ExporterListener。

4:ExporterListener

源碼如下:

@SPI public interface ExporterListener {// 服務暴露調用的方法void exported(Exporter<?> exporter) throws RpcException;// 服務取消暴露調用的方法void unexported(Exporter<?> exporter); }

類圖如下:

接下來看下這個唯一的實現類ExporterListenerAdapter,如下:

public abstract class ExporterListenerAdapter implements ExporterListener {@Overridepublic void exported(Exporter<?> exporter) throws RpcException {}@Overridepublic void unexported(Exporter<?> exporter) throws RpcException {}}

也僅僅是個空實現,沒有實際的邏輯。

總結

以上是生活随笔為你收集整理的dubbo之服务本地暴露的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。