Dubbo学习笔记
Dubbo学习手册
RPC基础知识
软件架构
单一应用架构
当网站流量很小、应用规模很小时,只需要一个应用,将所有功能都部署在一起,以减少部署服务器数量和成本。
此时,用于简化增删改查的数据访问框架 ORM 是关键
这种结构的应用适合小型系统,或者公司的内部系统,用户少、请求量低,对请求的处理时间没有太高的要求。
将所有的功能都部署到一个服务器上,简单易用,开发难度低。
缺点:
- 性能扩展困难
- 不利于多人同时开发
- 不利于升级维护
- 整个系统的空间占用比较大
分布式服务架构
当应用越来越多,应用与应用之间存在大量的交互,此时可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的 分布式服务框架 RPC 是关键。
分布式系统将服务作为独立的应用,实现服务的共享和重用。
分布式系统
什么是分布式系统
分布式系统是若干独立计算机(服务器)的集合,这些计算机对于用户来说就像单个相关系统。分布式系统(Distributed System)是建立在网络之上的服务器端的一种结构。
分布式系统中的计算机可以使用不同的操作系统,可以运行不同应用程序提供的服务,将服务分散部署到多个计算机服务器上。
RPC
RPC(Remote Produce Call)远程过程调用,是一种进程间通信方式,是一种技术思想,而不是技术规范。
它允许程序调用另一个地址空间(网络上的另一台机器)的过程或函数,而不用开发人员显式编码这个调用的细节。调用本地方法和调用远程方法一样。
RPC的实现方式可以不同,如:Java的 RMI,Spring的远程调用等。
RPC的概念是在上世纪80年代由 Brue Jay Nelson 提出的。使用 RPC可以将本地的调用扩展到远程调用(分布式系统的其他服务器)。
RPC的特点:
- 简单:使用简单,使得建立分布式应用更容易
- 高效:调用过程十分清晰,效率高
- 通用:作为进程间通讯的方式,有着通用的规则
RPC基本原理
RPC调用过程:
- 因为调用方 Client 要使用右侧 Server的功能,故发起对该功能的请求调用
- Client Stub是 RPC中定义的存根,作为 Client的助手,把要调用的方法参数进行序列化,把方法名称和其他数据包装起来
- 通过网络 Socket,把方法调用的细节内容发送给右侧的 Server
- Server端通过 Socket接收请求的方法名称,参数等数据,传给 Server Stub
- Server端获取到的数据由 Server Stub处理,调用 Server真正的方法,处理业务
- Server方法处理完业务,把结果对象 Object交给 Server Stub,助手把 Object进行序列化,转化为二进制数据
- Server Stub将二进制数据交给网络处理程序
- 通过网络将二进制数据发送给 Client
- Client接收数据,将给 Client Stub
- Client Stub将数据反序列化为 Java对象,作为远程方法的调用结果
注意:
- RPC通信是基于 TCP / UDP 的
- 序列化方式(XML / JSON / 二进制)
Dubbo介绍
Dubbo概述
Apache Dubbo 是一款高性能、轻量级的开源 Java RPC框架。
Dubbo是阿里巴巴服务化治理方案的核心框架,致力于提高性能和透明化的 RPC远程服务调用方案、服务治理方案。
它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,服务自动注册和发现
- 远程通讯:提供对多种基于长链接的 NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交互方式
- 集群容错:提供基于接口方法的透明远程过程调用,包括多协议支持,软负载均衡,失败容错,地址路由,动态配置等集群支持
- 自动发现:基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器
Dubbo的核心部件:
- Remoting:网络通信框架,实现了 sync-over-async 和 request-response 消息机制
- RPC:一个远程过程调用的抽象,支持负载均衡、容灾和集群功能
- Registry:服务目录框架用于服务的注册和服务事件的发布与订阅
其他的 RPC框架:
- 新浪微博的 Motan
- 当当的 DubboX
- 谷歌的 GRPC
Dubbo的基本架构
- Provider(服务提供者):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务
- Consumer(服务消费者):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务。它从提供者地址列表中,基于软负载均衡算法,选择一台提供者进行调用。
- Registry(注册中心):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长链接推送变更数据给消费者
- Monitor(监控中心):服务消费者和提供者,在内存中累计调用次数和调用事件,定时发送一次统计数据到监控中心
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者
- 服务提供者在启动时,向注册中心注册自己提供的服务
- 服务消费者在启动时,向注册中心订阅自己所需的服务
- 注册中心返回服务提供者的地址列表给消费者,如果有变更,注册中心将基于长链接推送变更数据给消费者
- 服务消费者从提供者地址列表中基于软负载均衡算法,选择一台提供者进行调用,如果失败再做挑选
- 服务消费者和提供者,在内存中统计调用次数和调用时间,定时每分钟发送到监控中心
Dubbo的底层实现是动态代理,由 Dubbo框架创建远程服务(接口)对象的代理对象,通过代理对象调用远程方法。
Dubbo支持的协议
Dubbo一共支持 8 种协议:
- dubbo
- hessian
- rmi
- http
- webservice
- thrift
- memcached
- redis
Dubbo 官方推荐使用 dubbo协议,默认端口号为 20880
使用 dubbo协议,在 Spring配置文件中需加入:
<dubbo:protocal name="dubbo" port="20880"/>
dubbo协议
dubbo协议的特点:
- 采用单一长连接和异步通讯,适合小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
- 底层网络通信默认使用 Netty,性能优秀
- 不适合传送大数据量的服务,比如:传文件、传视频等
长连接和短连接
长连接:指在一个连接上可以连续发送多个数据包,在连接保持的情况下,如果没有数据包发送,需要双方同时发送检测包。长连接多用于操作频繁,点对点的通讯,而且连接数不能太多的情况。例如:数据库的连接用长连接。
短连接:指双方有信息交互时就建立一个连接,数据发送完以后,就断开连接,即每次连接只完成一项业务的发送。像 Web网站的 HTTP服务一般都使用短连接,更省资源。
Dubbo直连方式调用
点对点的直连项目:服务消费者直接访问提供者,没有注册中心,因此,消费者必须指定服务提供者的访问地址(URL)
实现目标
用户访问网站 ------> 网站访问服务 -----> 生成者提供并暴露服务
具体实现
orderservice-provider
<!-- pom.xml -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
// 创建订单实体类 Order
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
// 订单 id
private String id;
// 商品名称
private String goodName;
// 商品单价
private float price;
// 购买数量
private Integer amount;
// setters and getters, toString()
}
// 创建订单服务接口 OrderService
public interface OrderService {
// 用户的id,商品名称,单价,数量
public Order createOrder(Integer userId, String goodName, float price, Integer amount);
}
// 创建接口实现类 OrderServiceImpl
public class OrderServiceImpl implements OrderService {
@Override
public Order createOrder(Integer userId, String goodName, float price, Integer amount) {
Order order = new Order();
String orderId = UUID.randomUUID().toString().replaceAll("-", "");
order.setId(orderId);
order.setGoodName(goodName);
order.setPrice(price);
order.setAmount(amount);
return order;
}
}
<!-- 创建dubbo配置文件 orderservice-provider.xml -->
<!-- 明确服务的名称,使用唯一值,服务的名称是该项目/模块的名称 -->
<dubbo:application name="ch01-link-userservice-provider"/>
<!-- 访问服务的协议名称、端口 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!--
interface:接口的全限定名
ref:接口的实现 bean的 id
registry:直连方式,不使用注册中心。N/A:不使用注册中心
-->
<dubbo:service interface="com.bjpowernode.service.OrderService" ref="orderService" registry="N/A"/>
<!-- 声明订单实现bean -->
<bean id="orderService" class="com.bjpowernode.service.impl.OrderServiceImpl"/>
// 测试配置文件
public class OrderApplication {
public static void main(String[] args) throws IOException {
String configLocation = "orderservice-provider.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(configLocation);
((ClassPathXmlApplicationContext)ac).start();
// 阻塞操作,应用一直运行
System.in.read();
}
}
orderservice-consumer
<!-- pom.xml -->
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>link-orderservice-provider</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
// 创建购买商品的接口
public interface ShopService {
// 用户的id,商品名称,单价,数量
public Order buyGoods(Integer userId, String goodName, float price, Integer amount);
}
// 创建购买接口的实现类
public class ShopServiceImpl implements ShopService {
// 使用远程服务接口
private OrderService orderService;
public void setOrderService(OrderService orderService) {
this..orderService = orderService;
}
@Override
public void buyGoods(Integer userId, String goodName, float price, Integer amount) {
// 调用远程方法创建订单
Order order = orderService.createOrder(userId, goodName, price, amount);
return order;
}
}
<!-- 创建 dubbo配置文件 -->
<!-- 服务名称,使用唯一值,服务的名称是 dubbo内部使用标识服务的 -->
<dubbo:application name="ch02-link-consumer" />
<!--
引用远程服务接口
id:远程接口的代理对象名称
interface:远程接口的全限定名称
url:访问服务提供者的地址
-->
<dubbo:reference id="remoteOrderService" interface="com.bjpowernode.service.OrderService" url="dubbo://localhost:20880" registry="N/A" />
<bean id="shopService" class="com.bjpowernode.service.impl.ShopServiceImpl">
<property name="orderService" ref="remoteOrderService" />
</bean>
// 执行消费者
public class ConsumeApplication {
public static void main(String[] args) throws IOException {
String configLocation = "shop-consumer.xml";
ApplicationContext ac = new ClassPathXmlApplicationContex(configLocation);
((ClassPathXmlApplicationContext) ctx)).start();
ShopService service = (ShopService) ac.getBean("shopService");
Order order = service.buyGoods(1, "手机", 2000, 2);
System.out.println("order:" + order);
}
}
接口抽取和远程调用
接口抽取
关键词:
- 分包:将服务接口、服务模型、服务异常等均放在公共包中
- 粒度:服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo暂未提供分布式事务支持。服务接口应以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。接口应有明确的语义以便于后期维护。
- 版本:每个接口都应定义版本号,为后续不兼容升级提供可能,如:
<dubbo:service interface="com.xxx.XxxService"version="1.0"/>
。建议使用两位版本号,要变更服务版本时,先升级一半提供者为新版本,再将消费者全部升级为新版本,然后将剩下的一半提供者升级为新版本。
实现目标
公共接口包
// 新建 Address实体类
public class Address implements Serializable {
private static final long serialVersionUID=1L;
// 收件人姓名
private String name;
// 城市
private String city;
// 街道
private String street;
// 邮编
private String zipcode;
// 手机号
private String mobile;
// 是否使用此地址
private Boolean use;
//setters and getters, toString
}
// 新建 UserInfoService接口
public interface UserInfoService {
List<Address> queryAddress(Integer userId);
}
用户信息服务提供者
<!-- pom.xml -->
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>node-shop-interface</artifactId>
<version>1.0.0</version>
</dependency>
<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.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
// 创建 UserInfoServiceImpl实现类
public class UserInfoServiceImpl implements UserInfoService {
@Override
public List<Address> queryAddress(Integer userId) {
List<Address> adds = new ArrayList<>();
if(userId == 1) {
Address address = new Address();
address.setName("张三");
address.setCity("北京");
address.setStreet("凉水河二街");
address.setUse(true);
adds.add(address);
} else if(userId == 2) {
Address address = new Address();
address.setName("赵明");
address.setCity("北京");
address.setStreet("西城区西单10号");
address.setUse(true);
// 其他属性赋值
adds.add(address);
}
return adds;
}
}
<!-- dubbo配置文件 -->
<!-- 服务的名称,使用唯一值,服务名称是dubbo内部使用标识服务的 -->
<dubbo:application name="ch04-link-userservice-provider-1" />
<!-- 访问服务的协议名称,端口 -->
<dubbo:protocol name="dubbo" port="20880" />
<!--
暴露的服务,消费者调用此接口的方法
interface:接口的全限定名称
ref:接口的实现类对象id
-->
<dubbo:service interface="com.bjpowernode.service.UserInfoService" ref="userService" registry="N/A" />
<!-- 服务接口的实现对象:功能的真正实现者 -->
<bean id="userService" class="com.bjpowernode.service.impl.UserInfoServiceImpl" />
<!-- web.xml注册 Spring监听器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:userservice-provider.xml</param-value>
</context-param>
<!-- Spring监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
服务消费者
<!-- index.jsp -->
<script type="text/javascript">
var URL_PREFIX="${pagaContext.request.contextPath}";
function buyGoods(userId, name, price, amount) {
window.location.href = URL)PREFIX + "/shop/buy?userId=" + userId
+ "&name=" + name
+ "&price=" + price
+ "&amount" + amount;
}
function showAddress(userId) {
window.location.href = URL_PREFIX + "/shop/address?userId=" + userId;
}
</script>
<div style="margin-left:400px">
<p>
商品列表
</p>
<table border="1" cellpadding="1" cellspacing="1" width="60%">
<tr>
<td>iphone</td>
<td>5000</td>
<td>20</td>
<td><a href="javascript:void(0) onClick='buyGoods(1, iphone, 5000, 1)'"></a>购买</td>
</tr>
</table>
</div>
<div>
<p>
我的收件地址
</p>
<a href="javascript:void(0)" onclick="showAddress(2)">我的收件地址</a>
</div>
<!-- pom.xml -->
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>nod-shop-interface</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<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.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
// 创建商品信息实体类 Goods
public class Goods {
private String name;
private Float price;
private Integer amount;
// setters and getters
}
// 创建接口 ShopService
public interface ShopService {
public Order createOrder(Integer userId, Goods goods);
public List<Address> showAddress(Integer userId);
}
// 创建接口实现类 ShopServiceImpl
public class ShopServiceImpl implements ShopService {
// 远程接口作为属性
private OrderService orderService;
private UserInfoService userInfoService;
public void setOrderService(OrderServcie orderService) {
this.orderService = orderService;
}
public void setUserInfoServcie(UserInfoService userInfoService) {
this.userInfoService = userInfoService;
}
@Override
public Order createOrder(Integer userId, Goods goods) {
Order order = orderService.createOrder(userId, goods.getName(), goods.getPrice.floatValue(), goods.getAmount());
return order;
}
@Override
public List<Address> showAddress(Integer userId) {
List<Address> addresses = userInfoService.queryAddress(userId);
return addresses;
}
}
// 创建类 ShopController
@Controller
@RequestMapping("/shop")
public class ShopController {
@Autowired
private ShopService shopService;
@RequestMapping("/buy")
public ModelAndView buyGoods(Integer userId, Goods goods) {
ModelAndView mv = new ModelAndView();
Order order = shopService.createOrder(userId, goods);
mv.addObject("order", order);
mv.setViewName("view-order");
return mv;
}
@RequestMapping("/addresses")
public ModelAndView showListAddress(Integer userId) {
ModelAndView mv = new ModelAndView();
List<Address> addresses = shopService.showAddress(userId);
mv.addObject("addresses", addresses);
mv.setViewName("view-address");
return mv;
}
}
<!-- view-order.jsp -->
显示订单信息:<br/>
<div align="center" style="margin-left:400px">
view-order.jsp<br/>
<h5>
您的订单已经生成
</h5>
<h5>
订单号:${order.id}
</h5>
<h5>
商品:${order.goodName}
</h5>
</div>
<!-- view-address.jsp -->
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
显示地址信息<br/>
<div style="margin-left:400px">
view-address.jsp<br/>
<c:forEach items="${addresses}" var="address">
<h5>
收件人:${address.name}
</h5>
<h5>
收件地址:${address.city}${address.street}
</h5>
<h5>
联系电话:${address.mobile}
</h5>
<h5>
邮编:${address.zipcode}
</h5>
<h5>
默认地址:${address.use}
</h5>
</c:forEach>
</div>
<!-- Spring配置文件 -->
<!-- 声明组件扫描器 -->
<context:component:scan base-package="com.bjpowernode.controllers" />
<!-- 声明视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 声明注解驱动 -->
<mvc:annnotation-driven />
<!-- dubbo配置文件 -->
<dubbo:application name="ch05-link-consumer" />
<dubbo:reference id="remoteUserService" interface="com.bjpowernode.service.UserInfoService" url="dubbo://localhost:20880" registry="N/A" check="false" />
<dubbo:reference id="remoteOrderService" interface="com.bjpowernode.service.OrderService" url="dubbo://localhost:20881" registry="N/A" check="false" />
<bean id="shopServcie" class="com.bjpowernode.service.impl.ShopServiceImpl">
<property name="orderService" ref="remoteOrderService" />
<property name="userInfoService" ref="remoteUserService" />
</bean>
<!-- web.xml -->
<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:dispatcherServlet.xml,classpath:dubbo-consume.xml</param-value>
<load-on-startuip>1</load-on-startuip>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Dubbo常用标签
Dubbo中常用标签分为三个类别:
- 公用标签
- 服务提供者标签
- 服务消费者标签
公用标签
<dubbo:application />
配置应用信息:<dubbo:application name=“服务的名称”/>
<dubbo:registry />
配置注册中心:<dubbo:registry address=“ip:port” protocol=“协议”/>
服务提供者标签
<dubbo:protocol />
配置访问服务提供者的协议信息:<dubbo:protocol name=“协议名称”port=“端口号”/>
<dubbo:service />
配置暴露的服务:<dubbo:service interface=“服务接口名”ref=“服务实现对象 bean”/>
服务消费者标签
<dubbo:reference />
配置服务消费者引用远程服务:<dubbo:reference id=“服务引用的 bean的 id”interface=“服务接口名”/>
Dubbo常用配置
关闭检查 check
dubbo在缺省状态下启动时会检查依赖的服务是否可用,不可用时会抛出异常,组织 Spring初始化,以便于上线时及早发现问题。默认为 check=true,通过设置 check=false关闭检查。
比如,测试时出现了循环依赖,必须有一方先启动。
请求重试 retries
远程服务调用重试次数,不包括第一次调用,默认是 2 次。
<dubbo:reference retries=“5”/>
超时时间 timeout
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免导致客户端资源耗尽(线程挂起),必须设置超时时间。
timeout = 调用远程服务超时时间(毫秒值)
<!-- 消费者端 -->
<dubbo:reference interface="com.xxx.XxxService" timeout="2000" />
<!-- 生成者端 -->
<dubbo:server interface="com.xxx.XxxService" timeout="2000" />
版本号 version
每个接口都应定义版本号,为后续不兼容升级提供可能。当一个接口有不同的实现类,为了区分不同的接口实现使用 version。特别是项目需要把早期接口的实现全部换位新的实现类的情况。
可以用版本号从早期的接口实现过渡到新的接口实现,版本号不同的服务相互间不引用。
Dubbo注册中心
注册中心概述
对于提供者,它需要发布服务,而且由于应用系统的复杂性,服务数量、类型不断膨胀;对于消费者,它只关心自己如何获取到需要的服务,而面对复杂的应用系统,需要管理大量的服务调用。
而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即既需要提供服务,又需要消费服务。通过将服务统一管理起来,可以有效地优化内部应用对服务发布 / 使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。
Dubbo提供如下可供选择的注册中心:
- Multicast注册中心:组播方式
- Redis注册中心:使用 Redis作为注册中心
- Simple注册中心:就是 dubbo的一个服务,提供查找服务的功能
- ZooKeeper注册中心:使用 Zookeeper作为注册中心
ZooKeeper介绍
Zookeeper是 Apache Hadoop的子项目,是一个高性能的、分布式的、开放源码的树形的目录服务,支持变更推送,适合作为 dubbo服务的注册中心,工业强度高,可用于生成环境。
Zookeeper就像是 Windows的资源管理器,包含了所有的文件。服务提供者和服务消费者都在注册中心中登记。注册中心使用“心跳”机制更新提供者的状态,会自动删除不能使用的提供者。
ZooKeeper安装
ZooKeeper下载
Windows平台安装 ZooKeeper
1)解压下载的文件
2)修改 ZooKeeper-3.6.2/conf/目录下的配置文件
复制 zoo-sample.cfg 改名为 zoo.cfg
文件内容:
- tickTime:心跳时间,毫秒值。ZooKeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是说每个 tickTime时间就会发送一个心跳,表明存活状态。
- dataDir:数据目录,可以是任意目录。存储 ZooKeeper的快照文件, pid文件。默认为 /tmp/zookeeper,建议改为安装目录下的新建文件夹 data
- clientPort:客户端连接 zookeeper的端口,即 zookeeper对外的服务端口,默认为 2181
Linux平台安装 ZooKeeper
Linux平台安装 ZooKeeper需要先安装好 jdk,并配置好 JAVA_HOME
1)解压文件到指定目录下
2)配置文件
在 zookeeper的 conf目录下,将 zoo_sample.cfg 改名为 zoo.cfg
3)类似Windows,修改配置文件
4)切换到 bin目录下启动
5)切换到 bin目录下关闭
使用 ZooKeeper
<!-- 在生产者和消费者处修改 pom.xml -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.1.0</version>
</dependency>
提供者:
<!-- 修改 dubbo配置文件 -->
<dubbo:application name="ch07-zk-userservice-provider"/>
<!-- 创建注册中心 zookeeper -->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!-- 创建访问的协议 -->
<dubbo:protocol name="dubbo" port="20881" />
<dubbo:service interface="com.bjpowernode.service.OrderService" ref="orderService" />
<bean id="orderService" class="com.bjpowernode.service.impl.OrderServiceImpl"/>
消费者:
<!-- 修改 dubbo配置文件 -->
<dubbo:application name="ch08-zk-consumer"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:reference interface="com.bjpowernode.service.OrderService" id="orderService"/>
注册中心的高可用
高可用通常用来描述一个系统经过专门的设计,从而减少不能提供服务的时间,而保持其服务的高度可用性。
ZooKeeper是高可用的、健壮的。ZooKeeper宕机后,正在运行中的 dubbo服务仍然可以正常访问。
健壮性:
- 监控中心宕机后不影响使用,只是丢失部分采样数据
- 注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 服务提供者无状态,任意一台宕机后,不影响使用
- 服务提供者全部宕机后,消费者应用将无法正常使用,并会无限次重连等待服务提供者回复
Dubbo监控中心
什么是监控中心
dubbo的使用,其实只需要有注册中心、消费者、提供者这三者即可,但是并不能看到有哪些消费则会和提供者。为了更好的调试、发现问题、解决问题,引入了 dubbo-admin。通过 dubbo-admin可以对消费者和提供者进行管理,在 dubbo应用部署做动态的调整,服务的管理。
dubbo-admin:图形化的服务管理页面,安装时需要指定注册中心地址,即从注册中心获取到所有的提供者、消费者进行配置管理。
dubbo-monitor-simple:简单的监控中心
发布配置中心
1)下载监控中心
2)可以导入到maven中并进行编译为war包,后发布到tomcat中
3)修改 dubbo-admin下 WEB-INF/dobbo-properties文件,内容如下
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
4)运行dubbo-admin
注意:应先启动注册中心,在启动服务者和消费者,再打开dubbo-admin

Dubbo负载均衡
集群
集群是一种服务器结构,把一组多个计算机,包括硬件和软件组织再一个网络中,相互连接起来共同完成某个工作。对用户来说使用的是同一个计算机。集群对用户来说是透明的。
负载均衡
负载均衡是以集群为前提的,将负载(工作任务)进行平衡、分摊到多个操作单元上进行执行。
对于网络应用而言,并不是一开始就要负载均衡,而是当网络应用的访问量不断增长,单个处理单元无法满足负载需求时,网络应用流量将要出现瓶颈时,负载均衡才会起到作用。一般通过一个或者多个前端负载均衡器,将工作负载分发到后端的一组服务器上,从而达到整个系统的高性能和高可用性。
负载均衡有两方面的含义:首先,单个重负载的工作分配到堕胎服务器做并行处理,每个服务器处理结束后将结果汇总,返回给用户,系统处理能力将大幅度提高,这就是集群(cluster)技术带来的优势。其次,大量的并发访问或数据流量分担到多台服务器分别处理,减少用户等待响应的时间,每个访问分配给不同的服务器处理。
负载策略
Dubbo提供了多种均衡策略,缺省为 random随机调用
- Random LoadBalance随机,按权重设置随机概率,在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
- RoundRbin LoadBalance轮循,按公约后的权重设置轮循比率,。存在满的提供者累积请求问题,如:第二胎机器很慢但是没宕机,当请求调到第二台时就卡在那里,久而久之,所有请求都开在调到第二台上了。
- LeastActive LoadBalance最少活跃调用数,相同活跃数的随机,活跃数指调用前后技术差。使慢的提供者收到更少的请求,因为越慢的提供者的调用前后计数差会越大。
- ConsistentHash LoadBalance一致性 Hash,相同参数的请求总是发送到同一提供者。缺省制度一第一个参数 Hash
配置方式
<dubbo:service interface="com.xxx.XxxService" loadbalance="roundrobin"/>
或者
<dubbo:reference interface="com.xxx.XxxService" loadbalance="roundrobin"/>
- 随机:loadbalance =“round”
- 轮循:loadbalance =“roundrobin”
- 最少活跃:loadbalance =“leastactive”
- 一致性 Hash:loadbalance =“consistenthash”
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗