Dubbo使用
决定放弃了Dubbo的学习,转战Spring Cloud。
在用3.0的版本+springboot的时候,很多问题不能解决。
招聘中对Dubbo技能要求也不是很高
基础理论
《分布式系统原理与范型》定义:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
分布式系统(distributed system)是建立在网络之上的软件系统。
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,急需一个治理系统确保架构有条不紊的演进。
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。
缺点
- 性能扩展比较难
- 协同开发问题
- 不利于升级维护
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。
此时,用于加速前端页面开发的Web框架(MVC)是关键。
通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。
缺点: 公用模块无法重复利用,开发性的浪费
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键。
演进原因
在大规模服务化之前,应用可能只是通过 RMI 或 Hessian 等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过 F5 等硬件进行负载均衡。
存在问题:
当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器 ?
因此,针对这些问题,机构进行演进出现了以下为最基本的几个需求。
- 服务注册中心
- 能够动态地注册和发现服务,使服务的位置透明。并通过在消费方获取服务提供方地址列表,实现软负载均衡和失败重启,降低对 F5 硬件负载均衡器的依赖,也能减少部分成本。
应用管理中心
- 画出应用间的依赖关系图,描述应用间的架构关系。
服务监控
- 监控服务的调用情况,对系统的服务提供机器容量等进行监控、分析、调控。将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。
- 其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阈值,记录此时的访问量,再以此访问量乘以机器数反推总容量。
RPC
什么叫RPC
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。
即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
RPC 概念是在上世纪 80 年代由 Brue Jay Nelson(布鲁·杰伊·纳尔逊)提出。使用 PRC 可以 将本地的调用扩展到远程调用(分布式系统的其他服务器)。
RPC 的特点
- 简单:使用简单,建立分布式应用更容易。
- 高效:调用过程看起来十分清晰,效率高。
- 通用:进程间通讯的方式,有通用的规则。
RPC基本原理
RPC两个核心模块:通讯,序列化。
- 调用方 client 要使用右侧 server 的功能(方法),发起对方法的调用
- client stub 是 PRC 中定义的存根,看做是 client 的助手。stub 把要调用的方法参数进行序 列化,方法名称和其他数据包装起来。
- 通过网络 socket(网络通信的技术),把方法调用的细节内容发送给右侧的 server
- server 端通过 socket 接收请求的方法名称,参数等数据,传给 stub。
- server 端接到的数据由 serverstub(server 的助手)处理,调用 server 的真正方法,处理业务
- server 方法处理完业务
- 把处理的结果对象(Object)交给了助手
- 助手把 Object 进行序 列化,对象转为二进制数据。
- server 助手二进制数据交给网络处理程序 通过网络将二进制数据,发送给 client。
- client 接数据,交给 client 助手。 client 助手,接收数据通过反序列化为 java 对象(Object)
- (Object)作为远程方法调用结果。
rpc 通讯是基于 tcp 或 udp 协议
序列化方式(xml/json/二进制)
dubbo概述
- Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:
- 面向接口的远程方法调用
- 智能容错和负载均衡
- 服务自动注册和发现
- Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案、服务治理方案。
- 官网:http://dubbo.apache.org/
基本概念
Dubbo实现架构如下:
节点角色说明:
节点 角色说明 Provider
(服务提供者)暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。 Consumer
(服务消费者)调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 Registry
(注册中心)服务注册与发现的注册中心, 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者 Monitor
(监控中心)统计服务的调用次数和调用时间的监控中心 ,服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心 Container
服务运行容器 调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
在2.6版本之前是阿里维护, 捐赠给Apache基金会后版本变成2.7,包名变化比较大。
dubbo 支持的协议
支持多种协议:dubbo , hessian , rmi , http, webservice , thrift , memcached , redis。 dubbo 官方推荐使用 dubbo 协议。dubbo 协议默认端口 20880
使用 dubbo 协议,spring 配置文件加入:
1
<dubbo:protocol name="dubbo" port="20880" />
dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,例如:
1
2
3
4
5
6
7
8
9<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="pers.fulsun.api.HelloService" ref="helloService"
protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="pers.fulsun.api.DemoService" ref="demoService"
protocol="rmi" />
Dubbo的直连使用
假设:某电商平台系统需求,用户浏览商品;选择商品下订单,订单系统需要获取用户信息中的送货地址;向支付系统请求完成付款。
服务 | 功能 |
---|---|
网站系统 | 展示商品,修改用户信息 |
订单系统 | 生成订单,获取用户地址 |
用户系统 | 用户信息(地址,收件人,联系方式等 |
直连方式 dubbo
点对点的直连项目:消费者直接访问服务提供者,没有注册中心。消费者必须指定服务提供者的访问地址(url)。消费者直接通过 url 地址访问固定的服务提供者。这个 url 地址是不变的。
1 | 用户访问 ------>【商品网站服务】访问-----> 【订单服务】 |
编写服务提供者
创建服务提供者:订单服务, 项目名称:001-link-userservice-provider
maven pom.xml
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
28
29
30
31
32<!--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>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<build>
<plugins>
<!--JDK1.8编译插件-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>创建用户实体类:User
1
2
3
4
5
6
7
8
9
10
11
12
13package com.wkcto.dubbo.model;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
// ...get ...set
}新建用户服务接口 UserService
1
2
3
4
5
6
7
8
9
10
11
12
13package com.wkcto.dubbo.service;
import com.wkcto.dubbo.model.User;
public interface UserService {
/**
* 根据用户标识获取用户信息
* @param id
* @return
*/
User queryUserById(Integer id);
}新建接口的实现类:UserServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.wkcto.dubbo.service.impl;
import com.wkcto.dubbo.model.User;
import com.wkcto.dubbo.service.UserService;
public class UserServiceImpl implements UserService {
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("lisi");
user.setAge(23);
return user;
}
}创建 dubbo 配置文件 dubbo-userservice-provider.xml
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<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--服务提供者声明名称:必须保证服务名称的唯一性,它的名称是dubbo内部使用的唯一标识-->
<dubbo:application name="001-link-userservice-provider"/>
<!--访问服务协议的名称及端口号,dubbo官方推荐使用的是dubbo协议,端口号默认为20880-->
<!--
name:指定协议的名称
port:指定协议的端口号(默认为20880)
-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--
暴露服务接口->dubbo:service
interface:暴露服务接口的全限定类名
ref:接口引用的实现类在spring容器中的标识
registry:如果不使用注册中心,则值为:N/A
-->
<dubbo:service interface="com.wkcto.dubbo.service.UserService" ref="userServer" registry="N/A"/>
<!--将接口的实现类加载到spring容器中-->
<bean id="userServer" class="com.wkcto.dubbo.service.impl.UserServiceImpl"/>
</beans>测试配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.wkcto.dubbo.service;
import java.io.IOException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.dubbo.config.ApplicationConfig;
public class UserApplication {
public static void main(String[] args) throws Exception {
String configLocation = "dubbo-userservice-provider.xml";
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(configLocation);
ctx.start();
// 阻塞操作 , 应用一直运行
System.in.read();
}
}安装本地 jar 到 maven 仓库
服务接口中的方法要给消费者使用,消费者项目需要知道接口名称和接口中的方法名称、参数等。这些信息服务提供者才知道。需要把服务接口项目的类文件打包为 jar, 安装到 maven 仓库,仓库中的提供者 jar 可以被消费者 使用。 可以在 maven 窗口执行 install
创建服务消费者
新建 java project 项目名称:002-link-consumer
maven pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<dependencies>
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--依赖服务提供者-->
<dependency>
<groupId>com.wkcto.dubbo</groupId>
<artifactId>001-link-userservice-provider</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>创建获取用户接口
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
27package com.wkcto.dubbo.web;
import com.wkcto.dubbo.model.User;
import com.wkcto.dubbo.service.UserService;
import com.wkcto.dubbo.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void userDetail( Integer id) {
User user = this.userService.queryUserById(id);
System.out.println(user);
}
}创建 dubbo 配置文件 dubbo-consumer.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明服务消费者的名称:保证唯一性-->
<dubbo:application name="002-link-consumer"/>
<!--
引用远程服务接口:
id:远程服务接口对象名称
interface:调用远程接口的全限定类名
url:访问服务接口的地址
registry:不使用注册中心,值为:N/A
-->
<dubbo:reference id="userService"
interface="com.wkcto.dubbo.service.UserService"
url="dubbo://localhost:20880"
registry="N/A"/>
<bean id="userController" class="com.wkcto.dubbo.web.UserController">
<property name="userService" ref="userService"/>
</bean>
</beans>执行消费者
1
2
3
4
5
6
7
8
9
10
11package com.wkcto.dubbo.web;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConsumeApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("dubbo-consumer.xml");
UserController userController = ctx.getBean("userController", UserController.class);
userController.userDetail(1);
}
}
dubbo 服务化最佳实践
分包
建议将服务接口、服务模型、服务异常等均放在公共包中。
粒度
服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持。
服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。
不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。
版本
每个接口都应定义版本号,为后续不兼容升级提供可能,如: <dubbo:service interface="com.xxx.XxxService" version="1.0" />
。
建议使用两位版本号,要变更服务版本。先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。
改造 dubbo 项目
抽象分散在多个项目中的公共接口,实体类,异常,工具类到一个项目中,在其他项目 如服务提供者,消费者共用公共的资源。
实现目标
用户访问电商网站浏览商品—选择商品购买
用户访问电商网站—查看用户信息(收件人地址)
创建公共资源项目
服务提供者,消费者,网站等多个服务中共用,重复使用的类单独定义在一个项目。
创建公共的 maven java project ,项目名称:003-link-interface
1
2
3
4
5
6
7
8
9
10
11
12
<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.wkcto.dubbo</groupId>
<artifactId>003-link-interface</artifactId>
<version>1.0.0</version>
</project>复制之前 001-link-userservice-provider 项目的接口文件,实体类文件
安装 jar 到 maven 仓库:使用 idea 的 maven 窗口执行 install
创建用户信息服务
新建 web project
004-link-userservice-provider
maven pom.xml
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48<groupId>com.wkcto.dubbo</groupId>
<artifactId>004-link-userservice-provider</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<dependencies>
<!--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>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.wkcto.dubbo</groupId>
<artifactId>003-link-interface</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--JDK1.8编译插件-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>创建 UserInfoServiceImpl 实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.wkcto.dubbo.service.impl;
import com.wkcto.dubbo.model.User;
import com.wkcto.dubbo.service.UserService;
public class UserServiceImpl implements UserService {
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("zhangsan");
return user;
}
public Integer queryAllUserCount() {
return 52;
}
}dubbo 配置文件 dubbo-userservice-provider.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明dubbo服务提供者的名称:保证唯一性-->
<dubbo:application name="004-link-userservice-provider"/>
<!--设置dubbo使用的协议和端口号-->
<!--
name:dubbo使用协议的名称
port:dubbo服务的端口号
-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--
暴露服务接口
-->
<dubbo:service interface="com.wkcto.dubbo.service.UserService" ref="userService" registry="N/A"/>
<!--加载业务接口的实现类到spring容器中-->
<bean id="userService" class="com.wkcto.dubbo.service.impl.UserServiceImpl"/>
</beans>web.xml 注册 spring 监听器 src/main/webapp/WEB-INF/web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/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_3_0.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dubbo-userservice-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
创建消费服务
新建 web project 项目名称:005-link-consumer
maven pom.xm
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47<groupId>com.wkcto.dubbo</groupId>
<artifactId>005-link-consumer</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<dependencies>
<!--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>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--接口工程-->
<dependency>
<groupId>com.wkcto.dubbo</groupId>
<artifactId>003-link-interface</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--JDK1.8编译插件-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>创建 UserController
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
28
29
30package com.wkcto.dubbo.web;
import com.wkcto.dubbo.model.User;
import com.wkcto.dubbo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
public class UserController {
private UserService userService;
public String userDetail(Model model,Integer id) {
//根据用户标识获取用户详情
User user = userService.queryUserById(id);
//获取用户总人数
Integer allUserCount = userService.queryAllUserCount();
model.addAttribute("user",user);
model.addAttribute("allUserCount",allUserCount);
return "userDetail";
}
}dubbo 配置文件 dubbo-consumer.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明服务消费者名称:保证唯一性-->
<dubbo:application name="005-link-consumer"/>
<!--引用远程接口服务-->
<dubbo:reference id="userService"
interface="com.wkcto.dubbo.service.UserService"
url="dubbo://localhost:20880"
registry="N/A"/>
</beans>新建 spring 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?xml version="1.0" encoding="UTF-8"?>
<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://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.wkcto.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 DispatcherServlet src/main/webapp/WEB-INF/web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/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_3_0.xsd">
<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:applicationContext.xml,classpath:dubbo-consumer.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>编写响应文件 src/main/webapp/userDetail.jsp
1
2
3
4
5
6
7
8
9
10
11
12<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户详情</title>
</head>
<body>
<h3>用户详情</h3>
<div>用户标识:${user.id}</div>
<div>用户姓名:${user.username}</div>
<div>用户总人数:${allUserCount}</div>
</body>
</html>
测试服务
启动用户信息服务
启动消费服务,访问 /userDetail 接口
dubbo 常用标签
Dubbo 中常用标签。分为三个类别:公用标签,服务提供者标签,服务消费者标签
公用标签
<dubbo:application/>
和 <dubbo:registry/>
配置应用信息
<dubbo:application name=”服务的名称”/>
配置注册中心
<dubbo:registry address=”ip:port” protocol=”协议”/>
包扫描
服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包 (包括子包) 下的类。
如果不使用包扫描,也可以通过如下配置dubbo:service
, dubbo:reference
的方式来发布服务:
1 | <dubbo:annotation package="com.lxs.service.impl" /> |
一个配置项 (dubbo:service
、dubbo:reference
) 只能发布或者引用一个 服务,如果有多个服务,这种方式就比较繁琐了。推荐使用包扫描方式。
服务提供者标签
配置暴露的服务<dubbo:service interface=”服务接口名” ref=”服务实现对象 bean”>
服务消费者
配置服务消费者引用远程服务<dubbo:reference id=”服务引用 bean 的 id” interface=”服务接口名”/>
注册中心-Zookeepe
注册中心概述
对于服务提供方,它需要发布服务,而且由于应用系统的复杂性,服务的数量、类型也不断膨胀;对于服务消费方,它最关心如何获取到它所需要的服务,而面对复杂的应用系统,需要管理大量的服务调用。
而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即需要提供服务,有需要消费服务。 通过将服务统一管理起来,可以有效地优化内部应用对服务发布使用的流程和管理。
服务注册中心可以通过特定协议来完成服务对外的统一。Dubbo 提供的注册中心有如下几种类型可供选:
- Multicast 注册中心:组播方式
- Redis 注册中心:使用 Redis 作为注册中心
- Simple 注册中心:就是一个 dubbo 服务。作为注册中心。提供查找服务的功能。
- Zookeeper 注册中心:使用 Zookeeper 作为注册中心,推荐使用 Zookeeper 注册中心。
注册中心工作方式
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
流程说明:
- 服务提供者 (Provider) 启动时:向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
- 服务消费者 (Consumer) 启动时:订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
- 监控中心 (Monitor) 启动时:订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址
改造dubbo使用Zookeeper
新建项目 006-zk-interface 007-zk-userservice-provider 008-zk-consumer ,内容同上
provider consumer 项目加入 zookeepeer 相关 jar: org.apache.curator curator-framework 4.1.0
1
2
3
4
5
6<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.1.0</version>
</dependency>修改dubbo配置文件 dubbo-zk-userservice-provider.xml dubbo-zk-consumer.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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明dubbo服务提供者的名称:保证唯一性-->
<dubbo:application name="007-zk-userservice-provider"/>
<!--声明dubbo使用的协议名称和端口号-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--现在要使用zookeeper注册中心-->
<!--指定注册中心地址和端口号-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--暴露服务接口-->
<dubbo:service interface="com.bjpowernode.dubbo.service.UserService" ref="userServiceImpl"/>
<!--加载接口实现类-->
<bean id="userServiceImpl" class="com.bjpowernode.dubbo.service.impl.UserServiceImpl"/>
</beans>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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明dubbo服务消费者名称:保证唯一性-->
<dubbo:application name="008-zk-consumer"/>
<!--指定注册中心-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--引用远程接口服务-->
<dubbo:reference id="userService" interface="com.bjpowernode.dubbo.service.UserService"/>
</beans>运行应用
先启动注册中心
再启动 tomcat 服务器
访问 008-zk-consumer 的 /userDetail
注册中心的高可用
高可用性(High Availability):通常来描述一个系统经过专门的设计,从而减少不能提供服务的时间,而保持其服务的高度可用性。
Zookeeper 是高可用的,健壮的。但是Zookeeper 宕机,正在运行中的 dubbo 服务仍然可以正常访问。ps(如果生产者还活着的话)
生产者启动后,把自身的信息写入到zookeeper中,让zookeeper以心跳机制查询生产者是否活着。然后消费者启动时,向zookeeper获取生产者的信息,保存在内存中, 如果zookeeper活着,会实时更新 。当调用消费者进行消费的时候,是根据获取到的 地址,以及 状态 直接进行 RPC 调用。
zoookeeper宕机了!无论是消费者还是生产者,这时候在控制台都疯狂报错,因为它们都在不断地尝试连接zookeeper。但是消费者是根据内存中保存的生产者的信息进行通信的,而在zookeeper宕机之前,这份信息已经保存在在内存里面,所以,即使这时候zookpper宕机了,消费者还是能与生产者通信。
一般情况下,如果zookeeper还活着,因为有心跳检测机制,能检测到生产者是宕机了,那么它就会直接通知消费者,让消费者修改内存信息,这样消费者就不会去连接宕掉的服务。如果两台生产者也有一台在zookeeper宕机之后宕机了,要是发生这种情况,首先看负载均衡配置了哪一种,如果分发的调用是访问好的生产者,那自然没问题。如果是访问到了坏的生产者,那么消费者会立即修改内存里面的生产者的 状态位,并且立即访问好的那一台。
演示操作:
- 先启动 zookeeper, dubbo 服务提供者,dubbo 服务消费者。
- 测试正常访问胸
- 停止 zookeeper
- 测试消费者仍然可以访问提供者
dubbo的配置
配置原则
在服务提供者配置访问参数。因为服务提供者更了解服务的各种参数。
关闭检查
dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题
默认 check=true。通过 check=”false”关闭检查,
比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
1 | # 例 1:关闭某个服务的启动时检查 |
重试次数
消费者访问提供者,如果访问失败,则切换重试访问其它服务器,但重试会带来更长延迟。访问时间变长,用户的体验较差。多次重新访问服务器有可能访问成功。可通过 retries=”2” 来设置重试次数(不含第一次)。
1 | 重试次数配置如下: |
超时时间
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间。timeout:调用远程服务超时时间(毫秒)
dubbo 消费端 指定接口超时配置
1
<dubbo:reference interface="com.foo.BarService" timeout="2000" />
dubbo 服务端指定接口超时配置
1
<dubbo:server interface="com.foo.BarService" timeout="2000" />
版本号
- 每个接口都应定义版本号,为后续不兼容升级提供可能。当一个接口有不同的实现,项目早期使用的一个实现类, 之后创建接口的新的实现类。区分不同的接口实现使用version。特别是项目需要把早期接口的实现全部换位新的实现类,也需要使用 version.
- 可以用版本号从早期的接口实现过渡到新的接口实现,版本号不同的服务相互间不引用。可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
负载均衡
负载均衡(Load Balance):其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。
在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询、最少活跃调用数、一致性 Hash),缺省为 random 随机调用。
算法 | 特性 | 备注 | 配置值 |
---|---|---|---|
Weighted Random LoadBalance | 加权随机 | 默认算法,默认权重相同 | random (默认) |
RoundRobin LoadBalance | 加权轮询 | 借鉴于 Nginx 的平滑加权轮询算法,默认权重相同, | roundrobin |
LeastActive LoadBalance | 最少活跃优先 + 加权随机 | 背后是能者多劳的思想 | leastactive |
Shortest-Response LoadBalance | 最短响应优先 + 加权随机 | 更加关注响应速度 | shortestresponse |
ConsistentHash LoadBalance | 一致性哈希 | 确定的入参,确定的提供者,适用于有状态请求 | consistenthash |
P2C LoadBalance | Power of Two Choice | 随机选择两个节点后,继续选择 “连接数” 较小的那个节点。 | p2c |
Adaptive LoadBalance | 自适应负载均衡 | 在 P2C 算法基础上,选择二者中 load 最小的那个节点 | adaptive |
配置负载均衡策略,既可以在服务提供者一方配置,也可以在服务消费者一方配置,如下:
1 | import com.alibaba.dubbo.config.annotation.Reference; |
启动多个服务提供者来观察 Dubbo 负载均衡效果, 在一台机器上启动多个服务提供者,所以需要修改 tomcat 的端口号和 Dubbo 服务的 端口号来防止端口冲突。
服务端负载均衡
1 |
|
Dubbo 快速入门
Dubbo 作为一个 RPC 框架,其最核心的功能就是要实现跨网络的远程调用。这里是要创建两个应用,一个作为服务的提供方,一个作为服务的消费方。通过 Dubbo 来实现服务消费方远程调用服务提供方的方法。
服务提供方开发
创建 maven 工程(打包方式为 war)dubbodemo_provider,在 pom.xml 文件中导入如下坐标
1 | <modelVersion>4.0.0</modelVersion> |
配置 web.xml 文件: 右键项目add framework support(添加框架支持)
,选择WEB Application
, 文件目录IdeaProjects\dubbodemo_provider\web\WEB-INF\web.xml
1 | <context-param> |
创建服务接口
1 | public interface HelloService { |
创建服务实现类, 类上使用的 Service 注解是 Dubbo 提供的,用于对外发布服务
1 | import com.alibaba.dubbo.config.annotation.Service; |
在 src/main/resources 下创建 applicationContext-service.xml
1 |
|
插件选择 tomcat7:run
启动服务, 在 Zookeeper 查看
1 | 127.0.0.1:2181 $ ls /dubbo/pers.fulsun.service.HelloService/providers |
服务消费方开发
创建 maven 工程(打包方式为 war)dubbodemo_consumer
,pom.xml 配置和上面服务提供者相同,只需要将 Tomcat 插件的端口号改为 8082 即可。
配置 web.xml 文件
1 |
|
将服务提供者工程中的 HelloService 接口复制到当前工程, 编写 Controller
1 | import com.alibaba.dubbo.config.annotation.Reference; |
在 src/main/resources 下创建 applicationContext-web.xml
1 |
|
测试运行
运行测试 tomcat7:run 启动,先启动 provider 再启动 consumer 在浏览器输入 http://localhost:8082/demo/hello?name=huh , 查看浏览器输出结果
在 zookeeper 中查看
监控中心
什么是监控中心
dubbo 的使用,其实只需要有注册中心,消费者,提供者这三个就可以使用了,但是并不能看到有哪些消费者和提供者,为了更好的调试,发现问题,解决问题,因此引入 dubbo-admin。通过 dubbo-admin 可以对消费者和提供者进行管理。可以在 dubbo 应用部署做动态的调整,进行服务的管理。
-
- 图形化的服务管理页面;安装时需要指定注册中心地址,即可从注册中心中获取到所有的提供者/消费者进行配置管理
-
- 简单的监控中心;
发布配置中心(新版本)
访问github 地址,下载即可
解压dubbo-admin-0.6.0.zip, 修改 dubbo-admin-0.6.0\dubbo-admin-server\src\main\resources\application.properties 配置文件
1
2
3
4
5server.port=38080
admin.registry.address=zookeeper://127.0.0.1:2181
dubbo.registry.address=${admin.registry.address}
admin.root.user.name=root
admin.root.user.password=root进入dubbo-admin-0.6.0 根目录下,使用命令
mvn clean package
进行clean 打包1
mvn clean package -Dmaven.test.skip=true
打包成功之后,将打好的jar包(只需dubbo-admin-server-0.3.0.jar)拷贝的指定目录,当然不拷贝也行,启动服务
1
java -jar dubbo-admin-server-0.3.0.jar
启动浏览器输入:http://127.0.0.1:38080/
发布配置中心(旧版本)
下载监控中心,https://github.com/apache/incubator-dubbo-ops,这里下载的是源代码,需要手工编译打包。
application.properties 文件,内容如下:
1
2
3
4
5
6
7
8
9
10server.port=7001
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root
spring.guest.password=guest
dubbo.registry.address=zookeeper://127.0.0.1:2181运行 dubbo-admin 应用
- 先启动注册中心
- 执行提供者项目
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
启动 dubbo 管理后台- 在浏览器地址栏输入 http://localhost:7001 访问监控中心-控制台。