spring整合dubbo+zookeeper搭分布式服务,简单案例

来源:互联网 发布:python adodb下载 编辑:程序博客网 时间:2024/06/06 09:53
一、首先,使用maven搭建父子结构项目

子项目间的关系说明:
utils、common、bean基本上是最底层的
utils提供工具类,其它子项目需要就引入。
common提供公共的文件或者常量类,其它子项目需要就引入。
bean提供实体类,如果作为service接口中的参数或返回值一定要序列化。
core依赖utils,core提供了绝大多数的引包工作,特别是web和app都要用到的包(比如spring),这样web和app就不用再写一遍引用了,直接引入core。
mapper依赖bean,mapper提供了数据持久层接口。
service依赖mapper,提供业务层接口,web和app都要引用,web调用接口方法,app实现接口方法。这里的接口在app项目中配置成dubbo service暴露服务接口。在web项目中配置成dubbo reference调用服务。
app依赖service和core,提供service接口实现,数据库的操作。
web依赖service和core,包含controller与页面。

二、除了正常项目(我这边是ssm项目)要引的包之外,还要引入三个包dubbo,zookeeper和zkclient。还把dubbo中依赖的spring给剔除掉,不然会有冲突。这个引包的工作我是在core项目中添加的,因为web和app都需要这些包,也都依赖于core。
<!-- dubbo --><dependency>    <groupId>com.alibaba</groupId>    <artifactId>dubbo</artifactId>    <version>${com.alibaba.dubbo.version}</version>    <exclusions>    <exclusion>    <artifactId>spring</artifactId>    <groupId>org.springframework</groupId>    </exclusion>    </exclusions></dependency><!-- /dubbo --><!-- zookeeper --><dependency>    <groupId>org.apache.zookeeper</groupId>    <artifactId>zookeeper</artifactId>    <version>${org.apache.zookeeper.version}</version></dependency><!-- /zookeeper --><!-- zkclient --><dependency>    <groupId>com.101tec</groupId>    <artifactId>zkclient</artifactId>    <version>${com.101tec.version}</version></dependency><!-- /zkclient -->
三、app项目的功能是提供服务,需要实现service接口。
application里提供一个服务启动带main方法的类App.java。
service.impl里提供service接口的实现。
mapper里是mapper.xml(这里用的是mybatis)
资源文件:包括mybatis、spring、缓存ehcache、数据库和日志的配置。

其中比正常项目多了一个spring-dubbo-provide.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-4.1.xsd          http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">     <!-- 提供方应用信息,用于计算依赖关系 -->  <dubbo:application name="ch-zdemo-dubbo-app-provider" /><!-- 使用multicast广播注册中心暴露服务地址 --><!--  <dubbo:registry address="multicast://224.5.6.7:1234" /> -->   <!-- 使用zookeeper注册中心暴露服务地址 --><dubbo:registry address="zookeeper://localhost:2181" />   <!-- 用dubbo协议在20880端口暴露服务 --><dubbo:protocol name="dubbo" port="20880" /><!-- 声明需要暴露的服务接口 --><dubbo:service interface="com.chhuang.system.service.IChMenuService" ref="chMenuService" />  <dubbo:service interface="com.chhuang.system.service.IChRoleMenuMapService" ref="chRoleMenuMapService" /><dubbo:service interface="com.chhuang.system.service.IChRoleService" ref="chRoleService" /><dubbo:service interface="com.chhuang.system.service.IChUserRoleMapService" ref="chUserRoleMapService" /><dubbo:service interface="com.chhuang.system.service.IChUserService" ref="chUserService" />   <!-- 和本地bean一样实现服务,这里注释掉是因为用了注解@Service("引号里最好写上名字对应上面dubbo:service里的ref,不然要ref要写上包名+类名")<bean id="chMenuService" class="com.chhuang.system.service.impl.ChMenuServiceImpl" />--></beans>

好了,在applicationContext.xml中添加import:
这里不能用classpath:,打包成jar后会找不到,用相对路径就可以了,我这里几个spring的配置文件都是在一个目录下的。
<import resource="spring-dubbo-provider.xml" />

app项目要打包成jar运行的,所以需要一个入口,一个带main方法的类:
package com.chhuang.application;import java.io.InputStream;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.log4j.PropertyConfigurator;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 启动服务 * @author CHHUANG * */public class App {private static final Log log = LogFactory.getLog(App.class);@SuppressWarnings("resource")public static void main(String[] args) {//修改log4j文件路径,默认是在包根目录下,就是java或者resource文件夹下。InputStream in = App.class.getResourceAsStream("/config/log4j.properties");PropertyConfigurator.configure(in);//加载springClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:config/spring/applicationContext.xml");context.start();// 为保证服务一直开着,利用输入流的阻塞来模拟/*方法1.使用System.in.read阻塞,在后台运行jar时就不起作用了,因为没有控制台 * 比如在linux服务器上用带&命令起动表示后台运行: * nohup java -jar *.jar &try {log.info("Application server is running...");System.in.read();} catch (IOException e) {e.printStackTrace();}*///方法2.Thread.sleepwhile(true){try {log.info("Application server is running...");Thread.sleep(24*60*60*1000l);} catch (InterruptedException e) {e.printStackTrace();}}}}
四、web是服务消费者,调用service接口,实现与用户交互。
配置文件添加了一个spring-dubbo-consumer.xml,配置如下:

<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-4.1.xsd          http://code.alibabatech.com/schema/dubbohttps://raw.githubusercontent.com/alibaba/dubbo/master/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd">  <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --><dubbo:application name="ch-zdemo-dubbo-app-consumer"/><!-- 使用multicast广播注册中心暴露发现服务地址 --><!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> --><dubbo:registry address="zookeeper://localhost:2181" /><!-- 生成远程服务代理,可以和本地bean一样使用 --><dubbo:reference interface="com.chhuang.system.service.IChMenuService" id="chMenuService" />  <dubbo:reference interface="com.chhuang.system.service.IChRoleMenuMapService" id="chRoleMenuMapService" /><dubbo:reference interface="com.chhuang.system.service.IChRoleService" id="chRoleService" /><dubbo:reference interface="com.chhuang.system.service.IChUserRoleMapService" id="chUserRoleMapService" /><dubbo:reference interface="com.chhuang.system.service.IChUserService" id="chUserService" /></beans>
同样把spring-dubbo-consumer.xml引入web项目的spring.xml中,我这里没用ContextLoaderListener初始化spring,直接引入springmvc配置文件中
<import resource="classpath:config/spring/spring-dubbo-consumer.xml" />

controller中正常注解写法就行了


五、zookeeper
下载zookeeper
解压后修改conf目录下的zoo_sample.cfg,把名字改成zoo.cfg
运行bin目录下的zkServer。
启动顺序是先启动zookeeper,再启动app,最后启动web。
还有一个dubbo管理平台。

六、调试
zookeeper控制台

应用服务器日志

web服务器日志


七、打包发布
这里要注意app项目的pom构建的配置:
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include><!-- 主要是mapper.xml文件 --></includes></resource><resource><directory>src/main/resources</directory><!-- 配置文件 --></resource></resources><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.1</version><configuration><source>1.7</source><!-- 源代码使用的开发版本 --><target>1.7</target><!-- 需要生成的目标class文件的编译版本 --></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.0.0</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>com.chhuang.application.App</mainClass><!-- 带main方法的类 --></transformer><!-- 以添加的方式写入spring.handlers和spring.schemas --><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer></transformers><filters><!-- 过滤掉多余的.SF .DSA .RSA文件 --><filter><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters></configuration></execution></executions></plugin></plugins><finalName>ch-zdemo-dubbo-app</finalName></build>
可以使用父项目的pom打包需要排好module之间依赖顺序,也可以单独打包根据项目间的依赖关系顺序打包。
app项目构建成功后target目录下,不带original的jar文件,作为应用服务器。
web项目打包成war,作为web服务器。
发布时,先启动zookeeper,再启动应用服务器注册提供服务,最后启动web服务器。

八、问题
1、遇到dubbo的xml报错,需要在xml catalog中引入一下dubbo.xsd,github上有找一下就可以了。
实在不行, 就直接到地址改成下载地址


2、web项目,要在deploymentAssembly中把依赖的项目都加齐了,不然项目上会报错,又找不到出错的文件 。


3、单独打包某个子项目时失败,可能需要运行一次父项目的pom打包,注意pom看module的顺序,依赖关系多的项目放后面。


4、没报错在web中查询结果出不来,看一下接口的方法的参数或者返回值是否是基本类型或者自定义的实体类有没有序列化。另外参数或者返回值如果是自定义的实体类子类不要重复定义与父类相同的属性。不然会反序列化不了。


5、打包app成功后,运行jar报错找不到spring的配置文件,可能是因为在applicationContext.xml中import资源时用了classpath,直接用相对路径就可以。


6、打包app后,运行jar报错
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location
Unable to locate Spring NamespaceHandler for XML schema namespace
原因是打包时META-INF/spring.schemas和META-INF/spring.handlers这两个文件被其它包依赖的spring覆盖了,就找不到可用的xsd地址了。
解决办法是:在app项目pom.xml中改用maven-shade-plugin插件打包,不要使用maven-assembly-plugin。并且添加<transformer>,作用是往spring.schemas和spring.handlers中追加内容,而不是覆盖。


7、java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
原因是因为在META-INF下会有多余的以SF结尾的文件,删除后不会出现次问题
解决方法在maven-shade-plugin插件<configuration>标签中,再添加 filter过滤掉.SF .DSA .RSA的文件不打包。


8、net.sf.ehcache.CacheException: Error configuring from input stream. Initial cause was 2 字节的 UTF-8 序列的字节 2 无效。
原因是ehcache.xml中使用了中文注释,
解决方法把中文注释去掉就好用了。

4 0