1. 引言
本篇側重用代碼說明Dubbo應用程序的開發及使用,通過一個簡單的示例演示Dubbo應用。讀者要是對Dubbo框架的基本概念還有模糊,可以先閱讀
【Dubbo】深入理解Apache Dubbo(一):帶你走近高性能RPC通信框架
另外本篇涉及的所有源碼都已經上傳到GitHub,歡迎交流學習:
Dubbo應用程序實戰
2. 環境配置
無論你使用的是Windows、Linux還是Mac OS操作系統,請保證已經配置如下的環境:JDK(必須,推薦1.8)、IDE(推薦IDEA)、Maven(推薦版本3.x.x)以及ZooKeeper(不是必須,但生產環境已經大量使用ZooKeeper作爲註冊中心,爲了深入理解Dubbo,建議配置)。
另外,爲了更好的學習Dubbo,強烈建議clone源碼學習,附GitHub地址:
https://github.com/apache/dubbo
通過git clone https://github.com/apache/dubbo
命令將Dubbo源碼clone到本地之後,用IDEA打開。所有模塊的測試都在對應的test文件夾下,可以在對應的源碼中打好斷點,然後利用測試類進行單元調試,有利於更加深入的理解Dubbo。
3. 開發Dubbo應用程序
在本節中,筆者將動手快速構建一個完整的服務器和客戶端程序。程序功能很簡單:服務器接收客戶端的請求,然後將消息不做任何處理返回給客戶端。但是麻雀雖小五臟俱全,這個示例將很有助於我們理解Dubbo的運行過程。應用程序編寫有三種方式:XML、註解和API。
3.1 基於XML配置的方式
1.編寫服務端
先定義服務暴露的接口EchoService
。
程序清單3-1
/**
* @author Carson Chu
* @email [email protected]
* @date 2020/1/31 13:53
* @description
*/
public interface EchoService {
/**
* @description 發送給客戶端的信息
* @params [msg]
* @returns java.lang.String
*/
String productMsg(String msg);
}
然後實現該接口。
程序清單3-2
/**
* @author Carson Chu
* @email [email protected]
* @date 2020/1/31 13:53
* @description
*/
public class EchoServiceImpl implements EchoService {
@Override
public String productMsg(String msg) {
String curTime = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("[" + curTime + "] Hello " + msg
+ ", request from client: " + RpcContext.getContext().getRemoteAddress());
return msg;
}
}
接下來,就是基於XML配置的核心了,將配置文件echo-provider.xml
放到項目資源目錄resources/spring下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 服務提供方應用名稱, 方便用於依賴跟蹤 -->
<dubbo:application name="echo-provider"/>
<!-- 使用本地zookeeper作爲註冊中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 只用Dubbo協議並且指定監聽端口 20880 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 通過xml方式配置爲bean, 讓spring託管和實例化 -->
<bean id="echoService" class="com.pers.server.impl.AnnotationServiceImpl"/>
<!-- 聲明服務暴露的接口,並暴露服務 -->
<dubbo:service interface="com.pers.server.AnnotationService" ref="echoService"/>
</beans>
之後,我們還需要編寫一個類用於加載該配置文件並啓動Dubbo服務:
程序清單3-3
/**
* @author Carson Chu
* @description
*/
public class EchoProvider {
public static void main(String[] args) throws Exception {
// #1 指定服務暴露配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/echo-provider.xml"});
// #2 啓動spring容器並暴露服務
context.start();
System.in.read();
}
}
2.編寫客戶端
客戶端也是通過加載XML配置文件的:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 服務消費方應用名稱, 方便用於依賴跟蹤 -->
<dubbo:application name="echo-consumer"/>
<!-- 使用本地zookeeper作爲註冊中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 指定要消費的服務 -->
<dubbo:reference id="echoService" check="false" interface="com.pers.server.EchoService"/>
</beans>
但此步需要保證client依賴server端的服務(因爲EchoService
屬於server模塊),在IDEA中的配置步驟是:
- 打開Project Structure,
- 在彈出的窗口按照下圖依次勾選:
- 最後在彈出的窗口選擇你要依賴的模塊,這裏選擇dubbo-server:
配置完成之後,開始編寫客戶端消費程序:
程序清單3-4
/**
* @author Carson Chu
* @description
*/
public class EchoConsumer {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/echo-consumer.xml"});
context.start();
EchoService echoService = (EchoService) context.getBean("echoService"); // get remote service proxy
String status = echoService.productMsg("Hello Dubbo!");
System.out.println("echo result: " + status);
}
}
編寫完成之後運行main方法,可以在控制檯看到運行結果:
到這裏,基於XML的方式編寫Dubbo應用程序就完成了。
3. 可能遇到的問題
如果啓動報錯:
java.lang.NoClassDefFoundError: io/netty/channel/nio/NioEventLoopGroup
則需要引入該jar包:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
3.2 基於註解的方式
通過XML方式配置啓動Dubbo服務比較常見,另外Dubbo也支持通過註解的方式啓動。通過註解的方式更加友好一點,雖然會耦合一些Dubbo框架自身的註解,但是代碼重構的時候比較便利。
1. 編寫服務端
該方式在定義了接口之後,只需要在接口實現類上加上註解@Service。需要注意的是,**這個@Service不是Spring框架的註解,而是Dubbo的註解。**使用註解之後,由Dubbo框架bazhege 實現類升級爲Spring容器的Bean。
程序清單3-5
import com.alibaba.dubbo.config.annotation.Service;
/**
* @author Carson Chu
* @description
*/
@Service
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String productMsg(String msg) {
String curTime = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("[" + curTime + "] Hello " + msg
+ ", request from client: " + RpcContext.getContext().getRemoteAddress());
return msg;
}
}
不同於基於XML的方式,基於註解的方式生成Dubbo的配置信息是通過程序以及註解來實現的。
程序清單3-6
/**
* @author Carson Chu
* @email [email protected]
* @date 2020/1/31 15:30
* @description
*/
public class AnnotationProvider {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}
@Configuration
// #1 指定掃描服務的位置
@EnableDubbo(scanBasePackages = "com.pers.server")
static class ProviderConfiguration {
@Bean
public ProviderConfig providerConfig() {
return new ProviderConfig();
}
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("echo-annotation-provider");
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
// #2 使用zookeeper作爲註冊中心,同時給出註冊中心ip和端口
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("localhost");
registryConfig.setPort(2181);
return registryConfig;
}
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
// #3 默認服務使用dubbo協議,在20880端口監聽服務
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
return protocolConfig;
}
}
}
2. 編寫客戶端
通過註解消費服務的時候,只需要@Reference註解標註,該註解適用於對象字段和方法。
程序清單3-7 基於註解包裝消費
@Component
public class EchoConsumer {
@Reference
private AnnotationService annotationService;
public String produceMsg(String msg) {
return annotationService.productMsg(msg);
}
}
在完成上述的消費定義之後,還需要完成基於註解的啓動代碼。
程序清單3-7 基於註解消費服務
/**
* @author Carson Chu
* @email [email protected]
* @date 2020/1/31 17:40
* @description
*/
public class AnnotationConsumer {
public static void main(String[] args) {
// #1 基於註解配置初始化spring上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start();
// #2 發起服務調用
EchoConsumer echoService = context.getBean(EchoConsumer.class);
String hello = echoService.produceMsg("Hello Dubbo!");
System.out.println("result: " + hello);
}
@Configuration
// #3 指定要掃描的消費註解,會觸發注入
@EnableDubbo(scanBasePackages = "com.pers.client")
@ComponentScan(value = {"com.pers.client"})
static class ConsumerConfiguration {
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("echo-annotation-consumer");
return applicationConfig;
}
@Bean
public ConsumerConfig consumerConfig() {
return new ConsumerConfig();
}
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
// #4 使用zookeeper作爲註冊中心,同時給出註冊中心ip和端口
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("localhost");
registryConfig.setPort(2181);
return registryConfig;
}
}
}
然後啓動該服務,依然會看出控制檯輸出:result:Hello Dubbo!
3. 可能遇到的問題
如果運行的時候報錯:
NoClassDefFoundError: com/alibaba/spring/util/PropertySourcesUtils
則需要引入該jar包:
<dependency>
<groupId>com.alibaba.spring</groupId>
<artifactId>spring-context-support</artifactId>
<version>1.0.5</version>
</dependency>
3.3 基於API的方式
該方式在大部分場景下不會直接使用,但在開發網關類的應用時,該方式則非常有用。本篇對該方式不做贅述。感興趣的同學可以私聊我學習交流。
小結
通過以上的代碼,我們已經實現了利用Dubbo服務實現客戶端和服務端交互的功能。在客戶端啓動時發生了哪些事情呢?這裏做個小結:
- 客戶端啓動時會創建和Zookeeper註冊中心的連接並拉取服務列表。
- 拉取服務列表完成之後,會與遠程服務建立TCP長連接。
- 客戶端發起服務調用,發送“Hello Dubbo!”給服務方,服務方不做任何處理返回給客戶端。
- 客戶端收到回顯消息並輸出。
在實現了第一個Dubbo應用程序之後,從下一篇開始,筆者將會開始Dubbo底層架構的學習。