引用官方的一段介绍
Apache Dubbo (incubating) |ˈdʌbəʊ| is a high-performance, JAVA based RPC framework open-sourced by Alibaba. As in many RPC systems, dubbo is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a dubbo server to handle client calls. On the client side, the client has a stub that provides the same methods as the server.
Dubbo是阿里出的一个高性能的分布式微服务框架,它的特点是性能高、界面友好、文档齐全(特别是中文文档),是很多中小型公司快速搭建微服务的首选技术。前段时间被阿里重新进行维护,并进入Apache孵化器。迁移到Apache后也推出了新的starter,简单试用后感觉不错,为此特地写下这一系列笔记,用以记录。(整个项目的源码在最后一章,感兴趣的童鞋可以下载自己搭建试试,也可以将其作为脚手架搭建你自己的微服务)
在dubbo中,每一个服务都是分布式的,消费者与提供者的关系并不是一对一的关系,而是一对多,并且服务提供者的实例位置是未知的,如果都由开发者使用静态文件去维护这个关系显示不合适的,一方面是徒增工作量,另一方面是灵活度不高,如果服务提供者更换了服务器地址,那么需要重新配置等,所以目前比较推崇的方案是所有服务提供者均主动将自己的信息注册到同个地方中,而消费者只要一律从这个地方取他需要的东西即可,这个地方就是所谓的注册中心。目前dubbo可以选择的注册中心包括zookeeper、redis等,一般建议使用zookeeper,下面演示一下在windows中如何搭建zookeeper注册中心。
1、下载zookeeper
直接在官网的镜像站下 (https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/current/zookeeper-3.4.12.tar.gz)
2、解压并配置zookeeper
为了演示,直接使用官方示例配置。把解压出来的zookeeper-3.4.12confzoo_sample.cfg 文件名改为zoo.cfg即可,如下图:
3、运行zookeeper注册中心
双击运行/zookeeper-3.4.12/bin/zkServer.cmd,出现以下窗口代表运行成功。
在Dubbo中,一大杀器就是它的可视化监控界面,在这个界面中可以查看服务者、提供者的数量、状态等,并对服务降级、容错、屏蔽等提供了可视化操作。下面演示如何搭建Dubbo Admin。
1、下载源码并解压
点击下面地址下载dubbo admin项目源码 (https://codeload.github.com/apache/incubator-dubbo-ops/zip/master)
下载后解压出来,如下图:
2、打包
打开cmd,进入第一步解压出来的dubbo-admin文件夹中,执行mvn clean package
(ps:要是提示未存在mvn命令,就把maven文件夹的bin目录添加到系统环境变量PATH中)
3、配置dubbo admin
打开上面那步打出来的war包(incubator-dubbo-ops-masterdubbo-admintargetdubbo-admin-2.0.0.war),编辑WEB-INF文件夹中的dubbo.properties文件,如下图
将红框处的IP地址改为你的注册中心的地址,由于我们的注册中心是在本地运行的,故此处无需做修改。
4、运行Dubbo Admin
将war包改名为ROOT.war后放到Tomcat的webApp目录中,然后运行tomcat。使用浏览器打开http://127.0.0.1:8080,提示输入账号密码,账号和密码默认均为root,登陆后的界面如下:
由于Dubbo是采用RPC调用方式,这也是与Spring Cloud比较大的区别之一。在Spring Cloud中,服务通过HTTP接口提供给其它服务调用,对于调用者与被调用者来说,只要双方保证满足http接口的契约即可,但是在dubbo中,是严格要求调用接口的类路径、参数等双方一致。所以需要构建一个API项目,该项目里面存放服务接口即公共实体,双方依赖这个项目完成远程调用。
pom.xml
<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.czh.dubbo</groupId> <artifactId>dubbo-common</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> </project>
公共实体-People.java
package com.czh.dubbo.common.entity; import java.io.Serializable; public class People implements Serializable { /** * */ private static final long serialVersionUID = 1415852192397895853L; // 人员编号 private int id; // 姓名 private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "People [id=" + id + ", name=" + name + "]"; } }
公共服务接口-PeopleService.java
package com.czh.dubbo.common.service; import com.czh.dubbo.common.entity.People; public interface PeopleService { People getPeople(People people); }
服务提供者就是被消费者所调用的,它需要实现公共服务接口,并通过dubbo把自己的服务暴露出来。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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.czh</groupId> <artifactId>dubbo-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>dubbo-provider</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.12.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-project --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.101tec/zkclient --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency> <dependency> <groupId>com.czh.dubbo</groupId> <artifactId>dubbo-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
com.alibaba.boot是迁移过后出的一个最新的dubbo spring boot starter,也是目前官方推荐使用的。值得注意的是,该starter不包含注册中心客户端,需要自己配置。一般推荐使用zookeeper作为注册中心,所以需要手动引入zkclient。
application.properties
#web服务端口,由于我们不需要使用web服务,所以将其设为随机端口 server.port=-1 #dubbo服务端口,我们无需知道dubbo服务运行在哪个端口,故也将其设为随机端口 dubbo.protocol.port = -1 #dubbo服务名称 dubbo.application.name = dubbo-provider #dubbo服务所在包路径 dubbo.scan.basePackages = com.czh.dubbo.provider.service #注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 #设置服务的日志输出级别为debug级 logging.level.com.czh.dubbo.provider.service=debug
服务实现类PeopleServiceImpl
package com.czh.dubbo.provider.service; import java.util.Random; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.dubbo.config.annotation.Service; import com.czh.dubbo.common.entity.People; import com.czh.dubbo.common.service.PeopleService; //注意,这里的service注解用的不是spring的注解,而是com.alibaba.dubbo.config.annotation.Service; @Service public class PeopleServiceImpl implements PeopleService{ private static final Logger logger = LoggerFactory.getLogger(PeopleServiceImpl.class); /** * 这个方法的作用是接收传过来的People实体,将其ID赋为随机数 */ @Override public People getPeople(People people) { people.setId(new Random().nextInt(10000)); logger.debug("People:{},ID:{}",people.getName(),people.getId()); return people; } }
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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.czh</groupId> <artifactId>dubbo-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>dubbo-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.12.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-project --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.101tec/zkclient --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>com.czh.dubbo</groupId> <artifactId>dubbo-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
#web服务端口 server.port=80 #消费者名称 dubbo.application.name=dubbo-consumer #需要使用到提供者服务的包路径 dubbo.scan.base-packages=com.czh.dubbo.consumer.controller #注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181
控制器PeopleController
package com.czh.dubbo.consumer.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.alibaba.dubbo.config.annotation.Reference; import com.czh.dubbo.common.entity.People; import com.czh.dubbo.common.service.PeopleService; @RestController public class PeopleController { @Reference private PeopleService peopleService; @GetMapping("/people/{name}") public People getPeople(@PathVariable("name") String name) { People people = new People(); people.setName(name); return peopleService.getPeople(people); } }
至此,整个项目便搭建完毕,三个项目的项目结构如下
1、打包运行
安装API项目到本地maven仓库
在dubbo-common项目的pom.xml文件上点击右键,Run as - Maven install
打包服务提供者与服务消费者
在这两个项目的pom.xml文件执行以下操作:点击右键,Run as - Maven build… 在弹出的选项框中Golas 填写:clean package,如下图
点击run,打包成功后如下图:
将两个项目的target文件夹中拿出打好的jar包放到某个文件夹下备用,如下图
使用java -jar 命令依次运行dubbo-provider 与dubbo-consumer,顺序不能搞错,并且得确保provider启动完成后再启动consumer,否则后期会出现空指针异常。
至此,我们总共启动了四个服务,分别是zookeeper注册中心、dubbo-admin、dubbo-provider、dubbo-consumer,如下图:
打开dubbo监控网页检查服务消费者和提供者是否已经注册成功:
可以看到,提供者与消费者已成功注册到注册中心
2、基本调用
使用浏览器访问此地址http://127.0.0.1/people/test,观察服务提供者的控制台输出内容
后台控制台已输出日志了,证明调用成功,那么浏览器是否能够拿到数据呢?
可以看到浏览器也拿到数据了,证明已经成功完成调用了。
3、容错与屏蔽
容错
顾名思义就是当服务提供者出现异常时,消费者是否能够正常处理,在这里,我们关闭掉服务提供者,再进行调用,观察是否会报错
可以观察到前端报异常了。我们进入dubbo-admin在消费者中把服务容错开启,如下
再进行调用可以看到
可以看到此时调用后返回确实为空,证明容错生效。
屏蔽
当对某个服务启动屏蔽时,此时消费者调用该服务时均会返回空对象。首先我们需要确保提供者与消费者均已启动,然后在消费者中对提供者进行屏蔽,如下图
再进行服务调用
可以看到此时调用后返回确实为空,证明屏蔽生效。
其实屏蔽与容错还有更高级的玩法,最基本的屏蔽是返回空对象,我们甚至可以自定义返回内容。
进入动态配置,选择新增
编辑红框处的内容如下图:
再访问网页
可以看到确实屏蔽成功,并返回我们自定义的内容,注意,此时并未调用到提供者服务。
3、服务降级
为了测试服务降级,我们需要再启动一个服务提供者
可以看到目前两个服务提供者
然后我们再连续多次访问网页
两个服务接收到的请求数量是差不多的,此时我们对第二个服务进行降级,为了测试明显,这里我们连续降两次级,将其降到0.25,再连续调用,观察结果
已将第二个服务提供者降级到0.25,此时再连续访问
此时很明显可以看到第二个服务接收的请求比第一个少很多,证明降级成功。
4、负载均衡
dubbo提供了三个基本的负载均衡策略,分别是 随机访问、轮询、最少并发,从字面上也可以得知是什么意思,在这里我演示一下轮询的负载均衡策略。在dubbo-admin中,选择负载均衡功能,点击新增,如下图
按下图进行配置
老样子,这次我们连续访问网页10次
可以看到两个服务刚好各均分了五个请求,负载均衡成功!
相比于spring cloud,个人认为dubbo提供的功能相对简单,但每个功能又很实用,且使用起来十分方便(因为有可视化的web界面操作),更适合中小型公司进行微服务搭建。