网上关于dubbo的资料其实已经不少了,尤其是还有一个比较全面的官方文档教程。
但是正所谓知易行难,很多事不动动手,就总不知道里边的一些细节。
虽然早就想学一下dubbo,但是奈何需要学的东西太多,于是就以“工作为主导”的出发点,先学了其他技术。
这次,也是基于上边的出发点,我们某个项目需要重构成dubbo的项目,于是便正好从dubbo基础环境搭建开始学一下,并写一篇笔记备忘。
这篇笔记大概会包含如下一些内容:
1、搭建过程记录
2、遇到的问题和解决办法记录,这一点会在搭建过程记录的同时进行说明,不另外指出
整个过程基本如下:
zookeeper注册中心
注册中心的理解
由于之前学习过springcloud微服务,所以对于zookeeper注册中心的理解,我觉得还是比较容易的。具体的理解可以参考当时写的springcloud的注册中心那篇文章: http://blog.csdn.net/tuzongxun/article/details/72650100
比较明显的不同是,这里的zookeeper需要下载安装一个zookeeper软件。
zookeeper安装
我在安装的时候主要是参考了“小宝鸽”的博客,他写的很是详细,所以我觉得没有必要在重复造轮子,只需要记住在哪里可以查到参考资料。他关于zookeeper安装的博客地址如下:
http://blog.csdn.net/u013142781/article/details/50395650
dubbo管理平台
由于管理平台与业务逻辑本身没有关系,所以我没有深入了解,也是直接参考“小宝鸽”的博客:
http://blog.csdn.net/u013142781/article/details/50396621
dubbo服务者
创建项目
根据目前的主流,我这里是在eclipse中创建了一个maven管理的web项目。
基础依赖
项目基础依赖主要参考dubbo官方指导文档的说明http://dubbo.io/books/dubbo-user-book/dependencies.html
按照这个文档上所说,需要包含如下一些依赖组件,分别是:
dubbo.jar
spring.jar
log4j.jar
commons-logging.jar
javassist.jar
netty.jar
在实际导入的时候会发现,dubbo.jar已经依赖了spring.jar和netty.jar,这个版本也是根据导入的dubbo.jar的版本来定的。
为了避免冲突,也为了使用自己想要的版本的spring和netty,于是在导入dubbo.jar时需要把这两个先进行排除配置,之后的pom.xml中依赖包的配置基本如下:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.15</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> <version>3.2.5.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.9.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.5.3</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> <exclusion> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> </exclusion> </exclusions> </dependency>
spring配置
dubbo本身就整合了spring,因此spring的配置是必不可少的,这里也是使用最简单的配置,文件头就先省略:
<context:property-placeholder location="classpath:config.properties" /> <import resource="spring-dubbo-provider.xml" />
config.properties配置
配置dubbo服务,需要指定zookeeper注册中心的地址,以及使用dubbo暴露服务的端口等,这些配置配在config.properties文件中:
dubbo.zookepper.address=127.0.0.1:2181 dubbo.provider.port=29880
dubbo配置
要把我们的项目作为一个服务注册到zookeeper注册中心,就需要进行相应的配置,配置如下:
<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="dubboServerDemo-pro"/> <!-- 注册中心配置,file值为application name加实例名 --> <dubbo:registry protocol="zookeeper" address="${dubbo.zookepper.address}" file="dubboServerDemo-pro-1.cache"/> <!-- 用dubbo协议在20880端口暴露服务,dubbo访问日志输出到应用日志 --> <dubbo:protocol name="dubbo" port="${dubbo.provider.port}" accesslog="true"/> <!-- 声明需要暴露的服务接口 超时时间90分钟,超时不重试--> <dubbo:service interface="dubboServerTest.DubboServerService" ref="dubboServiceTest" cluster="failfast" timeout="5400000" /> <!-- 和本地bean一样实现服务 --> <bean id="dubboServiceTest" class="dubboServerTest.DubboServerServiceImpl" /> </beans>
dubbo具体服务接口和实现
dubbo的具体服务接口和实现就是很普通的java接口和类,如下:
package dubboServerTest; public interface DubboServerService { public void sayHello(); }
package dubboServerTest; public class DubboServerServiceImpl implements DubboServerService { @Override public void sayHello() { System.out.println("dubbo测试"); } }
web.xml配置
spring.xml文件需要被加载,可以用java代码加载,也可以用其他方式,我这里就选择使用web.xml的配置来加载,web.xml配置如下:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
问题说明
按理说,照上边的做法程序应该是可以运行,并且可以再管理平台中显示出服务者的,但是实际上我启动后却抛出了如下异常:
java.lang.NoClassDefFoundError: org/I0Itec/zkclient/exception/ZkNoNodeException
原因是少了zookeeper的依赖包,需要在pom.xml中补上如下的依赖配置:
<dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency>
消费者
项目搭建
消费者的项目搭建其实和服务者差不多,区别在于dubbo的配置略有区别,消费者的配置大致如下:
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <dubbo:application name="consumer-of-helloworld-app" /> <!-- 使用multicast广播注册中心暴露发现服务地址 --> <dubbo:registry address="${dubbo.zookepper.address}" /> <!-- 生成远程服务代理,可以和本地bean一样使用demoService --> <dubbo:reference id="demoService" interface="dubboServerTest.DubboServerService" />
同样的,这里的也需要指定注册中心的地址,也是配置在config.properties中:
dubbo.zookepper.address=zookeeper://127.0.0.1:2181
需要注意的是,这里配置的zookeeper注册中心地址的写法,和服务中心配置的写法也略有不同。
接口声明
消费端,需要一个和服务端一样的接口,但是不用实现这个接口。这里的做法实际上和webservice接口比较类似,像cxf、xfire、hessian都差不多。
服务调用
在消费端可以直接调用上边声明的接口的抽象方法,而实际上运行的就是服务端的具体实现,调用处的代码如下:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dubboServerTest1.DubboServerService; public class MyController { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "classpath:spring.xml"); DubboServerService dubboServerService = (DubboServerService) applicationContext .getBean("demoService"); dubboServerService.sayHello(); } }
问题说明
消费者这里相对比较简单,但是除了说对于zookeeper注册中心地址的配置处有细节需要注意外,对于接口的声明也需要特别注意。
为了验证是否和webservice一样的,消费端接口包括包名在内都必须和服务端一样,我在声明接口时特意使用了不一样的包名,使得实际的类内容以及在dubbo配置文件中的配置如下:
package dubboServerTest1; public interface DubboServerService { public void sayHello(); }
<dubbo:reference id="demoService" interface="dubboServerTest1.DubboServerService" />
然后运行main方法的时候,就会抛出如下异常:
Caused by: java.lang.IllegalStateException: Failed to check the status of the service dubboServerTest1.DubboServerService. No provider available for the service
由此也就证明,消费端的接口声明,包括包名在内,都必须和服务端一模一样。针对这个问题,有一种推荐的做法,就是把这个接口类打包,让服务端和消费端公用。