Dubbo
初识 Dubbo
概述
Apache Dubbo 是一款高性能、轻量级的开源 Java RPC
框架,它提供了三大核心能力:面向接口的远程方法调用
,智能容错
和负载均衡
,以及服务自动注册和发现
。
Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案、服务治理方案。
面向接口代理:调用接口的方法,在 A 服务器调用 B 服务器的方法,由 dubbo 实现对 B 的调用,无需关心实现的细节,就像 MyBatis 访问 Dao 的接口,可以操作数据库一样。不用关 心 Dao 接口方法的实现。
Dubbo 通过二进制流
的形式进行数据传输。
什么是RPC?
RPC 【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,是一种技术思想,而不是规范。它允许程序调用另一个地址空间(网络的另一台机器上)的过程或函数,而不用开发人员显式编码这个调用的细节。调用本地方法和调用远程方法一样。
RPC 的实现方式可以不同。例如 java 的 rmi, spring 远程调用等。
RPC 概念是在上世纪 80 年代由 Brue Jay Nelson(布鲁·杰伊·纳尔逊)提出。使用 PRC 可以将本地的调用扩展到远程调用(分布式系统的其他服务器)。
基本架构
- 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
支持的协议
支持多种协议:dubbo , hessian , rmi , http, webservice , thrift , memcached , redis。 dubbo 官方推荐使用 dubbo 协议。dubbo 协议
默认端口 20880
直连方式
点对点的直连项目:消费者直接访问服务提供者,没有注册中心。消费者必须指定服务提供者的访问地址(url)。
直连案例
创建项目
创建两个 maven 项目,分别对应
消费者
与服务者
服务提供者
引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<dependencies>
<!--dubbo 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--Spring 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
</dependencies>创建接口类以及实现
1
2
3
4public interface SomeService {
String hello(String msg);
}1
2
3
4
5
6public class SomeServiceImpl implements SomeService {
public String hello(String msg) {
return "Hello " + msg;
}
}安装 translation 插件(非必要)
选中单词
ctrl shift y
可直接翻译创建 link-provider.xml 配置文件
在 resources 下创建,名称随意
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 声明服务提供者名称,保证它的唯一性,它是dubbo内部使用的唯一标识 -->
<dubbo:application name="provider-01" />
<!-- 指定 dubbo 协议名称和端口号(默认20880) -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 加载接口实现类到spring容器-->
<bean id="someServiceImpl" class="com.cuc.dubbo.service.impl.SomeServiceImpl" />
<!-- 暴露服务
interface:暴露服务的接口全限定类名
ref:引用接口在spring容器中的标识名称
registry:注册中心,这里是直连方式不需要用到,值必须设置成 N/A
-->
<dubbo:service interface="com.cuc.dubbo.service.SomeService" ref="someServiceImpl" registry="N/A" />
</beans>配置 web.xml
配置服务提供者监听器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:link-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>打包
先注释掉
<packaging>war</packaging>
,打包后解回来
消费者
引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<dependencies>
<!--dubbo 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--Spring 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!-- 服务者打的包 -->
<dependency>
<groupId>com.cuc</groupId>
<artifactId>provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>不同的是还要引入服务者刚才打的包,直接去 provider 的 pom.xml 文件中复制就行。
创建 link-consumer.xml 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--声明消费者名称,保证唯一,是dubbo内部唯一标识-->
<dubbo:application name="consumer-01" />
<!--引用远程接口
id:远程接口代理对象名称
url:调用远程接口服务的url地址
-->
<dubbo:reference id="someService" interface="com.cuc.dubbo.service.SomeService"
url="dubbo://localhost:20880" registry="N/A" />
</beans>创建一个控制器方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SomeController {
private SomeService someService;
public String hello(Model model){
// 调用远程接口服务
String result = someService.hello("World");
model.addAttribute("msg",result);
return "hello";
}
}创建 SpringMVC 配置文件
在 resources 下创建 springmvc.xml,名称随意。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.cuc.dubbo.web" />
<!--注解驱动-->
<mvc:annotation-driven />
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>配置 web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:link-consumer.xml,classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>创建 jsp 页面
在 webapp 下创建 hello.jsp
1
2
3
4
5
6
7
8
9<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
测试
配置 Tomcat
端口不能重复
启动两个 tomcat
浏览器访问
存在的问题
消费者可以直接 new 接口实现类,并没有屏蔽底层细节,违背了官网所说的原则。
解决
项目结构
dubbo 官方推荐的项目结构
服务提供者工程(web 工程)
实现接口工程中的接口
消费者工程(web 工程)
消费接口工程提供的接口
接口工程(java 工程)
业务接口和实体类
创建Java工程
就是一个普通的 maven 工程
接口工程
创建 User 实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class User {
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}创建 SomeService 接口类
1
2
3
4
5
6public interface SomeService {
String hello(String msg);
User queryUserById(Integer id);
}
服务者
删除原本接口
即 SomeService
引入接口工程
1
2
3
4
5
6<!--接口工程-->
<dependency>
<groupId>com.cuc</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>实现接口工程的 SomeService 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class SomeServiceImpl implements SomeService {
public String hello(String msg) {
return "Hello " + msg;
}
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("swj");
return user;
}
}
消费者
引入接口工程
需删除之前引入的服务者依赖
1
2
3
4
5
6<!-- 接口工程 -->
<dependency>
<groupId>com.cuc</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>修改控制器方法
这时不会再 new 出接口实现类了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SomeController {
private SomeService someService;
public String hello(Model model){
String result = someService.hello("World");
model.addAttribute("msg",result);
return "hello";
}
public String userDetail(Integer id,Model model){
User user = someService.queryUserById(id);
model.addAttribute("user",user);
return "userDetail";
}
}创建 userDetail.jsp
1
2
3
4
5
6
7
8
9
10<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>用户编号:${user.id}</h2>
<h2>用户姓名:${user.username}</h2>
</body>
</html>
测试
启动 服务者工程 以及 消费者工程
此时出错是因为 dubbo 传输是的是
二进制流
(需要序列化),字符串等类型会自动序列化,但是当服务提供者返回实体对象给消费者时,由于没有序列化,进而报错。
序列化对象
在接口工程修改 User 实体类
1 | public class User implements Serializable { |
再次测试
ZK 案例
注册中心
概述
对于服务提供方,它需要发布服务,而且由于应用系统的复杂性,服务的数量、类型也不断膨胀;对于消费者,它最关心如何获取到它所需要的服务,而面对复杂的应用系统, 需要管理大量的服务调用。
而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,既需要提供服务,又需要消费服务。 通过将服务统一管理起来,可以有效地优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。Dubbo 提供的注册中心有如下几种类型可供选:
- Multicast 注册中心:组播方式
- Redis 注册中心:使用 Redis 作为注册中心
- Simple 注册中心:就是一个 dubbo 服务。作为注册中心。提供查找服务的功能。
- Zookeeper 注册中心:使用 Zookeeper 作为注册中心
官方推荐使用 Zookeeper 注册中心。
工作方式
流程说明:
- 服务提供者启动时:向
/dubbo/com.foo.BarService/providers
目录下写下自己的URL
地址 - 服务消费者启动时:订阅
/dubbo/com.foo.BarService/providers
目录下的提供者URL
地址。并向/dubbo/com.foo.BarService/consumers
目录下写下自己的URL
地址 - 监控中心启动时:订阅
/dubbo/com.foo.BarService
目录下的所有提供者和消费者的URL
地址
ZK搭建
zookeeper 启动默认占用 2181
以及 8080
端口
可在 zoo.cfg
配置文件修改:
- clientPort=2181
- admin.serverPort=8888
创建项目
接口工程不变,再创建两个web工程,同直连案例
引入依赖
在
zk-consumer
、zk-provider
中引入依赖
1 | <dependencies> |
服务提供者
创建接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class SomeServiceImpl implements SomeService {
public String hello(String msg) {
return "Hello " + msg;
}
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("swj");
return user;
}
}创建配置文件 zk-provider.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 声明服务提供者名称 -->
<dubbo:application name="zk-provider-01" />
<!-- 指定 dubbo 协议名称和端口号 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 指定注册中心 -->
<dubbo:registry address="zookeeper://hadoop101:2181" />
<!-- 加载接口实现类到spring容器-->
<bean id="someServiceImpl" class="com.cuc.dubbo.service.impl.SomeServiceImpl" />
<!-- 暴露服务 -->
<dubbo:service interface="com.cuc.dubbo.service.SomeService" ref="someServiceImpl" />
</beans>配置 web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:zk-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
消费者
创建控制器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SomeController {
private SomeService someService;
public String hello(Model model){
// 调用远程接口服务
String result = someService.hello("World");
model.addAttribute("msg",result);
return "hello";
}
public String userDetail(Integer id,Model model){
User user = someService.queryUserById(id);
model.addAttribute("user",user);
return "userDetail";
}
}创建配置文件 zk-consumer.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--声明消费者名称-->
<dubbo:application name="zk-consumer-01" />
<!--指定注册中心-->
<dubbo:registry address="zookeeper://hadoop101:2181" />
<!--引用远程接口-->
<dubbo:reference id="someService" interface="com.cuc.dubbo.service.SomeService" url="dubbo://localhost:20880" />
</beans>创建配置文件 springmvc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.cuc.dubbo.web" />
<!--注解驱动-->
<mvc:annotation-driven />
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>配置 web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:zk-consumer.xml,classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>创建两个 jsp 页面
同直连
测试
配置 Tomcat
启动 ZK 集群
启动服务提供者、消费者项目
效果
查看集群信息
Dubbo 配置
配置原则
尽量在服务提供者配置参数,因为服务提供者更了解服务的各种参数。
关闭检查
dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=true。通过 check="false"
关闭检查, 比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
例 1:关闭某个服务启动时的检查
1 | <dubbo:reference interface="com.foo.BarService" check="false" /> |
例 2:关闭注册中心启动时的检查
1 | <dubbo:registry check="false" /> |
默认启动服务时检查注册中心存在并已运行。注册中心不启动会报错。
重试次数
消费者访问提供者,如果访问失败,则切换重试访问其它服务器,但重试会带来更长延迟。 访问时间变长,用户的体验较差。多次重新访问服务器有可能访问成功。可通过 retries="2"
来设置重试次数(不含第一次)。
1 | <dubbo:service retries="2" /> |
超时时间
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间。
1 | <dubbo:reference interface="com.foo.BarService" timeout="2000" /> |
版本号
每个接口都应定义版本号,为后续不兼容升级提供可能。当一个接口有不同的实现,项目早期使用的一个实现类, 之后创建接口新的实现类。区分不同的接口实现使用 version
。 特别是项目需要把早期接口的实现全部换位新的实现类,也需要使用 version。
可以用版本号从早期的接口实现过渡到新的接口实现,版本号不同的服务相互间不引用。
后面有案例
监控中心
概述
dubbo 的使用,其实只需要有注册中心,消费者,提供者这三个就可以使用了,但是并不能看到有哪些消费者和提供者,为了更好的调试,发现问题,解决问题,因此引入 dubbo-admin。 通过 dubbo-admin 可以对消费者和提供者进行管理。可以在 dubbo 应用部署做动态的调整,服务的管理。
dubbo-admin
图形化的服务管理页面。安装时需要指定注册中心地址,即可从注册中心中获取到所有的提供者/消费者进行配置管理。
dubbo-monitor-simple
简单的监控中心
dubbo-admin
下载 dubbo-admin
进入目录修改 zk 地址
修改地址:
dubbo-admin\src\main\resources\application.properties
1
dubbo.registry.address=zookeeper://hadoop101:2181
测试
在
dubbo-admin
目录下cmd
执行命令打包1
C:\Users\usesr\Desktop\incubator-dubbo-ops-master\dubbo-admin>mvn clean package
在
target
文件中可看到打包后的 jar 包之后在 jar 包所在目录
cmd
执行java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
运行打包好的 jar 包。运行后在浏览器中访问
localhost:7001
,访问到注册中心,输入账号密码root
dubbo-monitor-simple
下载
修改
修改地址
dubbo-monitor-simple\src\main\resources\conf
打包
1
C:\Users\usesr\Desktop\incubator-dubbo-ops-master\dubbo-monitor-simple>mvn package
解压打包后的
dubbo-monitor-simple-2.0.0-assembly.tar.gz
双击
start.bat
启动dubbo-monitor-simple
监控中心访问
localhost:8080
在服务提供者和消费者的xml中配置以下内容
1
<dubbo:monitor protocol="registry"></dubbo:monitor>
重启项目后监控中心捕获到了服务提供和消费者信息
整合 SpringBoot
三种方式
导入 dubbo-starter。在application.properties配置属性,使用@Service【暴露服务】,使用@Reference【引用服务】
保留 Dubbo 相关的 xml 配置文件
导入 dubbo-starter,使用 @ImportResource 导入 Dubbo 的 xml 配置文件
使用 注解API 的方式
在配置类中将每一个组件手动配置到容器中,让 dubbo 来扫描其他的组件
下面使用第一种方式
创建项目
接口工程不变,新建服务提供者以及消费者 SpringBoot 工程。
服务提供者
引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.cuc</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>配置
application.properties
省去服务的暴露,改用注解暴露
1
2
3
4
5
6
7server.port=8082
dubbo.application.name=boot-provider
dubbo.registry.address=hadoop101:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880实现接口
注意:@Service 在
com.alibaba.dubbo.config.annotation.Service
下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 暴露服务
public class SomeServiceImpl implements SomeService {
public String hello(String msg) {
return "Hello " + msg;
}
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("swj");
return user;
}
}启动类中开启扫描
1
2
3
4
5
6
7
8// 开启基于注解的dubbo功能
public class BootProviderApplication {
public static void main(String[] args) {
SpringApplication.run(BootProviderApplication.class, args);
}
}
消费者
引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.cuc</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>配置
application.properties
省去了接口的注入,改用注解
1
2
3server.port=8081
dubbo.application.name=boot-consumer
dubbo.registry.address=zookeeper://hadoop101:2181控制器方法(调用接口)
注意:@Reference 在
com.alibaba.dubbo.config.annotation.Reference
下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SomeController {
// 引用远程提供者服务
SomeService someService;
public String hello(){
String result = someService.hello("World");
return result;
}
public User userDetail(Integer id, Model model){
User user = someService.queryUserById(id);
model.addAttribute("user",user);
return user;
}
}启动类中开启扫描
1
2
3
4
5
6
7
8// 开启基于注解的dubbo功能
public class BootConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootConsumerApplication.class, args);
}
}
测试
版本号案例
基于 SpringBoot 实现的版本号案例
服务提供者的实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SomeServiceImpl implements SomeService {
public String hello(String msg) {
return "Hello " + msg;
}
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("swj");
return user;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SomeServiceImpl2 implements SomeService {
public String hello(String msg) {
return "Hello " + msg + "!";
}
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("SuWeiJin");
return user;
}
}消费者的控制器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SomeController {
SomeService someService;
SomeService someService2;
public String hello(){
String result1 = someService.hello("World");
String result2 = someService2.hello("World");
return result1+"|"+result2;
}
public User userDetail(Integer id){
User user1 = someService.queryUserById(id);
User user2 = someService2.queryUserById(id);
User user = new User();
user.setId(id);
user.setUsername(user1.getUsername()+"|"+user2.getUsername());
return user;
}
}测试
高可用
即通过设计,减少系统不能提供服务的时间
原因:
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
负载均衡
Dubbo 有四种负载均衡机制,默认为 random 随机调用
Random LoadBalance
基于权重的随机负载均衡机制
此处每次访问有 2/7 概率访问提供者1,4/7 概率访问提供者2,1/7 概率访问提供者3。
RoundRobin LoadBalance
基于权重的轮询负载均衡机制
第一个请求随机访问其中一个提供者,之后的所有请求都按照顺序访问提供者,如果被访问的提供者在当前被访问次数已满,则跳过继续访问下一个提供者。
此处第一个请求随机访问了 1 号提供者,之后所有请求都以轮询方式访问提供者,直到第六个请求要访问 3 号提供者时,该提供者可供访问次数已被第三个请求占用,所以转而去请求提供者 1,同时提供者 1 的两次请求次数也被使用了,则去访问 2 号提供者。
LeastActive LoadBalance
最少活跃数负载均衡机制
相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
一致性hash 负载均衡机制
相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
可以通过服务降级功能临时屏蔽某个非关键服务,并定义降级后的返回策略:
1 | RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); |
其中:
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
mock=fail:return+null
表示消费方对该服务的方法调用在失败后(比如访问超时),再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
也可直接在 dubbo-admin
可视化页面设置
集群容错
简介
在集群调用失败时,Dubbo 提供了多种容错方案,缺省(默认)为 failover 重试
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息
配置
参照以下示例在服务提供方或消费方配置集群模式
1 | <dubbo:service cluster=“failsafe” /> |
整合 hystrix
Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能
引入依赖
spring boot 官方提供了对 hystrix 的集成,直接在 pom.xml 里加入依赖
1
2
3
4
5<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>启动类上添加 @EnableHystrix 启用 hystrix starter
1
2
3
4
5//开启服务容错功能
public class ProviderApplication {
...启动方法
}Provider 端
在 Dubbo 的 Provider 上增加 @HystrixCommand 配置,这样子调用就会经过 Hystrix 代理。
1
2
3
4
5
6
7
8
9
10
11
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
throw new RuntimeException("Exception to show hystrix enabled.");
}
}Consumer 端
对于 Consumer 端,则可以增加一层 method 调用,并在 method 上配置 @HystrixCommand。当调用出错时,会走到fallbackMethod = “reliable” 的调用里。
1
2
3
4
5
6
7
8
9
10
private HelloService demoService;
public String doSayHello(String name) {
return demoService.sayHello(name);
}
public String reliable(String name) {
return "hystrix fallback value";
}