Spring Boot 2.0 集成 Dubbo 示例

2018-06-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:

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

<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
]

服务消费端 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 所有数据都在内存中,模型类似一颗文件树,ZNode Tree,每个 ZNode 节点都会保存自己的数据内容和一系列属性。
ZNode 分为持久节点临时节点,后者和客户端会话绑定。

Zookeeper 的节点模型

通过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/

上一篇下一篇

猜你喜欢

热点阅读