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