美文网首页
Spring Boot 2.0 集成 Dubbo 示例

Spring Boot 2.0 集成 Dubbo 示例

作者: 专职跑龙套 | 来源:发表于2018-06-27 14:27 被阅读619次

    简单介绍下 Dubbo

    http://dubbo.apache.org/

    Apache Dubbo™ (incubating) is a high-performance, java based, open source RPC framework.

    通常情况 , Dubbo 应用有两种使用场景 , 其一为 Dubbo 服务提供方 , 另外一个是 Dubbo 服务消费方。

    Apache Dubbo (incubating) offers three key functionalities:

    • interface based remote call 基于接口的远程调用
    • fault tolerance & load balancing 容错及负载均衡
    • automatic service registration & discovery 自动化服务注册与发现

    Dubbo 的主要的服务注册发现功能便是由 Zookeeper 来提供的。ZooKeeper 是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题。

    Dubbo 架构

    Spring Boot 2.0 集成 Dubbo

    通过 http://start.dubbo.io/ 构造基于 maven 的项目框架,包括服务提供端和服务消费端。

    服务提供端 serviceDemo
    服务消费端 clientDemo

    在 IntelliJ IDEA 中分别打开这两个项目。
    由于在 http://start.dubbo.io/ 只能选择 1.X 版本的 Spring Boot,因此我们需要手动修改两个项目pom.xml文件。参考 https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/README_CN.md

    • 修改 spring-boot-starter-parent 的版本号为 2.0.3.RELEASE
    • 修改 dubbo-spring-boot-starter 的版本号为 0.2.0
    • 添加如下内容:
    <repositories>
        <repository>
            <id>sonatype-nexus-snapshots</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    

    分析服务提供端 serviceDemo

    serviceDemo 项目结构
    application.properties:Dubbo 的基本配置
    dubbo.application.name = dubbo-demo-server
    
    # Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service
    dubbo.scan.basePackages  = com.example
    
    ## RegistryConfig Bean
    dubbo.registry.id = my-registry
    dubbo.registry.address = zookeeper://localhost:2181?client=curator
    
    
    dubbo.application.qosEnable=false
    

    HelloService.java:接口,以后通过接口来进行服务调用

    public interface HelloService {
    
        String sayHello(String name);
    
    }
    

    HelloServiceImpl.java:服务的具体实现

    package com.example;
    
    import java.util.Date;
    
    import com.alibaba.dubbo.config.annotation.Service;
    
    @Service(version = "1.0.0")
    public class HelloServiceImpl implements HelloService {
    
        @Override
        public String sayHello(String name) {
            return "Hello, " + name + ", " + new Date();
        }
    
    }
    

    ServiceDemoApplication.java:程序入口,修改为如下内容

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    @SpringBootApplication
    public class ServiceDemoApplication {
    
    
        public static void main(String[] args) {
            
            // start embedded zookeeper server
            new EmbeddedZooKeeper(2181, false).start();
    
            new SpringApplicationBuilder(ServiceDemoApplication.class)
                    .web(false) // 非 Web 应用
                    .run(args);
        }
        
    }
    

    EmbeddedZooKeeper.java:内嵌的 ZooKeeper 实现。

    分析服务消费端 clientDemo

    clientDemo 项目结构
    application.properties:Dubbo 的基本配置
    # Dubbo Config properties
    ## ApplicationConfig Bean
    dubbo.application.name= dubbo-demo-client
    
    ## RegistryConfig Bean
    dubbo.registry.id = my-registry
    dubbo.registry.address = zookeeper://localhost:2181?client=curator
    
    
    
    
    dubbo.application.qosEnable=false
    

    HelloService.java:接口,以后通过接口来进行服务调用

    public interface HelloService {
    
        String sayHello(String name);
    
    }
    

    ClientDemoApplication.java:程序入口,修改为如下内容

    import com.alibaba.dubbo.config.annotation.Reference;
    import com.example.HelloService;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    import javax.annotation.PostConstruct;
    
    @SpringBootApplication
    public class ClientDemoApplication {
    
        @Reference(version = "1.0.0")
        private HelloService demoService;
    
        public static void main(String[] args) {
            
            new SpringApplicationBuilder(ClientDemoApplication.class)
                    .web(false) // 非 Web 应用
                    .run(args);
        }
        
        @PostConstruct
        public void init() {
            String sayHello = demoService.sayHello("world");
            System.err.println(sayHello);
        }
    }
    

    测试

    分别通过 mvn spring-boot:run 启动两个项目,可以在服务消费端看到如下的日志内容,表明调用成功:

    2018-06-27 14:24:42.828  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Register: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=consumers&check=false&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:42.865  INFO 36771 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn          : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1643fe4a4410001, negotiated timeout = 60000
    2018-06-27 14:24:42.878  INFO 36771 --- [ain-EventThread] o.a.c.f.state.ConnectionStateManager     : State change: CONNECTED
    2018-06-27 14:24:42.916  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Subscribe: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:42.948  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=36354&revision=1.0.0&side=provider&timestamp=1530080308262&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=configurators&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:43.152  INFO 36771 --- [           main] c.a.d.remoting.transport.AbstractClient  :  [DUBBO] Successed connect to server /10.93.67.135:20880 from NettyClient 10.93.67.135 using dubbo version 2.6.2, channel is NettyChannel [channel=[id: 0x580c5d4e, /10.93.67.135:52308 => /10.93.67.135:20880]], dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:43.153  INFO 36771 --- [           main] c.a.d.remoting.transport.AbstractClient  :  [DUBBO] Start NettyClient acbc32941d79.ant.amazon.com/10.93.67.135 connect to the server /10.93.67.135:20880, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:43.227  INFO 36771 --- [           main] com.alibaba.dubbo.config.AbstractConfig  :  [DUBBO] Refer dubbo service com.example.HelloService from url zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=dubbo-demo-client&check=false&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&register.ip=10.93.67.135&remote.timestamp=1530080308262&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 14:24:43.239  INFO 36771 --- [           main] c.a.d.c.s.b.f.a.ReferenceBeanBuilder     : <dubbo:reference object="com.alibaba.dubbo.common.bytecode.proxy0@19da400d" singleton="true" interface="com.example.HelloService" uniqueServiceName="com.example.HelloService:1.0.0" generic="false" version="1.0.0" id="com.example.HelloService" /> has been built.
    Hello, world, Wed Jun 27 14:24:43 CST 2018
    

    将上述的服务注册到 Zookeeper

    在上面的例子是,我们是使用了一个内嵌的 Zookeeper 来注册与发现服务,现在我们启动一个外部的 Zookeeper 服务来替代。

    简单介绍下 Zookeeper,https://zookeeper.apache.org/
    通过如下命令在 mac 中安装 Zookeeper:

    brew info zookeeper
    brew install zookeeper
    

    安装完成后,默认配置文件在 /usr/local/etc/zookeeper/ 中。
    通过命令 zkServer 启动 Zookeeper,默认端口是2181。

    xianch@acbc32941d79 ⮀ /usr/local/etc/zookeeper ⮀ zkServer
    ZooKeeper JMX enabled by default
    Using config: /usr/local/etc/zookeeper/zoo.cfg
    Usage: ./zkServer.sh {start|start-foreground|stop|restart|status|upgrade|print-cmd}
    

    客户端(服务提供者与消费者)与 Zookeeper 是 TCP 长连接,默认对外端口是 2181,通过这个连接,客户端保持和 Zookeeper 服务器的心跳以维护连接,也能向 Zookeeper 发送请求并响应,同时还可以接收到注册通知。

    可以通过命令 zkServer statuszkServer stop 来分别查看和关闭 Zookeeper。

    服务提供端 serviceDemo

    注释掉 ServiceDemoApplication.java 中的 new EmbeddedZooKeeper(2181, false).start();

    启动程序,日志中可以看出,注册了一个服务 dubbo://10.93.67.135:20880/com.example.HelloService

    2018-06-27 14:52:28.053  INFO 41887 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Register: dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=41887&revision=1.0.0&side=provider&timestamp=1530082347373&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    

    注意,每一个服务都需要一个未被使用的 dubbo 端口 ,默认是 20880,也可以通过 spring.dubbo.protocol.port=xxxxx 来配置。

    做一个实验,在配置中修改端口为 spring.dubbo.protocol.port=20881,将实现类 HelloServiceImpl 做一个小的修改:变成 New Hello。然后重新打开一个 Terminal,启动程序。

    public String sayHello(String name) {
            return "New Hello, " + name + ", " + new Date();
        }
    

    此时,通过 zkCli 可以查看 Zookeeper 的信息:可以看出,已经注册了一个服务 com.example.HelloService,它有两个提供者 providers,端口分别是 20880 与 20881。

    xianch@acbc32941d79 ⮀ /usr/local/etc/zookeeper ⮀ zkCli
    Connecting to localhost:2181
    Welcome to ZooKeeper!
    JLine support is enabled
    
    WATCHER::
    
    WatchedEvent state:SyncConnected type:None path:null
    [zk: localhost:2181(CONNECTED) 0] ls
    [zk: localhost:2181(CONNECTED) 1] ls /
    [dubbo, zookeeper]
    [zk: localhost:2181(CONNECTED) 2] ls /dubbo
    [com.example.HelloService]
    [zk: localhost:2181(CONNECTED) 3] ls /dubbo/com.example.HelloService
    [configurators, providers]
    [zk: localhost:2181(CONNECTED) 4] ls /dubbo/com.example.HelloService/configurators
    []
    [zk: localhost:2181(CONNECTED) 5] ls /dubbo/com.example.HelloService/providers
    [
    dubbo%3A%2F%2F10.93.67.135%3A20880%2Fcom.example.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-server%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D50337%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1530089150751%26version%3D1.0.0, 
    dubbo%3A%2F%2F10.93.67.135%3A20881%2Fcom.example.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-server%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D50999%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1530089806995%26version%3D1.0.0
    ]
    
    • 服务提供者在启动的时候,向 Zookeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。
    • 服务消费者启动的时候,订阅 /dubbo/${serviceName}/providers 目录下的提供者 URL 地址, 并向 /dubbo/${serviceName}/consumers 目录下写入自己的 URL 地址。
    • 注意,所有向 Zookeeper 上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。临时节点的生命周期与会话一致,会话关闭则临时节点删除。这个特性经常用来做心跳,动态监控,负载等动作。

    服务消费端 clientDemo

    启动程序,日志中可以看出,订阅了一个服务 consumer://10.93.67.135/com.example.HelloService,并且收到了一个 Notify,告诉它服务提供者的地址。

    2018-06-27 15:32:19.338  INFO 44571 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Subscribe: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 15:32:19.367  INFO 44571 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=41887&revision=1.0.0&side=provider&timestamp=1530082347373&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=configurators&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
    

    此时,通过 zkCli 可以查看 Zookeeper 的信息:可以看出,服务 com.example.HelloService,它有一个消费者 consumers

    [zk: localhost:2181(CONNECTED) 12] ls /dubbo/com.example.HelloService
    [consumers, configurators, routers, providers]
    [zk: localhost:2181(CONNECTED) 13] ls /dubbo/com.example.HelloService/consumers
    [consumer%3A%2F%2F10.93.67.135%2Fcom.example.HelloService%3Fapplication%3Ddubbo-demo-client%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.6.2%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D44571%26qos.enable%3Dfalse%26revision%3D1.0.0%26side%3Dconsumer%26timestamp%3D1530084738835%26version%3D1.0.0]
    

    做一个实验,我们依次停止之前启动的两个 serviceDemo,从 clientDemo 的日志中,可以看出,它收到了通知 subscribe url,并且依次断开了与两个服务的连接 disconnected from

    2018-06-27 17:07:32.789  INFO 52762 --- [ain-EventThread] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=50337&revision=1.0.0&side=provider&timestamp=1530089150751&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 17:07:32.794  INFO 52762 --- [ain-EventThread] c.a.d.r.transport.netty.NettyChannel     :  [DUBBO] Close netty channel [id: 0x208cebb4, /10.93.67.135:55699 => /10.93.67.135:20881], dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 17:07:32.799  INFO 52762 --- [andler-thread-1] c.a.d.rpc.protocol.dubbo.DubboProtocol   :  [DUBBO] disconnected from /10.93.67.135:20881,url:dubbo://10.93.67.135:20881/com.example.HelloService?anyhost=true&application=dubbo-demo-client&check=false&codec=dubbo&dubbo=2.6.2&generic=false&heartbeat=60000&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&register.ip=10.93.67.135&remote.timestamp=1530089806995&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 17:07:38.387  INFO 52762 --- [ain-EventThread] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, urls: [empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
    2018-06-27 17:07:38.389  INFO 52762 --- [ain-EventThread] c.a.d.r.transport.netty.NettyChannel     :  [DUBBO] Close netty channel [id: 0xee9aa5e5, /10.93.67.135:55698 => /10.93.67.135:20880], dubbo version: 2.6.2, current host: 10.93.67.135
    

    关于 Zookeeper 的一些补充

    参考 http://www.importnew.com/24411.html
    对于一个服务框架,注册中心是其核心中的核心,虽然暂时挂掉并不会导致整个服务出问题,但是一旦挂掉,整体风险就很高。考虑一般情况,注册中心就是单台机器的时候,其实现很容易,所有机器起来都去注册服务给它,并且所有调用方都跟它保持长连接,一旦服务有变,即通过长连接来通知到调用方。但是当服务集群规模扩大时,这事情就不简单了,单机保持连接数有限,而且容易故障。

    作为一个稳定的服务化框架,Dubbo 可以选择并推荐 Zookeeper 作为注册中心。其底层将 Zookeeper 常用的客户端 zkclient 和 curator 封装成为 ZookeeperClient。

    • 当服务提供者服务启动时,向 Zookeeper 注册一个节点;
    • 服务消费者则订阅其父节点的变化,诸如启动停止都能够通过节点创建删除得知,异常情况比如被调用方掉线也可以通过临时节点session 断开自动删除得知;
    • 服务消费方同时也会将自己订阅的服务以节点创建的方式放到 Zookeeper;
    • 于是可以得到映射关系,诸如谁提供了服务,谁订阅了谁提供的服务,基于这层关系再做监控,就能轻易得知整个系统情况。

    Zookeeper 所有数据都在内存中,模型类似一颗文件树,ZNode Tree,每个 ZNode 节点都会保存自己的数据内容和一系列属性。
    ZNode 分为持久节点临时节点,后者和客户端会话绑定。

    Zookeeper 的节点模型
    • 同一时刻多台机器创建同一个节点,只有一个会争抢成功。利用这个特性可以做分布式锁。
    • 临时节点的生命周期与会话一致,会话关闭则临时节点删除。这个特性经常用来做心跳,动态监控,负载等动作。
    • 顺序节点保证节点名全局唯一。这个特性可以用来生成分布式环境下的全局自增长 ID。

    通过zookeeper提供的原语服务,可以对 Zookeeper 能做的事情有个精确和直观的认识:

    • 创建节点
    • 删除节点
    • 更新节点
    • 获取节点信息
    • 权限控制
    • 事件监听

    关于 Zookeeper 的扩展

    这里我们只有一台 Zookeeper 服务器,假如它挂了,那么整个系统就挂了。
    为了防止这种情况,我们需要设置 Zookeeper 集群。
    Zookeeper 不仅可以单机提供服务,同时也支持多机组成集群来提供服务,实际上 Zookeeper 还支持另外一种伪集群的方式,也就是可以在一台物理机上运行多个 Zookeeper 实例。

    在 Zookeepe r中,有 Leader、Follower、Observer 三种角色。集群中所有机器通过一个 Leader 选举来决定一台机器作为 Leader,Leader 为客户端提供读和写服务。Follower 和 Observer 都提供读服务,区别在于 Observer 机器不参与选举。

    Zookeeper 通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,它就能够保证服务继续。

    因为官网建议至少3个节点,3台机器只要有2台可用就可以选出leader并且对外提供服务(2n+1台机器,可以容n台机器挂掉)。

    具体的方式,参见 https://blog.52itstyle.com/archives/363/

    相关文章

      网友评论

          本文标题:Spring Boot 2.0 集成 Dubbo 示例

          本文链接:https://www.haomeiwen.com/subject/fgrbyftx.html