文章目录
  1. 1. 工程介绍
    1. 1.1. 服务提供方wechat-service-consumer-dubbo
      1. 1.1.1. 1. Pom.xml
      2. 1.1.2. 2. 服务接口定义,可以单独发布到common包,并由服务提供方和调用方都依赖
      3. 1.1.3. 3. 服务接口实现类,通过@Service注解配置其为dubbo服务
      4. 1.1.4. 4. 配置文件
    2. 1.2. 服务消费方wechat-service-consumer-dubbo
      1. 1.2.1. 1. pom.xml
      2. 1.2.2. 2. 服务接口定义,同服务提供方
      3. 1.2.3. 3. 服务消费方配置
      4. 1.2.4. 4. 业务逻辑开发,可以是任意形式,这里用一个controller进行调用,其效果就如同进行本地的服务调用
      5. 1.2.5. 5. 服务消费方的配置文件
    3. 1.3. 服务注册中心的搭建
    4. 1.4. 进行测试
    5. 1.5. 附录:dubbo管控台的使用
    6. 1.6. 注意

本文主要讲解如何使用SpringBoot框架配合Spring-Boot-starter-dubbo实现hlRPC调用。Springboot框架大家基本都有所认知,在此就不多做讲述,文章开始简单介绍一下Dubbo框架。

Dubbo是阿里巴巴出品的一款分布式的SOA服务治理框架,致力于提供高性能和透明化的RPC(Remote Procedure Call Protocol)远程调用的解决方案,以及SOA服务治理方案。

Dubbo框架基本的架构如下
Dubbo框架基本的架构

可见,其由四部分组成,分别是

Registry: 注册中心
Consumer: 服务消费方
Provider: 服务提供方
Monitor: 服务监控方

其中前三者是必需提供的,在生产环境中,服务注册中心一般使用zookeeper。

更多的Dubbo框架的信息可以参考官方文档 https://dubbo.gitbooks.io/dubbo-user-book/,该框架在停滞一段时间之后又开始了更新,加上国内使用该框架的公司数目也很客观,因此不必担心社区的热度会下降。

工程介绍

本文的业务基于之前的微信凭证查询业务进行,分为服务提供方和服务消费方两部分,基于springboot1.4.3。并在文章最后介绍一下利用Dubbo管控台进行服务的治理。

服务提供方wechat-service-consumer-dubbo

服务提供方程序结构图

1. Pom.xml

主要引入spring-boot-dubbo-starter,实现的很巧妙只用了四个Java配置类就将Dubbo与springboot整合到一起。

<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.hispeed.microservice</groupId>
    <artifactId>wechat-pz-service-provider-dubbo</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.3.RELEASE</version>
    </parent>

    <properties>
        <dubbo-spring-boot>1.0.0</dubbo-spring-boot>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.7</java.version>
    </properties>

    <dependencies>
        <!-- wechat-pz-common-core -->
        <dependency>
            <groupId>com.hispeed.microservice</groupId>
            <artifactId>wechat-pz-common-core</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!-- spring-boot-start-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring-boot-actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- spring-cloud-sleuth-zipkin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        </dependency>
        <!-- spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- spring-boot-devtools 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>-->
        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- oracle-driver -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>14.0.0</version>
        </dependency>
        <!-- spring-boot-starter-dubbo -->
        <dependency>
            <groupId>io.dubbo.springboot</groupId>
            <artifactId>spring-boot-starter-dubbo</artifactId>
            <version>${dubbo-spring-boot}</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2. 服务接口定义,可以单独发布到common包,并由服务提供方和调用方都依赖

定义如下

public interface WeChatPzService {
    /**根据订单号和凭证类型微信凭证实体*/
    public List<WeChatPzInfoVobj> getPzInfoVobj(String order_id, String pz_type) throws NullPointerException;
    /**新增微信凭证实体*/
    public boolean insertPzInfo(WeChatPzInfoVobj weChatPzInfoVobj);
    /**检测是否重复插入/
    public boolean isPzInfoExist(String hf_id);
}

3. 服务接口实现类,通过@Service注解配置其为dubbo服务

@Service(version = "1.0.0")
public class WeChatPzServiceImpl implements WeChatPzService {       
    private static final Logger LOGGER = LoggerFactory.getLogger(WeChatPzServiceImpl.class);
    @Autowired
    private WeChatPzMapper weChatPzMapper;
    @Override
    public List<WeChatPzInfoVobj> getPzInfoVobj(String order_id, String pz_type) throws NullPointerException {
        LOGGER.info("业务层进行处理,将[查询参数]传递给服务持久层");
        List<WeChatPzInfoVobj> weChatPzInfoVobjs = this.weChatPzMapper.getPzInfoVobj(order_id, pz_type);
        if (weChatPzInfoVobjs.size() == 0 || weChatPzInfoVobjs == null) {
            LOGGER.info("业务层得到的查询实体信息为空");
        }
        LOGGER.info("业务层得到的查询实体信息为:List<WeChatPzInfoVobj>=" + weChatPzInfoVobjs.toString());
        return weChatPzInfoVobjs;
    }
    @Override
    public boolean insertPzInfo(WeChatPzInfoVobj weChatPzInfoVobj) {
        LOGGER.info("业务层进行处理,将[插入数据 ]传递给服务持久层");
        int count = this.weChatPzMapper.insertPzInfo(weChatPzInfoVobj);
        if (count > 0) {
            LOGGER.info("数据持久层处理完毕,hf_id=" + weChatPzInfoVobj.getHfId() + "添加成功");
            return true;
        }
        LOGGER.info("数据持久层处理完毕,hf_id=" + weChatPzInfoVobj.getHfId() + "添加失败");
        return false;
    }
    @Override
    public boolean isPzInfoExist(String hf_id) {
        WeChatPzInfoVobj weChatPzInfoVobj = this.weChatPzMapper.getWeChatPzByHfId(hf_id);
        // 通过话费流水号能够查到对应的条目,说明已经添加,返回true
        // 否则返回false
        if (weChatPzInfoVobj != null) {
            LOGGER.info("数据持久层处理完毕,hf_id=" + weChatPzInfoVobj.getHfId() + "的凭证信息存在,具体的信息为:" + weChatPzInfoVobj.toString());
            return true;
        }
        return false;
    }
}

核心就是@Service 注解标识为 Dubbo 服务,并通过 version 指定了版本号。

com.hispeed.wechat.pz.mapper.WeChatPzMapper是通过mybatis的注解方式进行数据库的访问。具体的代码感兴趣可以自行查看。

4. 配置文件

server.port=8082
/#Dubbo
spring.dubbo.application.name=wechat_pz_service_provider_dubbo应用名称
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181        注册中心地址
spring.dubbo.protocol.name=dubbo                          协议名称
spring.dubbo.protocol.port=20880                           协议端口
spring.dubbo.scan=com.hispeed.wechat.pz.service              服务类包目录
/#datasource
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@192.168.63.90:1521:testdb
spring.datasource.username=esales26
spring.datasource.password=esales26
/#eureka-client
spring.application.name=wechat-pz-service-provider-dubbo
eureka.client.serviceUrl.defaultZone=http://192.168.66.40:8761/eureka
eureka.instance.prefer-ip-address=true
/#interface version
wechat.pz.inter.version=v1.0

这里就解释一个地方,spring.dubbo.scan表示要扫描作为服务的类路径,为了方便就将service包都扫描进去了。

服务消费方wechat-service-consumer-dubbo

1. pom.xml

服务消费方程序结构图

2. 服务接口定义,同服务提供方

为了避免将代码到处复制粘贴,引起代码的不一致,再次建议将公共代码封装为一个公共依赖。

3. 服务消费方配置

通过@Reference引用远程服务提供者,发起服务的调用

@Component
public class WeChatPzDubboServiceImpl {

    @Reference(version = "1.0.0")
    WeChatPzService weChatPzService;

    public List<WeChatPzInfoVobj> getPzInfoVobj(String order_id, String pz_type)
            throws NullPointerException {
        return weChatPzService.getPzInfoVobj(order_id, pz_type);
    }

    public boolean insertPzInfo(WeChatPzInfoVobj weChatPzInfoVobj) {
        return weChatPzService.insertPzInfo(weChatPzInfoVobj);
    }

    public boolean isPzInfoExist(String hf_id) {
        return weChatPzService.isPzInfoExist(hf_id);
    }

}

这里我们定义了一个service 用于调用远程的接口,通过@Reference(version = “1.0.0”) 引用远程服务。通过该注解,订阅该接口版本为 1.0.0 的 Dubbo 服务。

同时我们把WeChatPzDubboServiceImpl这个bean添加到spring的bean容器中,便于后续的调用。

4. 业务逻辑开发,可以是任意形式,这里用一个controller进行调用,其效果就如同进行本地的服务调用

这里我就截取一部分代码,更详细的代码请参考wechat-pz-service-consumer-dubbo工程下的com.hispeed.wechat.pz.controller.WeChatDubboController类。
@RestController
public class WeChatDubboController {

private static final Logger LOGGER = LoggerFactory.getLogger(WeChatDubboController.class);

@Autowired
WeChatPzDubboServiceImpl iWeChatPzService;    

@Value("${wechat.pz.inter.version}")
private String interVersion;                    // 接口版本号

private ResponseParam<WeChatPzInfoVobj> responseParam;    // 封装非正常情况返回信息     

/**
 * 根据查询条件或许微信凭证
 * @param <T>
 * @return ResponseParam
 */
@GetMapping("pzinfo/select")
public ResponseParam<WeChatPzInfoVobj> getDataByParams(
        @RequestParam("order_id") String order_id,
        @RequestParam("pz_type") String pz_type,
        @RequestParam("version") String version) {

    LOGGER.info("该接口版本号为:version=" + interVersion);
    LOGGER.info("接收到的参数列表为:" + " order_id=" + order_id + ",pz_type=[" + pz_type + "],version=" + version); 

    responseParam = new ResponseParam<WeChatPzInfoVobj>();

    // 请求参数不完整或存在空值
    if (StringUtils.isEmpty(order_id) || StringUtils.isEmpty(pz_type) || StringUtils.isEmpty(version)) {
        responseParam.setData(null);
        responseParam.setReturnCode(SelectCodeEnum.INVALID_PARAM.getCode());
        responseParam.setDetail(SelectCodeEnum.INVALID_PARAM.getDetail());
        return responseParam;
    }  

5. 服务消费方的配置文件

server.port=8083
/# name of consumer
spring.application.name=wechat-pz-service-consumer-dubbo
/# eureka url
eureka.client.serviceUrl.defaultZone=http://192.168.66.40:8761/eureka
eureka.instance.prefer-ip-address=true
/# interface version
wechat.pz.inter.version=v1.0
/# dubbo
spring.dubbo.application.name=wechat_pz_service_consumer_dubbo
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.scan=com.hispeed.wechat.pz.service

spring.dubbo.scan扫描本消费者工程中引用了远程服务的包。

服务注册中心的搭建

服务注册中心使用zookeeper,这也是官方推荐的注册中心组件,还可以使用redis,不过生产环境还是zookeeper更可靠。
本文使用的是zookeeper-3.4.9,下载一份并解压,进入zookeeper-3.4.9\conf下,重命名zoo_sample.cfg为zoo.cfg,打开进行编辑。

核心配置项如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=d:/data/zookeeper
clientPort=2181
server.1=127.0.0.1:2888:3888

dataDir下的路径需要自己创建,创建之后,在该路径下创建一个无扩展名的文本文件myid,在其中写入写入一个编号,如1。在zk集群部署时生效。

由于本文是测试,只需要启动一台zk即可。
在zookeeper-3.4.9\bin启动zkServer.cmd,linux下为zkCli.sh,别忘记授予可执行权限。

更多的zk相关的资料,可以参考阅读《从Paxos到zookeeper分布式一致性原理与实践》

进行测试

  1. 启动zookeeper服务端,在zookeeper-3.4.9\bin启动zkServer.cmd,双击运行即可
  2. 启动服务提供方,运行wechat-pz-service-provider-dubbo下的启动类com.hispeed.wechat.pz.WeChatServiceDubboProviderBootstrap
  3. 可以在zk控制台看到如下日志输出

    2017-09-14 15:53:40,850 [myid:] - INFO  [ProcessThread(sid:0 cport:2181)::PrepRequestProcessor@649]
    - Got user-level KeeperException when processing sessionid:0x15e79be352d000f type:create cxid:0x7 zx
    id:0xe3b txntype:-1 reqpath:n/a Error Path:/dubbo/com.hispeed.wechat.pz.service.WeChatPzService/conf
    igurators Error:KeeperErrorCode = NodeExists for /dubbo/com.hispeed.wechat.pz.service.WeChatPzServic
    e/configurators
    

表明服务已注册到zk

  1. 启动服务消费方,调用消费方暴露的http接口,通过RPC方式调用服务提供方的服务。
    REST测试工具中访问接口
    服务调用返回结果图
  2. 服务消费方日志
    服务消费方日志
  3. 服务提供方日志:
    服务提供方日志

可以看到,两个结合在一起完成了一次完整的服务调用,服务消费方只对外暴露接口,对内通过Dubbo框架的RPC方式进行服务调用。而springCloud则是另外的一种方式,每个服务都暴露了Http接口,服务间通过Httpclient方式进行服务的调用。只能说各有千秋吧。

到这里,我们就开发并测试完成基于springboot 的dubbo服务的发布和调用。

附录:dubbo管控台的使用

Dubbo-admin管控台是官方提供的用于进行服务管理和控制的单独的一个web应用,有两种方式获取

  1. 直接在网络上下载编译好的二进制war包,但不是最好的方式,可能会由于jdk版本的问题造成启动失败。链接:http://www.pc0359.cn/downinfo/58773.html
  2. 更推荐的方式还是下载源码编译安装
  3. 从官方github下载最新的源码,我下载的是2.5.3,目前官方更新到了2.5.4,在管控台上功能变化不大。安装方法是一样的。
  4. 源码下载地址:https://github.com/alibaba/dubbo
  5. 解压源码包,在项目根路径下执行 mvn –clean –DskipTests install 跳过测试并安装
  6. 等待片刻即可安装成功,jdk版本至少在1.6以上

  7. 解压一份儿和jdk版本匹配的tomcat,我用的是tomcat7,jdk版本jdk7,从dubbo-admin\target下获取dubbo-admin-2.8.4.war,放置在tomcat的webapp/ROOT下,删除原有的文件。

  8. 启动tomcat,这里可能需要设置server.xml中的端口防止端口冲突

  9. 启动完成,在浏览器访问,我的链接是http://localhost:8088/
    这里会提示输入密码,默认为root/root
    登录成功可以看到如下界面
    7.png

  10. Dubbo-admin提供了基本的服务治理功能,点击服务治理-服务,可以看到我们发布的服务提供者
    8.png
    点击进入可以对其做一些动态的设置,包括权重、是否启用、对其消费者进行动态设置、路由配置、访问控制、负载均衡等。
    9.png

基本提供了一个服务治理的控制台,需要更多的其他功能需要自行扩展。
感兴趣可以搭建一套进行测试。

注意

  1. 这里还需要注意的一点是,凡是调用过程中涉及到的实体都需要实现Serializable 接口,并生成serialVersionUID。否则dubbo会报remoteException
  2. 开发工具中的devtools不要使用,否则会提示java.lang.ClassCastException错误
文章目录
  1. 1. 工程介绍
    1. 1.1. 服务提供方wechat-service-consumer-dubbo
      1. 1.1.1. 1. Pom.xml
      2. 1.1.2. 2. 服务接口定义,可以单独发布到common包,并由服务提供方和调用方都依赖
      3. 1.1.3. 3. 服务接口实现类,通过@Service注解配置其为dubbo服务
      4. 1.1.4. 4. 配置文件
    2. 1.2. 服务消费方wechat-service-consumer-dubbo
      1. 1.2.1. 1. pom.xml
      2. 1.2.2. 2. 服务接口定义,同服务提供方
      3. 1.2.3. 3. 服务消费方配置
      4. 1.2.4. 4. 业务逻辑开发,可以是任意形式,这里用一个controller进行调用,其效果就如同进行本地的服务调用
      5. 1.2.5. 5. 服务消费方的配置文件
    3. 1.3. 服务注册中心的搭建
    4. 1.4. 进行测试
    5. 1.5. 附录:dubbo管控台的使用
    6. 1.6. 注意
Fork me on GitHub