Dubbo学习笔记(一) 初识

1. 引子

为什么突然想起来要学Dubbo呢,原因有下:

  • 之前有人问什么是SPI,以前学习嵌入式的时候,听过硬件上有SPI总线协议,不知道Java世界也有一个SPI的概念,全称为:Service Provider Interface。在Dubbo的官网介绍开端就提到了SPI这个概念,为此就想通过Dubbo来多了解一下SPI。

  • 在学习Spring Cloud的时候,服务间访问是通过REST风格的HTTP调用实现的。但是很多书和文章提到在分布式架构中除了基于HTTP的REST调用外,还提到了RPC框架,即远程过程调用来实现服务间调用。至于REST和RPC的区别是什么一直有点模糊,直到在上偶然翻到一篇文章,作者叫柳树之,那篇文章的地址是:https://www.jianshu.com/p/2accc2840a1b。在一堆无病呻吟的软文中找到这种优秀的作者也是比较稀少的。从文中得知,Dubbo就是基于RPC实现的。为此也产生了想深入学习Dubbo的念想。

2. 环境搭建

Dubbo官网文档地址为:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html。下面就先照着官网文档内容进行环境搭建。

2.1 zookeeper

dubbo的服务注册依赖于zookeeper,在zookeeper官网下载包,分别执行下面的命令。

  • 解压

    > tar -zxvf zookeeper-3.4.10.tar.gz //解压
    > cd zookeeper-3.4.10/conf //切换到配置目录下
    > mv zoo_sample.cfg zoo.cfg //更改默认配置文件名称
    > vi zoo.cfg //编辑配置文件,自定义dataDir
    
  • 启动

    > cd zookeeper-3.4.10/bin //切换到 bin目录
    > ./zkServer.sh start //启动
    
  • 客户端连接

    > cd zookeeper-3.4.10/bin //切换到 bin目录
    > ./zkCli.sh -server 127.0.0.1:2181
    
  • 停止

    > ./zkServer.sh stop //停止后,如果CLi没有关闭,将报错
    

2.2.1 服务发现组件介绍

​ 对于微服务的治理而言,核心就是服务的注册和发现。所以选择哪个组件,很大程度上要看它对于服务注册与发现的解决方案。在这个领域,开源的框架有很多,最常见的是ZooKeeper,但这并不是一个最佳的选择。

​ 在分布式系统领域有一个著名的CAP定理:C(数据一致性)、A(服务可用性)、P(服务对网络分区故障的容错性)这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。ZooKeeper是Apache下非常著名的一个顶级项目,很多场景下ZooKeeper也作为Service发现服务解决方案。ZooKeeper保证的是CP,即任何时刻对ZooKeeper的访问请求能得到一致的数据结果,同时系统对网络分区具备容错性,但是它不能保证每次服务请求的可用性。从实际情况来分析,在使用ZooKeeper获取服务列表时,如果ZooKeeper正在选主,或者ZooKeeper集群中半数以上机器不可用,那么就无法获取数据。所以说,ZooKeeper不能保证服务可用性。

​ 的确,对于大多数分布式环境,尤其是涉及数据存储的场景,数据一致性是应该首先被保证的,这也是ZooKeeper被设计成CP的主要原因。但是对于服务发现场景来说,具体情况就需要具体分析:针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不一致,也不会造成灾难性的后果。因为对于服务消费者来说,能够消费才是最重要的。所以,对于服务发现而言,可用性比数据一致性更加重要(AP优于CP)。而Spring Cloud Netflix在设计Eureka的时候遵守就是的AP原则。

​ Eureka是Netflix开源的一款提供服务注册和发现的产品,并且提供了相应的Java SDK封装。在它的实现中,节点之间是相互平等的,部分注册中心的节点“挂掉”也不会对集群造成影响,即使集群只剩一个节点存活,也可以正常提供发现服务。哪怕是所有的服务注册节点都“挂了”, EurekaClients也会缓存服务调用的信息。这就保证了微服务之间的互相调用是足够健壮的。为了适配更多的服务注册发现框架,Spring Cloud针对该方案进行了一层抽象,官方提供了三种实现:Eureka、Consul、ZooKeeper,目前支持得最好的就是Eureka,其次是Consul,最后是ZooKeeper。

2.2 dubbo快速启动

​ dubbo官网已经提供了一个demo,基于这个demo,我们可以快速创建出服务提供者和消费者。这里直接从码云上将dubbo代码拉取下来:

git clone https://gitee.com/mirrors/dubbo.git

不使用官网写的github,因为下载速度太慢了。

​ 下载后可以看到这个一个整体的大目录,下面有以"dubbo-"开头的各种包。其使用IDEA将dubbo/dubbo-demo/dubbo-demo-xml这个目录导入成一个maven工程。该maven工程中包含了名为dubbo-demo-xml-consumer和名为dubbo-demo-xml-provider的两个模块。

2.2.1 公共模块

在dubbo中,服务提供者提供的方法要以接口的形式单独打成一个jar包,然后分别被服务提供者和服务消费者引入。为此先新建一个只定义接口的maven工程:在dubbo-demo-xml这个大工程下,新建一个module,名为api。该模块只定义一个接口:

package org.apache.dubbo.demo;

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

然后,pom.xml文件修改如下:

com.dubbo.api
api
1.0-SNAPSHOT
jar

这样,在根目录下执行mvn install命令,将应用api打包成一个jar包(target目录下)。

生成jar包后,还要将这个jar包发布到本地的maven仓库中,发布命令为:

mvn install:install-file -Dfile=/Users/yubuyun/Documents/workspace/dubbo/dubbo/dubbo-demo/dubbo-demo-xml/api/target/api-1.0-SNAPSHOT.jar -DgroupId=com.dubbo.api -DartifactId=api -Dversion=1.0.0 -Dpackaging=jar -DgeneratePom=true -DcreateChecksum=true

这样本地仓库就有这个api-1.0-SNAPSHOT.jar了。

2.2.2 服务提供者

添加依赖


首先修改pom.xml文件,将公共模块这个jar包引入:


    com.dubbo.api
    api
    1.0-SNAPSHOT
    compile

访问接口实现


然后服务提供者应用中就可以直接实现DemoService这个接口了:

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
    }
}

这里服务提供者只是打印和返回了字符串。

配置文件


dubbo官方的demo中在resources目录下还提供了dubbo配置文件,名为dubbo-provider.xml,内容如下:






表示将当前应用起名为demo-provider,注册到本地的zookeeper,并且指定了接口实现类并注入到Spring容器中。

启动服务提供者


main函数定义如下:

public class Application {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
        context.start();
        System.in.read();
    }
}

就是解析dubbo配置到容器中,然后启动。

2.2.3 服务消费者

添加依赖


首先修改pom.xml文件,将公共模块这个jar包引入:


    com.dubbo.api
    api
    1.0-SNAPSHOT
    compile

配置文件


dubbo官方的demo中在resources目录下还提供了dubbo配置文件,名为dubbo-consumer.xml,内容如下:




表示将当前应用起名为demo-consumer,注册到本地的zookeeper,并且将通过demoService接口访问其他应用。

启动服务消费者


启动类定义如下:

public class Application {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
        context.start();
        DemoService demoService = context.getBean("demoService", DemoService.class);
        String result = demoService.sayHello("hello");
        System.out.println("consumer:"+result);
    }
}

启动过程中,服务消费者本身并没有实现DemoService接口实现,但是仍然可以直接调用该接口的访问,这时直接就访问到了服务提供者上。这就是RPC调用的优点:像本地调用一样进行远程调用。

2.2.4 调试

首先启动zookeeper,然后启动服务提供者,再启动服务消费者,就会发现,服务提供者打印:

provider.DemoServiceImpl: Hello hello, request from consumer: /99.15.214.152:55576

服务消费者收到返回值后打印:

consumer:Hello hello, response from provider: 99.15.214.152:20880

这就说明两个应用之间RPC调用成功了。

3. 其他

本文示例代码的地址为:https://gitee.com/yubuyun/dubbo-study

你可能感兴趣的:(Dubbo学习笔记(一) 初识)