dubbo基础入门使用与开发

绝地灬酷狼 2022-07-12 04:26 282阅读 0赞

一、使用dubbo的准备工作

1. zookeeper单节点环境

  1. Demo中选用的zookeeperzookeeper-3.4.5-cdh5.0.0.tar.gz版本,作为开发环境,在不考虑zookeeper部署多节点的情况下,在本机(windows环境)解压之后,进入解压目录下的conf文件夹,复制一份zoo\_sample.cfg文件,修改为zoo.cfg。然后运行bin/zkServer.cmd 启动zookeeper服务。

备注:(1)zookeeper启动会自动读取zoo.cfg文件,该文件主要定义了zookeeper需要暴露的端口,数据和日志的存储路径。

   (2)如果启动cmd之后 闪退,在该cmd中最后一行添加pause,可以查看错误信息。

2.zookeeper分布式部署

  1. 如果保证zookeeper的高可用性,需要部署一个zookeeper集群,这里介绍zookeeper集群在linux中部署的方式。

(1) 在三台机器上分别解压zookeeper-3.4.5-cdh5.0.0.tar.gz包,为了目录的清晰,可以修改解压后文件夹的名称为zookeeper-2181。

(2) 在每台机器的zookeeper解压目录中,创建data和logs两个文件夹,用于zookeeper的数据和日志存放:

1076786-20161207162846147-576814249.png

(3) 在每台机器的zookeeper解压目录的conf文件夹中,创建一个zoo.cfg文件,文件内容如下:

复制代码

  1. tickTime=2000
  2. initLimit=5
  3. syncLimit=2
  4. dataDir=/home/nxeop/zookeeper/data
  5. dataLogDir=/home/nxeop/zookeeper/logs
  6. clientPort=2181
  7. server.1= EopApp1:2888:3888
  8. server.2= EopApp2:2888:3888
  9. server.3= EopApp2:2888:3888
  10. maxClientCnxns=60
  11. minSessionTimeout=4000

复制代码

  1. **注解:**

tickTime:心跳时间

initLimit:多少个心跳时间内,允许其他server连接并初始化数据

syncLimit:多少个tickTime内,允许follower节点同步

dataDir:存放内存数据文件目录,根据实际环境修改

dataLogDir:存放日志文件目录,根据实际环境修改

clientPort:监听端口,使用默认2181端口

`server.x:配置集群主机信息,[hostname]:[通信端口]:[选举端口],根据自己的主机信息修改```

`maxClientCnxns:最大并发客户端数,用于防止DOS的,设置为0``是不加限制```

`minSessionTimeout:最小的客户端session``超时时间(单位是毫秒)```

`maxSessionTimeout:最大的客户端session``超时时间(单位是毫秒)```

EopApp1、EopApp2、EopApp3是三台需要部署zookeeper节点服务器的hostname,这个zoo.cfg文件在集群中所有节点的配置都是一样,直接复制就可以了。

server.1、server.2和server.3 表示三个服务器,每个服务器在zookeeper集群中有一个唯一的id,这个id就是server.x中的这个x。

(4) 最关键的一步,我们需要配置每台服务器上的zookeeper的id。之前已经在每台机器上的zookeeper解压目录中创建了data文件夹和logs文件夹。那么,我们在data文件夹中创建一个文件叫myid,对于第一台服务器,文件的内容为1。依次在各台主机的data目录下生成myid文件设置id值,myid的内容要与前面配置的zoo.cfg中设置的server.x保持一致。

    1076786-20161207162910085-1395874670.png

(5) 启动zookeeper:

在三台服务器上依次执行zookeeper目录中:

  1. bin/zkServer.sh start

命令,zookeeper三个节点一次启动。

全部启动完成后,可以执行

  1. bin/zkServer.sh status

去查看每个zk节点在集群中的状态。

1076786-20161207163355601-583990943.png

至此,zookeeper的集群已经部署完成。

示例1:最简单dubbo服务注册与调用

1. 示例场景和思路

  1. 当前实例的设计思路图如下所示:

1076786-20161207163430257-2026170009.png

person-center:人员信息中心,通过dubbo对外提供服务;

person-client:客户端,通过dubbo使用person-center提供的服务;

zookeeper:注册中心,所有的dubbo应用都注册到这上面;

person-interface:一个接口包,定义了人员信息接口和实体:

person-center引用interface包,用于实现这个接口;

person-client引用interface包,使用里面的接口名称去调用dubbo服务。

2.编写person-interface接口包

  1. 1)创建person-interfacemaven工程,默认打包方式为jar包。
  2. 2)编写实体类PersonInfo和接口IPersonInfoService,示例代码结构入下所示:

1076786-20161207163509569-1821967981.png

(3)IPersonInfoService定义两个方法:(这里虽然定义了PersonInfo,不过demo1先不使用,只做最简单的接口)

复制代码

  1. public interface IpersonInfoService {
  2. /**
  3. * 查询全部人员信息
  4. */
  5. public String queryPersonInfoAll();
  6. /**
  7. * 根据人员编号查询人员信息
  8. */
  9. public String queryPersonInfoByNumber(String personNumber);
  10. }

复制代码

3.编写person-center 服务端

工程参考结构如下:

1076786-20161207163600944-1383617738.png

(1)创建person-center的maven工程,默认打包方式为jar包。

(2)在pom文件中需要添加的核心依赖jar包如下:

  1. aperson-interface,引用公共接口,用于实现这些接口:
  2. <!-- 引入实现编写好的person接口层 -->
  3. <dependency>
  4. <groupId>test.dubbo.interface</groupId>
  5. <artifactId>person-interface</artifactId>
  6. <version>0.0.1-SNAPSHOT</version>
  7. </dependency>

  b、dubbo,引用dubbo依赖,客户端和服务端都使用同一个依赖:

复制代码

  1. <!-- 引入dubbo框架(服务端、客户端通用) -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>dubbo</artifactId>
  5. <version>2.8.4</version>
  6. <exclusions>
  7. <exclusion>
  8. <artifactId>spring</artifactId>
  9. <groupId>org.springframework</groupId>
  10. </exclusion>
  11. </exclusions>
  12. </dependency>

复制代码

  1. czkclient,引用zookeeper客户端,dubbo会自动使用zkclient去和zookeeper进行连接:
  2. <!-- 因为dubbo服务端需要注册服务到zk中,因此依赖zkClient -->
  3. <dependency>
  4. <groupId>com.github.sgroschupf</groupId>
  5. <artifactId>zkclient</artifactId>
  6. <version>0.1</version>
  7. </dependency>

(3)编写实现person-interface接口的代码:

  1. 其中,PersonInfoServiceImpl实现了person-interface包中的IPersonInfoService接口的方法:

复制代码

  1. @Service
  2. public class PersonInfoServiceImpl implements IpersonInfoService {
  3. @Override
  4. public String queryPersonInfoAll() {
  5. System.out.println("===================================");
  6. System.out.println("接口实现:queryPersonInfoAll()");
  7. System.out.println("===================================");
  8. return "from PersonInfoServiceImpl : some person Info";
  9. }
  10. @Override
  11. public String queryPersonInfoByNumber(String personNumber) {
  12. System.out.println("===================================");
  13. System.out.println("接口实现:queryPersonInfoByNumber(String personNumber)");
  14. System.out.println("===================================");
  15. return "from PersonInfoServiceImpl :" + personNumber + " 's Info.";
  16. }

复制代码

(4)把这个person-center工程配置为一个dubbo的应用。

  1. 在工程的src/main/resources下面创建文件(包括文件夹):

META-INF/spring/applicationContext.xml。文件可以直接从源码中把文件拿过来。

结构如下:

复制代码

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://code.alibabatech.com/schema/dubbo
  8. http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
  9. 备注1:引入dubbo配置相关的xml的命名空间
  10. <!-- 自动扫描注解:通过dubbo实现 -->
  11. <dubbo:annotation package="test.dubbo.*" />
  12. <!-- 必须加上:dubbo应用的名称 -->
  13. <dubbo:application name="person-center" />
  14. <!—dubbo应用注册到zk的地址 -->
  15. <dubbo:registry address="zookeeper://127.0.0.1:2181" />
  16. 备注2:应用名称+zookeeper注册地址,让普通应用变成一个dubbo应用。
  17. <!-- 用dubbo协议在20880端口暴露服务 -->
  18. <dubbo:protocol name="dubbo" port="20880" />
  19. 备注3:如果该dubbo应用想成为服务端,那么配置一个dubbo协议的端口。
  20. <!-- 服务端声明需要对外开放提供服务的接口 -->
  21. <dubbo:service interface="test.dubbo.itf.IpersonInfoService"
  22. protocol="dubbo" ref="personInfoService" />
  23. <!-- 服务端实现接口的bean -->
  24. <bean id="personInfoService"
  25. class="test.dubbo.impl.PersonInfoServiceImpl" />
  26. 备注4:服务端声明需要对外开放的服务接口,并且对接口关联一个实现类。
  27. </beans>

复制代码

(5)编写一个启动类StartDubboServer,启动这个dubbo应用。

复制代码

  1. import com.alibaba.dubbo.container.Main;
  2. public class StartDubboServer {
  3. public static void main(String[] args) {
  4. /**
  5. * 通过dubbo的启动程序,自动加载一个spring的context文件;
  6. * 配置文件默认指定路径为 resources/META-INF/spring/applicationContext.xml
  7. */
  8. Main.main(null);
  9. }
  10. }

复制代码

  通过dubbo提供的一个容器启动工具Main,可以启动一个spring的context容器,并且如果不指定配置文件的话,会自动找如下路径的文件:resources/META-INF/spring/applicationContext.xml

找到spring的xml文件后,容器加载dubbo相关配置,自动启动一个dubbo服务。启动成功后效果如下:

1076786-20161207164516726-849713073.png

  这个时候,从zookeeper中可以看到,dubbo节点自动创建,并且有一个接口服务已经登记到了dubbo节点下,此时:该接口节点下面consumer节点为空,没有消费方,provider节点有1个子节点,就是我们刚刚启动的那个服务端:

1076786-20161207164530491-1631322495.png

至此,一个dubbo的服务端已经成功创建完成。

4.编写person-client 客户端

工程参考结构如下:

1076786-20161207164538179-1301872810.png

(1)创建person-center的maven工程,默认打包方式为jar包。

(2)在pom文件中需要添加的核心依赖和服务端一模一样,不过person-interface这个jar包的依赖是用于接口的调用;

(3)编写一个使用person-interface接口的代码,person-interface接口通过注入的方式引用:

复制代码

  1. @Service
  2. public class CheckPersonStatus {
  3. /**
  4. * 这个bean的实现来源于dubbo的服务
  5. */
  6. @Autowired
  7. private IpersonInfoService personInfoService;
  8. public String checkAllPersonStatus() {
  9. return personInfoService.queryPersonInfoAll();
  10. }
  11. public String checkPersonStatusByPersonNumber(String personNumber) {
  12. return personInfoService.queryPersonInfoByNumber(personNumber);
  13. }
  14. }

复制代码

(4)把这个person-center工程配置为一个dubbo的应用。

复制代码

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://code.alibabatech.com/schema/dubbo
  8. http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
  9. 备注1:引入dubbo配置相关的xml的命名空间
  10. <!-- 自动扫描注解:通过dubbo实现 -->
  11. <dubbo:annotation package="test.dubbo.*" />
  12. <!-- 必须加上:dubbo应用的名称 -->
  13. <dubbo:application name="person-client" />
  14. <!—dubbo应用注册的zk地址 -->
  15. <dubbo:registry address="zookeeper://127.0.0.1:2181" />
  16. 备注2:应用名称+zookeeper注册地址,让普通应用变成一个dubbo应用。
  17. <!-- 注册需要使用的dubbo服务,通过interface指定服务接口 -->
  18. <dubbo:reference id="personInfoService" interface="test.dubbo.itf.IpersonInfoService"
  19. timeout="10000" check="false" />
  20. 备注2:dubbo应用作为一个服务使用者,只需要声明需要的接口服务即可。
  21. </beans>

复制代码

所有dubbo的应用都需要:

a、 添加dubbo应用的名称;

b、 添加dubbo应用注册的zookeeper的地址;

对于dubbo的应用:作为一个服务端:

a、添加对外开放的端口号;

b、添加需要开放的接口以及实现;

对于dubbo的应用:作为一个客户端端:

a、添加需要使用的接口;


(5)启动这个dubbo应用,为了使用方便,通过applicationContext进行spring的启动:


复制代码

  1. public class MainClientRunner {
  2. public static void main(String[] args) {
  3. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
  4. "META-INF/spring/applicationContext.xml");
  5. /**
  6. * 启动spring容器,同时也会启动dubbo的客户端
  7. */
  8. context.start();
  9. /**
  10. * 获取bean
  11. */
  12. CheckPersonStatus checkPersonStatus = (CheckPersonStatus) context.getBean("checkPersonStatus");
  13. /** 调用checkPersonStatus,checkPersonStatus调用了dubbo服务 **/
  14. String allInfo = checkPersonStatus.checkAllPersonStatus();
  15. String oneInfo = checkPersonStatus.checkPersonStatusByPersonNumber("12345678");
  16. System.out.println(allInfo);
  17. System.out.println(oneInfo);
  18. try {
  19. Thread.sleep(3600 * 24);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. /**
  24. * 关闭spring的容器
  25. */
  26. context.close();
  27. }
  28. }

复制代码

至此为止,一个dubbo的客户端已经完成。

客户端运行效果如下:

1076786-20161207164747054-569578436.png

这个时候,服务端的显示如下:

1076786-20161207164751413-675441493.png

说明客户端调用的dubbo服务,服务的实现,是在dubbo服务端完成的。

Zookeeper中的dubbo节点如下显示:

1076786-20161207164756819-1388520059.png

可以看出,此时,该接口增加了一个消费者。

示例2:dubbo集群实验

Dubbo服务的分布式主要体现在两个方面:

(1) 多个dubbo应用,可以为同一个接口提供服务。

这个意思就是说,我们编写了一个dubbo应用person-center,对外提供了一个test.dubbo.itf.IpersonInfoService这个接口,那么我们可以部署任意多个person-center服务端并且配置为同一套zookeeper环境,所有的服务端,都会注册到这个zookeeper中,也就意味着有多个provider提供了这个接口服务。直接避免了单点故障,而且能够线性提高接口服务的性能。

(2) 复杂业务系统,拆分成多个基于dubbo服务的业务子系统。

比如对于一个业务系统,可以拆分成多个子系统,子系统对外通过dubbo进行服务交互,实现功能上的分布式构建,降低系统耦合性,而且可以通过多节点,对每个功能实现集群构建。

1. 多节点dubbo服务实验

(1)给person-center这个服务端添加一个调用次数统计的代码:

1076786-20161207164820163-1655060527.png

通过统计调用次数,可以观察多个服务提供方的负载均衡情况。

(2)用过eclipse启动person-center服务端,此时配置文件中,提供服务的端口号为20880。

1076786-20161207164826226-1853743154.png

(3) 分别修改端口号为20881和20882之后再次启动服务端。这样,person-center这个服务端已经启动了三个节点,从zookeeper中能看到:

1076786-20161207164834116-511722936.png

也就是说,对于接口test.dubbo.itf.IPersonInfoService具有三个服务提供方同时提供。

(4)客户端调用:客户端通过一个for循环持续调用这个接口:

1076786-20161207164847038-118827374.png

  1. 启动客户端之后,客户端会循环调用服务端1000次,这个时候,我们观察每一个服务端的调用统计情况:

1076786-20161207164900522-1554011249.png

当客户端停止调用时,三个服务端分别被调用了338、335、327次,说明,三个节点的调用基本上是负载均衡。

  1. 也就是说,dubbo的服务端集群的负载均衡是在客户端完成的,对于服务端来说是没有感知,集群中每个节点之间是透明的,不存在类似zookeeperleaderfollower的概念。

示例3:dubbo构建REST服务

1. 完善person-interface接口包

之前我们创建的person-interface是一个纯粹的接口代码包,只能作为一个普通的接口,现在我们要把这个接口改造成为可以额外开放成为REST服务的一个接口包。

(1)pom文件中添加2个依赖javax.ws.rs-api和dubbo:

  1. <dependency>
  2. <groupId>javax.ws.rs</groupId>
  3. <artifactId>javax.ws.rs-api</artifactId>
  4. <version>2.0</version>
  5. </dependency>

这个依赖主要是定义了REST服务接口的注解。

Dubbo的具体依赖和上文是一样的。

(3) 改造接口:

代码中黄色背景部分是新增内容,下面对新增内容一一介绍如下:

复制代码

  1. package test.dubbo.itf;
  2. import javax.ws.rs.Consumes;
  3. import javax.ws.rs.GET;
  4. import javax.ws.rs.POST;
  5. import javax.ws.rs.Path;
  6. import javax.ws.rs.PathParam;
  7. import javax.ws.rs.Produces;
  8. import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType;
  9. /**
  10. *
  11. * @author ShengGang 人员数据操作接口
  12. *
  13. */
  14. @Path("personInfoService")
  15. @Consumes({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
  16. @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
  17. public interface IpersonInfoService {
  18. /**
  19. * 查询全部人员信息
  20. * @return
  21. */
  22. @GET
  23. @Path("queryPersonInfoAll")
  24. public String queryPersonInfoAll();
  25. /**
  26. * 根据人员编号查询人员信息
  27. * @return
  28. */
  29. @POST
  30. @Path("queryPersonInfoByNumber/{personNumber}")
  31. public String queryPersonInfoByNumber(@PathParam("personNumber") String personNumber);
  32. }

复制代码

“@Path”,类似于webMVC中的@RequestMapping, @Path(“personInfoService”)放在了接口类上面,当访问这个接口的方法时,相对的路径则是/personInfoService,也就是说,当这个接口开放为REST服务的时候,请求url就是 http://localhost:9090/personInfoService/xxx;

“@Consumes”、“@Produces”,分别指定这个接口的消费方和请求需要接收和发送哪种类型的数据,ContentType里面可以选择JSON或者是XML。如果是JSON,那么REST框架会自动进行对象和JSON格式的转换。

“@Get”、“@Post”,指定具体的接口方法暴露为REST服务的时候是Get请求还是Post请求;

“@Path(“queryPersonInfoByNumber/{personNumber}“)”,这个就是标准的REST风格的URL的定义方法。请求的参数是放到URL中,{personNumber}是指,这个路径会对应着这个方法的参数名。如果personNumber为11000123,那么请求的路径就是

http://localhost:9090/personInfoService/queryPersonInfoByNumber/11000123

“@PathParam(“personNumber”)”,这个注解是可选的,可以指定这个参数的别名。

2. 改造person-center服务端

这个服务端的改造比interface接口包改造要简单多,在之前的配置文件基础上额外增加二个配置即可:开放一个REST服务端口,让之前的接口支持REST协议。

(1) pom文件中添加如下依赖。

复制代码

  1. <!-- 通过dubbo提供rest服务,需要引入resteasy框架 -->
  2. <dependency>
  3. <groupId>org.jboss.resteasy</groupId>
  4. <artifactId>resteasy-client</artifactId>
  5. <version>3.0.7.Final</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.mortbay.jetty</groupId>
  9. <artifactId>jetty</artifactId>
  10. <version>6.1.26</version>
  11. </dependency>
  12. <!-- 数据bean的校验框架 -->
  13. <dependency>
  14. <groupId>org.glassfish.hk2.external</groupId>
  15. <artifactId>bean-validator</artifactId>
  16. <version>2.4.0</version>
  17. </dependency>

复制代码

这些依赖的作用分别是

resteasy-client:提供REST服务的封装;

jetty:开放HTTP服务;

bean-validator:校验入参和出参的bean;

(2) 在配置文件中添加额外添加一些配置:

1076786-20161207165145491-1101489702.png

标记的第一行意思是,这个dubbo应用开放了REST协议,并且端口号为9090。开放的http服务是通过jetty完成的,“contextpath”则是指定了根路径。

如果需要调用REST服务,则请求路径为:

http://localhost:9090/**service**/personInfoService/queryPersonInfoAll

  1. 标记的第二行意思是,这个接口会同时支持dubborest两种协议,既可以按照一般的dubbo的调用方式去使用这个接口,也可以通过HTTP进行REST服务的调用。
  2. 最简单的验证REST服务是否正确开放的方法是,直接通过浏览器去访问开放的REST服务地址:
  3. 访问GET请求的接口:

1076786-20161207165228804-1677861735.png

  1. GET请求在浏览器中响应正常。
  2. 访问POST请求的接口:

1076786-20161207165241819-1939184743.png

  1. POST请求在浏览器中响应为空,控制台会提示405,意思是该URL只会接收POST请求,而不会接收GET请求。

示例4:dubbo服务使用数据库

  1. 主要改造的工程为person-center服务端,让这个dubbo服务端真正连接数据库,通过接口返回数据库数据。

1. 添加maven依赖

复制代码

  1. <!-- 引入spring的jdbc -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-jdbc</artifactId>
  5. <version>${org.springframework-version}</version>
  6. </dependency>
  7. <!-- 使用阿里的druid数据源 -->
  8. <dependency>
  9. <groupId>com.alibaba</groupId>
  10. <artifactId>druid</artifactId>
  11. <version>0.2.8</version>
  12. <exclusions>
  13. <exclusion>
  14. <artifactId>tools</artifactId>
  15. <groupId>com.alibaba</groupId>
  16. </exclusion>
  17. <exclusion>
  18. <artifactId>jconsole</artifactId>
  19. <groupId>com.alibaba</groupId>
  20. </exclusion>
  21. </exclusions>
  22. </dependency>
  23. <!-- mysql的连接器 -->
  24. <dependency>
  25. <groupId>mysql</groupId>
  26. <artifactId>mysql-connector-java</artifactId>
  27. <version>5.1.21</version>
  28. </dependency>

复制代码

在person-center项目的pom.xml中,添加如下maven依赖:

其中:

spring-jdbc.jar提供了jdbcTemplate的数据库操作api;

druid.jar为阿里的数据源;

mysql-connector-java.jar为mysql的java连接驱动;

2. 修改applicationContext.xml文件

修改person-interface下面的META-INF/spring/applicationContext.xml文件,添加如下bean的配置:

复制代码

  1. <!-- 指定spring需要加载的配置文件 -->
  2. <bean
  3. class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  4. <property name="location" value="classpath:config.properties" />
  5. </bean>
  6. <!-- 采用druid的数据源 -->
  7. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
  8. init-method="init" destroy-method="close">
  9. <property name="url" value="${jdbc.url}" />
  10. <property name="username" value="${jdbc.username}" />
  11. <property name="password" value="${jdbc.password}" />
  12. <property name="initialSize" value="${jdbc.initialSize}" />
  13. <property name="maxActive" value="${jdbc.maxActive}" />
  14. <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
  15. <property name="validationQuery" value="${jdbc.validationQuery}" />
  16. </bean>
  17. <!-- jdbcTemplate -->
  18. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  19. <property name="dataSource" ref="dataSource" />
  20. </bean>

复制代码

和之前的spring文件相比,主要添加了三个bean:

(1) PropertySourcesPlaceholderConfigurer:这个bean指定了工程路径里面的resources/config.properties文件,那么spring的xml文件中所有的属性配置都通过配置文件去读取和修改;

(2) DataSource:配置了一个数据源,指定了url、username和passwd等数据库连接相关配置;

(3) jdbcTemplate:由spring提供的一个jdbc操作API,可以通过这个实例轻松进行数据库增删改查等操作;

3. 创建dao层

(1) 在工程中添加一个dao的package,用于编写dao层,工程结构如下:

1076786-20161207165345522-171725736.png

(2)编写RowMapper类

由于没有采用Hibernate等持久层框架,而是直接采用的jdbcTemplate操作数据库,因此并没有把数据库字段和实体类PersonInfo进行一一映射。所以,我们需要编写一个RowMapper类把数据库的数据字段一一映射成为PersonInfo的字段。该类主要继承了spring的RowMapper接口,代码如下:

复制代码

  1. package test.dubbo.dao.wrapper;
  2. import java.sql.ResultSet;
  3. import java.sql.SQLException;
  4. import org.springframework.jdbc.core.RowMapper;
  5. import test.dubbo.entity.PersonInfo;
  6. public class PersonInfoRowMapper implements RowMapper<PersonInfo> {
  7. @Override
  8. public PersonInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
  9. PersonInfo info = new PersonInfo();
  10. info.setPersonNumber(rs.getString("person_number"));
  11. info.setPersonAddr(rs.getString("person_address"));
  12. info.setPersonName(rs.getString("person_name"));
  13. info.setPersonPhoneNumber(rs.getString("person_phone_number"));
  14. info.setPersonStatus(rs.getString("person_status"));
  15. return info;
  16. }
  17. }

复制代码

(3) 编写PersonInfoDao类

复制代码

  1. package test.dubbo.dao;
  2. import java.util.List;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.jdbc.core.JdbcTemplate;
  5. import org.springframework.stereotype.Repository;
  6. import test.dubbo.dao.wrapper.PersonInfoRowMapper;
  7. import test.dubbo.entity.PersonInfo;
  8. @Repository
  9. public class PersonInfoDao {
  10. @Autowired
  11. private JdbcTemplate jdbcTemplate;
  12. public PersonInfo queryPersonInfo(String personNumber) {
  13. String sql = "select * from person_info where person_number = ?";
  14. return jdbcTemplate.queryForObject(sql, new PersonInfoRowMapper(), personNumber);
  15. }
  16. public List<PersonInfo> queryPersonInfoList() {
  17. String sql = "select * from person_info";
  18. return jdbcTemplate.query(sql, new PersonInfoRowMapper());
  19. }
  20. }

复制代码

PersonInfoDao使用了jdbcTemplate,从数据库进行读写操作,代码如下:

这里主要通过jdbcTemplate执行了queryForObject和query两个操作,因为我们用了之前编写的继承了RowMapper接口的PersonInfoRowMapper类,因此,查询结果直接返回成了PersonInfo对象。

4. 修改Service层的实现类

主要参考代码:

复制代码

  1. @Service
  2. public class PersonInfoServiceImpl implements IpersonInfoService {
  3. private static final AtomicInteger count = new AtomicInteger(0);
  4. @Autowired
  5. private PersonInfoDao personInfoDao;
  6. private static final String ERROR_INFO = "{\"errorCode\":\"-1\"}";
  7. @Override
  8. public String queryPersonInfoAll() {
  9. System.out.println("===================================");
  10. System.out.println("接口实现:queryPersonInfoAll()");
  11. List<PersonInfo> infoList = personInfoDao.queryPersonInfoList();
  12. String rtnInfo;
  13. try {
  14. rtnInfo = JSON.json(infoList);
  15. } catch (IOException e) {
  16. rtnInfo = ERROR_INFO;
  17. e.printStackTrace();
  18. }
  19. System.out.println("===================================");
  20. return rtnInfo;
  21. }
  22. @Override
  23. public String queryPersonInfoByNumber(String personNumber) {
  24. count.incrementAndGet();
  25. System.out.println("===================================");
  26. System.out.println("接口实现:queryPersonInfoByNumber(String personNumber)");
  27. System.out.println("接口被调用了"+count.get()+"次。");
  28. PersonInfo personInfo = personInfoDao.queryPersonInfo(personNumber);
  29. String rtnInfo;
  30. try {
  31. rtnInfo = JSON.json(personInfo);
  32. } catch (IOException e) {
  33. rtnInfo = ERROR_INFO;
  34. e.printStackTrace();
  35. }
  36. System.out.println("===================================");
  37. return rtnInfo;
  38. }
  39. }

复制代码

代码注意事项如下:

(1) service层注入了personInfoDao这个实例;

(2) 通过调用personInfoDao中的方法,获取到PersonInfo对象以及对象的List,但是我们需要把对象转成json格式,这里采用阿里的JSON包—-

com.alibaba.dubbo.common.json.*

到此为止,dubbo服务端使用数据库部分已经改造完成。

示例5:通过dubbo构建复杂业务场景

1. 模拟的场景结构图

1076786-20161207170217538-519395844.png

这里,作为模拟场景,我们有两个dubbo服务的提供方,person-center和product-center,这两个dubbo应用分别表示两个独立的系统,但是都注册到同一套zookeeper注册中心。order-system-service表示一个第三方综合系统,会使用person-center和product-center两个系统的接口,同时,自己会对外开放自己的dubb/REST接口。

  1. 具体的代码可以参考附件内容,里面所有的配置都是之前已经提到过的内容。

其他:常见异常问题及解决

1. 启动dubbo服务端后连接zookeeper出现如下异常:

复制代码

  1. INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.name=Windows 7
  2. INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.arch=amd64
  3. INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.version=6.1
  4. INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.name=zhaijj
  5. INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.home=C:\Users\zhaijj
  6. INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.dir=D:\workdir\person-center
  7. INFO : org.apache.zookeeper.ZooKeeper - Initiating client connection, connectString=192.168.159.5:2181 sessionTimeout=60000 watcher=org.I0Itec.zkclient.ZkClient@2a2b2bd1
  8. INFO : org.apache.zookeeper.ClientCnxn - Opening socket connection to server /192.168.159.5:2181
  9. INFO : org.I0Itec.zkclient.ZkEventThread - Terminate ZkClient event thread.
  10. INFO : org.apache.zookeeper.ClientCnxn - Socket connection established to 192.168.159.5/192.168.159.5:2181, initiating session
  11. INFO : org.apache.zookeeper.ClientCnxn - Session establishment complete on server 192.168.159.5/192.168.159.5:2181, sessionid = 0x158ced3a4380008, negotiated timeout = 40000
  12. INFO : org.apache.zookeeper.ZooKeeper - Session: 0x158ced3a4380008 closed
  13. INFO : org.apache.zookeeper.ClientCnxn - EventThread shut down
  14. org.I0Itec.zkclient.exception.ZkTimeoutException: Unable to connect to zookeeper server within timeout: 5000
  15. at org.I0Itec.zkclient.ZkClient.connect(ZkClient.java:876)
  16. at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:98)
  17. at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:92)
  18. at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:80)
  19. at com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient.<init>(ZkclientZookeeperClient.java:26)

复制代码

  然而,zookeeper是正常的,排除任何网络、防火墙原因。

  原因:zkClient客户端版本过低,配置的是0.1,引用的zookeeper的jar包是3.3.3,然而服务端的zookeeper版本比较高,达到3.4.6。

  解决方式:升高zkClient的版本:

  1. <dependency>
  2. <groupId>com.101tec</groupId>
  3. <artifactId>zkclient</artifactId>
  4. <version>0.9</version>
  5. </dependency>

这个最高版本的zkClient用的是zookeeper-3.4.8.jar 。更换包后,正常运行。

发表评论

表情:
评论列表 (有 0 条评论,282人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Dubbo使用入门

    项目工程结构: 建议采用同一个父工程的多个模块module. 服务提供者包括api和provider. api:存放服务接口(interface)和服务模型(pojo,输