當一個應用既是一個服務的提供者,同時也是這個服務的消費者的時候,可以直接對本機提供的服務發起本地調用。從 2.2.0 版本開始,Dubbo 默認在本地以 injvm 的方式暴露服務,這樣的話,
在同一個進程里對這個服務的調用會優先走本地調用。
與本地對象上方法調用不同的是,Dubbo 本地調用會經過 Filter 鏈,其中包括了 Consumer 端的 Filter 鏈以及 Provider 端的 Filter 鏈。通過這樣的機制,本地消費者和其他消費者都是統一對待,統一監控,服務統一進行治理。

同時,相比於遠程調用來說,Dubbo 本地調用性能較優,省去了請求、響應的編解碼及網絡傳輸的過程。
要使用 Dubbo 本地調用不需做特殊配置,按正常 Dubbo 服務暴露服務即可。任一服務在暴露遠程服務的同時,也會同時以 injvm 的協議暴露本地服務。
injvm 是一個偽協議,不會像其他協議那樣對外開啟端口,只用於本地調用的目的。
以下面的 XML 配置為例:
<dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20800"/> <bean id="demoServiceTarget" class="org.apache.dubbo.samples.local.impl.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.samples.local.api.DemoService" ref="demoServiceTarget"/> <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService"/>
這里同時配置了同一服務 DemoService 的提供者以及消費者。在這種情況下,該應用中的 DemoService 的消費方會優先使用 injvm 協議進行本地調用。
本地調用是可以顯示關閉的,通過這種方式,服務提供者可以做到對遠端服務消費者和本地消費者一視同仁。具體做法是通過 scope="remote" 來關閉 injvm 協議的暴露,這樣,即使是本地調用者,也需要從注冊中心上獲取服務地址列表,然后才能發起調用,而這個時候的調用過程,與遠端的服務消費者的過程是一致的。
<bean id="target" class="org.apache.dubbo.samples.local.impl.DemoServiceImpl"/> <!-- 服務提供者指定 scope="remote" --> <dubbo:service interface="org.apache.dubbo.samples.local.api.DemoService" ref="target" scope="remote"/> <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService"/>
同樣的,服務消費者也支持通過 scope 來限定發起調用優先走本地,還是只走遠程。比如,可以通過以下的方式強制消費端通過遠程調用的方式來發起 dubbo 調用:
<!-- 服務消費者指定 scope="remote" --> <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService" scope="remote"/>
如果同時服務提供方限定了 scope="local" 的話,
<!-- 服務提供者指定 scope="remote" --> <dubbo:service interface="org.apache.dubbo.samples.local.api.DemoService" ref="target" scope="remote"/> <!-- 服務消費者指定 scope="local" --> <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService" scope="local"/>
那么該程序中的 dubbo 調用將會失敗,原因是服務提供方只暴露了遠程服務到注冊中心上,並沒有暴露 injvm 協議的服務,而出於同一個進程中的服務消費者查找不到 injvm 協議的服務,也不會去遠程的注冊中心上訂閱服務地址。同樣的,當服務提供者限定 scope="local" 而服務消費者限定 scope="remote" 也會因為相同的原因導致調用失敗。出錯信息如下:
[20/03/19 05:03:18:018 CST] main INFO config.AbstractConfig: [DUBBO] Using injvm service org.apache.dubbo.samples.local.api.DemoService, dubbo version: 2.7.1, current host: 169.254.146.168 Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Failed to check the status of the service org.apache.dubbo.samples.local.api.DemoService. No provider available for the service org.apache.dubbo.samples.local.api.DemoService from the url injvm://127.0.0.1/org.apache.dubbo.samples.local.api.DemoService?application=demo-provider&default.lazy=false&default.sticky=false&dubbo=2.0.2&interface=org.apache.dubbo.samples.local.api.DemoService&lazy=false&methods=sayHello&pid=76198®ister.ip=169.254.146.168&release=2.7.1-SNAPSHOT&scope=local&side=consumer&sticky=false×tamp=1553072598838 to the consumer 169.254.146.168 use dubbo version 2.7.1
何時無法使用本地調用?
默認情況下,本地調用是自動開啟的,不需要做額外的配置。只有當需要關閉的時候,才需要通過 scope 的配置來顯式的關閉。
但是,特別需要指出的是,在下面的幾種情況下,本地調用是無法使用的:
第一,泛化調用的時候無法使用本地調用。
第二,消費者明確指定 URL 發起直連調用。當然,如果消費者指定的是 injvm 的 URL,最終的調用也是走本地調用的,比如:
<dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService" url="injvm://127.0.0.1/org.apache.dubbo.samples.local.api.DemoService"/>
強制打開本地調用
除了通過 scope 來控制本地調用的行為之外,也可以通過 injvm 這個配置來強制打開或者禁用本地調用。
<dubbo:consumer injvm="false" .../> <dubbo:provider injvm="true" .../>
但是通過 injvm 來配置本地調用的方式已經被廢棄。通過 scope 的方式來控制是官方推薦的。
總結
本文介紹了本地調用的概念以及帶來的好處,並進一步的揭示了 dubbo 本地調用實際上是在當前進程中暴露了 injvm 的協議,而該協議並不會對外暴露端口,然后討論了如何通過 scope 來細粒度的控制本地調用的行為,並強調了通過 invjm 來配置的方式已經被廢棄,在未來版本中可能會被刪除。