dubbo是一個分布式服務框架,是阿里巴巴SOA服務化治理方案的核心框架。zookeeper是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。zookeeper可以作為dubbo服務的注冊中心,兩者結合起來可以實現微服務中的 服務注冊、發現、負載均衡和健康檢查,容錯,動態配置管理的功能。我們先用springMVC將dubbo服務暴露成瀏覽器可訪問的http接口。
本例代碼已經上傳到GitHub,傳送門見下:
https://github.com/ArchitectRoad/BoliERP.git
重要的事情說前面。先總結Dubbo+Spring Boot使用過程中的要點、注意點吧。
- Spring Boot的目的之一就是使開發人員可以擺脫繁雜的xml配置,所以Spring Boot整合Dubbo時,使用Annotation是推薦方式。使用Annotation集成Spring Boot + Dubbo時可以參照 https://github.com/JeffLi1993/springboot-learning-example。
- 為便于更好理解Dubbo的原理,在本例中仍然使用xml進行Dobbo provider和consumer的配置。Spring Boot + Dubbo聯合使用時,需要顯示在入口類中通過以下方式引用dubbo配置。
@ImportResource(classpath: dubbo-consumer.xml)
public class ProdWebApplication {
...
}
- Service Provider打包為Jar,使用com.alibaba.dubbo.container.Main作為入口啟動Spring容器時,默認讀取classpath:META-INF/spring/目錄下的applicationContext.xml。關于如何生成Service Provider Jar,詳見prodService模塊的pom.xml中的build節點定義。
- 解決Dubbo與Spring Boot的依賴包沖突,引入dubbo時exclude spring。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo-version}</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
- 解決log4j與slf4j的版本沖突。Exclude zookeeper中的slf4j與log4j,exclude zkclient中的log4j。
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper-version}</version>
<!-- Solve conflict with Spring boot -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient-version}</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
- classpath 和 classpath* 區別:
classpath: 只會到你的class路徑中查找找文件。只加載找到的第一個文件。
classpath*: 不僅包含class路徑,還包括jar文件中(class路徑)進行查找。從多個jar文件中加載相同的文件。
用classpath*:需要遍歷所有的classpath,所以加載速度較慢。因此,在規劃的時候,應該盡可能規劃好資源文件所在的路徑,盡量避免使用classpath*。
**/ 表示的是任意目錄。
項目結構如下:
- pordParentPom module
定義所有模塊的parent pom,統一管理依賴版本。繼承spring-boot-starter-parent,并定義dubbo, zookeeper, zkclient版本。通過exclusion解決jar包沖突。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.boli</groupId>
<artifactId>prodParentPom</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<modules>
<module>../prodApi</module>
<module>../prodService</module>
<module>../prodWebApp</module>
</modules>
<properties>
<dubbo-version>2.5.3</dubbo-version>
<zookeeper-version>3.4.9</zookeeper-version>
<zkclient-version>0.1</zkclient-version>
</properties>
<dependencyManagement>
<dependencies>
<!-- dubbo dependency -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo-version}</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper-version}</version>
<!-- Solve conflict with Spring boot -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient-version}</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
</project>
-
prodApi module
Api接口定義,以及Dubbo consumer定義。prodService模塊具體實現Api接口。prodWebApp模塊引用該模塊,根據Api接口定義,通過zookeeper發現并調用具體服務。
prodApi工程目錄
IProdSvc.java
/*
* Copyright (c) 2017 architectroad@yeah.net. All Rights Reserved.
*/
package com.boli.service.intfc.prod;
import java.util.List;
/**
* Product Service Interface
* Author: Joey Zhu (architectroad@yeah.net)
* Date: 2017/3/11.
* Time: 11:01
*/
public interface IProdSvc {
public List<String> getProdList();
public String sayHello (String name);
}
prodApi.consumer.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: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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 消費者應用信息,用于提供依賴關系 -->
<dubbo:application name="prodSvcConsumer" />
<!-- 注冊地址,用于消費者尋找服務,, 此處可以配置多個zookeeper -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" />
<!-- 超時設置 -->
<dubbo:consumer timeout="5000" />
<!-- 引用的服務 -->
<dubbo:reference id="prodService" interface="com.boli.service.intfc.prod.IProdSvc" version="1.0.0" />
</beans>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>prodParentPom</artifactId>
<groupId>com.boli</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.boli</groupId>
<artifactId>prodApi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
</dependencies>
</project>
- prodService module
ProdSvcProvider.java
Service Provider入口類
/*
* Copyright (c) 2017 architectroad@yeah.net. All Rights Reserved.
*/
package com.boli;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Service Provider Entry Class
*
* Author: Joey Zhu (architectroad@yeah.net)
* Date: 2017/3/13.
* Time: 13:58
*/
public class ProdSvcProvider {
public static void main(String[] args) throws Exception {
//本地啟動spring容器,適用于開發階段調試
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"META-INF/spring/applicationContext.xml"});
context.start();
System.out.println("啟動成功");
System.in.read(); // 按任意鍵退出
//內置Tomcat方式啟動
// SpringApplication.run(ProdSvcImpl.class, args);
//Dubbo spring容器方式啟動
// com.alibaba.dubbo.container.Main.main(args);
}
}
prodService.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: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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方應用信息,用于計算依賴關系 -->
<dubbo:application name="prodSvcProvider" />
<!-- 使用multicast廣播注冊中心暴露服務地址 -->
<!--<dubbo:registry address="multicast://224.5.6.7:1234" /> -->
<!-- 使用zookeeper注冊中心暴露服務地址,即zookeeper的所在服務器ip地址和端口號 -->
<!-- check: 注冊中心不存在時是否報錯, subscribe: 是否向此注冊中心訂閱服務, register: 是否向此注冊中心注冊服務 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" check="false" subscribe="false" register="true"/>
<!-- 用dubbo協議在20880端口暴露服務 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 具體的實現bean -->
<bean id="prodService" class="com.boli.service.impl.prod.ProdSvcImpl"/>
<!-- 如果使用掃描包方式引入bean,使用如下方式 -->
<!-- dubbo:annotation package="com.boli.service.impl.*" / -->
<!-- 聲明需要暴露的服務接口 -->
<dubbo:service interface="com.boli.service.intfc.prod.IProdSvc" ref="prodService" version="1.0.0"/>
<!-- 使用注解方式暴露接口 -->
<!--dubbo:annotation package="com.boli.service.impl" /-->
<!-- 加入spring注解掃描 -->
<!--context:component-scan base-package="com.boli.service.impl"/-->
</beans>
applicationContext.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: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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- import dubbo providers -->
<import resource="classpath:dubbo/prodService.provider.xml"></import>
</beans>
pom.xml
通過mvn package命令打包生成prodService-1.0-SNAPSHOT.jar時,指定com.alibaba.dubbo.container.Main作為入口類,并拷貝所有的依賴包到lib目錄中。打包完成后,使用java -jar prodService-1.0-SNAPSHOT.jar
命令即可運行并注冊服務到指定的服務注冊中心,比如zookeeper。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.boli</groupId>
<artifactId>prodService</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<artifactId>prodParentPom</artifactId>
<groupId>com.boli</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.boli</groupId>
<artifactId>prodApi</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<!--生成的classes文件的路徑,此文件夾中包含運行時的所有文件,可以不配置-->
<targetPath>${project.build.directory}/classes</targetPath>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*provider.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<targetPath>${project.build.directory}/classes/META-INF/spring</targetPath>
<directory>src/main/resources/META-INF/spring</directory>
<filtering>true</filtering>
<includes>
<include>applicationContext.xml</include>
</includes>
</resource>
</resources>
<!--下面的必須配置-->
<plugins>
<!-- 打包jar文件時,配置manifest文件,加入lib包的jar依賴 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>target/classes/</classesDirectory>
<archive>
<manifest>
<mainClass>com.alibaba.dubbo.container.Main</mainClass>
<!-- 打包時 MANIFEST.MF文件不記錄時間戳版本 -->
<useUniqueVersions>false</useUniqueVersions>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
<manifestEntries>
<Class-Path>.</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<type>jar</type>
<includeTypes>jar</includeTypes>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- prodWebApp module
Web Application。服務消費方。
ProdController.java
/*
* Copyright (c) 2017 architectroad@yeah.net. All Rights Reserved.
*/
package com.boli.web;
import com.boli.service.intfc.prod.IProdSvc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* Product Controller
*
* Author: Joey Zhu (architectroad@yeah.net)
* Date: 2017/3/10.
* Time: 18:46
*/
@Controller
public class ProdController {
@Autowired
IProdSvc prodService;
@RequestMapping("/prodList")
@ResponseBody
private List<String> getProdList () {
return prodService.getProdList();
}
}
ProdWebApplication.java
/*
* Copyright (c) 2017 architectroad@yeah.net. All Rights Reserved.
*/
package com.boli;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
/**
* Product Module Web Application
* Author: Joey Zhu (architectroad@yeah.net)
* Date: 2017/3/13.
* Time: 12:16
*/
@EnableAutoConfiguration
@ImportResource({"classpath*:META-INF/spring/applicationContext.xml"})
@ComponentScan("com.boli.web")
public class ProdWebApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ProdWebApplication.class, args);
}
}
applicationContext.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: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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- import dubbo consumers -->
<import resource="classpath*:dubbo/*.consumer.xml"></import>
</beans>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.boli</groupId>
<artifactId>prodWebApp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<artifactId>prodParentPom</artifactId>
<groupId>com.boli</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.boli</groupId>
<artifactId>prodApi</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
代碼已經上傳到GitHub,傳送門見下:
https://github.com/ArchitectRoad/BoliERP.git
本地測試步驟及測試結果:
- 先啟動ZooKeeper
joeys-MacBook-Pro:~ joey$ zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /Library/ZooKeeper/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
joeys-MacBook-Pro:~ joey$
-
啟動ProdSvcProvider
啟動Provider
從日志中可以看到服務注冊成功
INFO com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry - [DUBBO] Register: dubbo://192.168.0.109:20880/com.boli.service.intfc.prod.IProdSvc?anyhost=true&application=prodSvcProvider&dubbo=2.5.3&interface=com.boli.service.intfc.prod.IProdSvc&methods=sayHello,getProdList&pid=5836&revision=1.0.0&side=provider×tamp=1490058278762&version=1.0.0, dubbo version: 2.5.3, current host: 127.0.0.1
3.啟動ProdWebApplication
從日志中可以看到服務訂閱成功
INFO 5872 --- [ main] c.a.d.r.zookeeper.ZookeeperRegistry : [DUBBO] Subscribe: consumer://192.168.0.109/com.boli.service.intfc.prod.IProdSvc?application=prodSvcConsumer&category=providers,configurators,routers&default.timeout=5000&dubbo=2.5.3&interface=com.boli.service.intfc.prod.IProdSvc&methods=sayHello,getProdList&pid=5872&revision=1.0.0&side=consumer×tamp=1490058693542&version=1.0.0, dubbo version: 2.5.3, current host: 192.168.0.109
4.訪問應用
訪問本地接口,驗證web應用部署成功
訪問遠程服務接口,驗證Dubbo遠程服務調用成功