Demo結構介紹
Demo使用Maven聚合功能,里面有三個模塊,目錄如下:
- 其中Consumer模塊為服務消費者,里面TestConsumer和consumer.xml組成了基於Spring配置方式的服務調用,TestConsumerApi是基於Dubbo API方式的服務調用,TestConsumerApiGeneric是泛化方式的服務調用,TestConsumerAsync是異步調用的方式。
- 其中Provider模塊為服務提供者,里面TestProvider和provider.xml組成了基於Spring配置方式的服務提供,TestProviderApi是基於Dubbo API的服務提供,UserServiceImpl為服務實現類。
- 其中SDK模塊是一個二方包,用來存放服務提供者所有的接口,是為了代碼復用使用,在服務提供者和消費者的模塊里面都需要引入這個二方包。
其中SDK里面的接口定義源碼如下:
public interface UserServiceBo {
String sayHello(String name);
String testPojo(Person person);
}
public interface Person {
}
在SDK模塊執行 mvn clean install 命令會安裝該模塊的jar到本地倉庫,要想在其他模塊引入該jar,必須要先執行這個安裝步驟。
基於Spring配置的服務提供方與消費方搭建
Provider模塊為服務提供者,作用是提供的服務到Zookeeper,並使用Netty服務監聽服務消費端的鏈接。里面TestProvider和provider.xml組成了基於XML方式的服務提供,UserServiceImpl為服務實現類。
首先需要在Provider模塊里面引入SDK模塊,因為Provider模塊需要用到UserServiceBo接口(需要在SDK模塊執行 mvn clean install 命令,會安裝該模塊的jar到本地倉庫)。
然后實現UserServiceBo接口為UserServiceImpl,代碼如下:
public class UserServiceImpl implements UserServiceBo{ @Override public String sayHello(String name) { //讓當前當前線程休眠2s try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return name; } @Override public String sayHello2(String name) { //讓當前當前線程休眠2s try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return name; } @Override public String testPojo(Person person) { return JSON.toJSONString(person); } }
provider.xml內容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 提供方應用信息,用於計算依賴關系 --> <dubbo:application name="dubboProvider" /> <!-- 使用zookeeper注冊中心暴露服務地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 用dubbo協議在20880端口暴露服務 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 啟用monitor模塊 --> <dubbo:monitor protocol="registry" /> <bean id="userService" class="com.test.UserServiceImpl" /> <!-- 聲明需要暴露的服務接口 --> <dubbo:service interface="com.test.UserServiceBo" ref="userService" group="dubbo" version="1.0.0" timeout="3000"/> </beans>
日志文件log4j.properties內容如下:
log4j.rootLogger=INFO,A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout
然后,編寫服務發布測試類TestProvider,代碼如下:
public class TestProvider { public static void main(String[] arg) throws InterruptedException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:provider.xml"); //掛起當前線程,如果沒有改行代碼,服務提供者進程會消亡,服務消費者就發現不了提供者了 Thread.currentThread().join(); } }
最后,運行TestProvider類,輸出如下:
說明當前服務已經注冊了Zookeeper。
基於Spring配置的服務消費方搭建
Consumer模塊為服務消費方,服務消費端主要是從Zookeeper獲取自己需要的服務提供者的ip列表,然后根據路由規則選擇一個ip進行遠程調用。里面TestConsumer和consumer.xml組成了基於xml方式的服務調用。
首先需要在Consumer模塊中引入SDK模塊,因為Consumer模塊需要用到UserServiceBo接口(泛化調用時候不需要這個步驟)。
consumer.xml內容如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 消費方應用名,用於計算依賴關系,不是匹配條件,不要與提供方一樣 --> <dubbo:application name="dubboConsumer" /> <!-- 使用multicast廣播注冊中心暴露發現服務地址 --> <dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2181" /> <!-- 啟動monitor--> <dubbo:monitor protocol="registry" /> <!-- 生成遠程服務代理,可以和本地bean一樣使用demoService --> <dubbo:reference id="userService" interface="com.test.UserServiceBo" group="dubbo" version="1.0.0" timeout="3000"/> </beans>
測試服務消費類TestConsumer代碼如下:
public class TestConsumer { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( new String[] { "classpath:consumer.xml" }); final UserServiceBo demoService = (UserServiceBo) context.getBean("userService"); System.out.println(demoService.sayHello("哈哈哈")); } }
最后運行TestConsumer,會輸出:
說明服務消費端已經正常調用了服務提供方的服務了。
注:至此一個經典的含有服務提供者/服務消費者/服務注冊中心的簡單分布式系統搭建完畢了。
基於Dubbo API方式的服務提供方與消費方搭建
基於Dubbo API方式的服務提供方搭建
其中Provider模塊為服務提供者,里面TestProviderApi是基於Dubbo Api的服務提供,UserServiceImpl為服務實現類。
首先需要在Provider模塊里面引入SDK模塊,這個不變。
然后實現UserServiceBo接口為UserServiceImpl,這個也不變。
再接着編寫Dubbo Api服務提供測試代碼TestProviderApi,代碼如下:
public class TestProviderApi { public static void main(String[] arg) throws InterruptedException { //(4.3.1-1)等價於 <bean id="userService" class="com.test.UserServiceImpl" /> UserServiceBo userService = new UserServiceImpl(); //(4.3.1-2)等價於 <dubbo:application name="dubboProvider" /> ApplicationConfig application = new ApplicationConfig(); application.setName("dubboProvider"); //(4.3.1-3)等價於 <dubbo:registry address="zookeeper://127.0.0.1:2181" /> RegistryConfig registry = new RegistryConfig(); registry.setAddress("127.0.0.1:2181"); registry.setProtocol("zookeeper"); // (4.3.1-4)等價於 <dubbo:protocol name="dubbo" port="20880" /> ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setPort(20880); //4.3.1-5)等價於 <dubbo:monitor protocol="registry" /> MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setProtocol("registry"); //4.3.1-6)等價於 <dubbo:service interface="com.test.UserServiceBo" ref="userService" //group="dubbo" version="1.0.0" timeout="3000"/> ServiceConfig<UserServiceBo> service = new ServiceConfig<UserServiceBo>(); // 此實例很重,封裝了與注冊中心的連接,請自行緩存,否則可能造成內存和連接泄漏 service.setApplication(application); service.setMonitor(monitorConfig); service.setRegistry(registry); // 多個注冊中心可以用setRegistries() service.setProtocol(protocol); // 多個協議可以用setProtocols() service.setInterface(UserServiceBo.class); service.setRef(userService); service.setVersion("1.0.0"); service.setGroup("dubbo"); service.setTimeout(3000); service.export(); //4.3.1-8) 掛起當前線程 Thread.currentThread().join(); } }
基於dubbo-spring-boot-starter使用SpringBoot搭建一個簡單的服務提供者服務應用,這里重新打包一個SDK,內容如下:
服務提供方搭建
服務提供方應用結構如下:
其中ProviderApp為SpringBoot的啟動類,代碼如下:
@SpringBootApplication @EnableDubboConfiguration @RestController @ComponentScan(basePackages = { "com.gitchat.demo.provider.service" }) public class ProviderApp { @RequestMapping("/") String home() { return "Hello Demo!"; } public static void main(String[] args) { SpringApplication.run(ProviderApp.class, args); } }
@ComponentScan(basePackages={"com.gitchat.demo.provider.service"})說明掃描com.gitchat.demo.provider.service包下的@Component注解的類到Spring容器。
UserServiceImpl為服務實現類,代碼如下:
@Service(interfaceClass = UserServiceBo.class,group="dubbo",version="1.0.0") @Component public class UserServiceBoImpl implements UserServiceBo{ @Override public String sayHello(String name) { return "hello:" + name; } @Override public String testPojo(Person person) { return person.toString(); } }
@Service(interfaceClass=UserServiceBo.class,group="dubbo",version="1.0.0")標示UserServiceBoImpl會作為服務提供出去,dubbo-start會掃描到這個注解,並創建一個ServiceConfig對象,然后調用ServiceConfig的export方法發布服務到Zookeeper。使用dubbo-starter后,你只需要在需要發布的服務的實現類上添加@Service注解就可以了,無須在顯示創建ServiceConfig對象。
配置文件application.properties內容如下:
server.port=7003 management.port=7004 spring.application.name=demo-provider spring.dubbo.server=true spring.dubbo.registry=zookeeper://127.0.0.1:2181
其中server.port為內嵌Tomcat容器監聽端口,management.port為健康檢查端口,spring.application.name為應用的名稱,spring.dubbo.server=true說明開啟Dubbo服務,spring.dubbo.registry為服務注冊中心地址。
日志配置文件log4j.properties內容如下:
log4j.rootLogger=INFO,A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout
pom.xml里面需要配置內容如下,首先由於是SpringBoot應用所以需要如下引入:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
還需要引入dubbo-spring-boot-starter,這個starter里面包含了Dubbo需要的東西:
<dependency> <groupId>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency>
然后引入Zookeeper包,和依賴的SDK:
<dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> <exclusions> <exclusion> <artifactId>slf4j-log4j12</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency>
最后需要引入spring-boot-maven插件,以便打包為可執行的jar文件:
<build> <finalName>demo-provider</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>false</executable> <excludeDevtools>true</excludeDevtools> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
上面配置完畢后,執行ProviderApp的main函數輸出如下,可知服務注冊到Zookeeper了:
服務消費方搭建
服務消費方應用結構如下:
其中ConsumerApp為SpringBoot啟動的入口,代碼如下:
@SpringBootApplication @EnableDubboConfiguration @RestController @ComponentScan(basePackages = { "com.gitchat.demo.consumer.service" }) public class ConsumerApp { @Autowired private ConsumerService consumerService; @RequestMapping(value = "/testSayHello", method = RequestMethod.GET) String testSayHello(@RequestParam(value = "name", required = true) String name) { return consumerService.sayHello(name); } @RequestMapping("/") String home() { return "Hello Demo!"; } public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); } }
可知注入了ConsumerService的一個實例到Spring容器,然后testSayHello方法內部調用了ConsumerService實例的sayHello方法,這個controller是為了測試服務消費的,下面我們看看ConsumerService的實現:
@Component public class ConsumerService { @Reference(group="dubbo",interfaceClass=UserServiceBo.class,version="1.0.0") private UserServiceBo userServiceBo; public String sayHello(String name){ return userServiceBo.sayHello(name); } }
注解@Reference標示當前要消費UserServiceBo接口的服務,並通過注解說明要消費服務的接口,版本和分組信息,dubbo-starter會自動掃描這個注解然后生成一個ReferenceConfig對象,然后調用ReferenceConfig的get()方法獲取遠程調用實例的代理類,並設置到userServiceBo變量。ConsumerService的sayHello方法則是調用遠程服務userServiceBo的sayHello方法。
配置文件application.properties內容如下:
server.port=7001 management.port=7002 spring.application.name=demo-consumer spring.dubbo.server=true spring.dubbo.registry=zookeeper://127.0.0.1:2181
pom文件和日志文件與服務提供者類似,不再贅述。