why-为什么需要Dubbo?
单一应用架构 –> 垂直应用架构 –> 分布式服务架构 –> 流动计算架构 –> RPC
单一应用架构:
当流量很小时,只需要一个应用,所有模块都部署在一台服务器即可满足要求且开发、运维简单、数据库CRUD简单
垂直应用架构
当流量逐渐增大,单一应用的增加机器无法满足访问量需求,需要将应用进行垂直拆分为多个互不相干的应用,以提升效率。
分布式服务架构
当垂直应用越来越多,应用之间不可避免的要交互,将核心业务抽离出来作为独立的服务,使前端能够快速的响应多变的市场需求。
面向服务的架构(SOA)
随着服务越来越多,服务之间的调用和依赖变得复杂,因此诞生了面向服务的架构体系,也衍生除了一系列相应的技术来简化这些服务间的调用,如Dubbo
what-Dubbo主要有哪些能力?
- 面向接口代理的高性能RPC调用,开发者可以就像调用本地方法一样调用远程方法
- 智能负载均衡,内置多种负载均衡策略
- 服务自动注册与发现(依靠Zookeeper)
- 高度可扩展能力
- 运行期流量调度
- 可视化的服务治理与运维
how-怎么做到的
在典型的RPC使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中RPC
指明了程序如何进行网络传输和序列化
这时典型的RPC基础架构
- 都有一个注册中心,server来注册服务,client发现服务
- client调用模块中有负载均衡、容错机制、透明等
- 在client中会对数据序列化、编码后进行网络传输,在server接收到调用请求后会进行解码、反序列化后执行方法调用
-
节点角色说明
- Provider:暴露服务的服务提供方
- Consumer:调用远程服务的服务消费方
- Registry:服务注册与服务发现的注册中心
- Monitor:统计服务的调用次数和调用时间的监控中心
- Container:服务运行容器
-
调用关系说明
- 服务容器负责启动,加载,运⾏服务提供者。
- 服务提供者在启动时,向注册中⼼注册⾃⼰提供的服务。
- 服务消费者在启动时,向注册中⼼订阅⾃⼰所需的服务。
- 注册中⼼返回服务提供者地址列表给消费者,如果有变更,注册中⼼将基于⻓连接推送变更数
据给消费者。 - 服务消费者,从提供者地址列表中,基于软负载均衡算法,选⼀台提供者进⾏调⽤,如果调⽤
失败,再选另⼀台调⽤。 - 服务消费者和提供者,在内存中累计调⽤次数和调⽤时间,定时每分钟发送⼀次统计数据到监
控中⼼。
Dubbo 入门Demo
-
创建api模块,供provider、consumer调用
import java.io.Serializable; /** * 订单 */ public class OrderDTO implements Serializable { private Long orderId; private String orderNo; private String desc; public Long getOrderId() { return orderId; } public void setOrderId(Long orderId) { this.orderId = orderId; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public OrderDTO(Long orderId, String orderNo, String desc) { this.orderId = orderId; this.orderNo = orderNo; this.desc = desc; } @Override public String toString() { return "Order{" + "orderId=" + orderId + ", orderNo='" + orderNo + '\'' + ", desc='" + desc + '\'' + '}'; } }
创建OrderService接口
import com.siqi.dubbo.api.model.OrderDTO; /** * 订单服务 **/ public interface OrderService { OrderDTO getOrder(String orderNo); }
-
创建Provider模块
-
pom.xml 中增加依赖
<!--依赖于api模块--> <dependency> <groupId>com.siqi</groupId> <artifactId>dubbo-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-zookeeper</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-metadata-report-zookeeper</artifactId> <version>2.7.3</version> </dependency>
-
配置provider
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-xml-provider" /> <dubbo:registry id="registry" address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20880"/> <dubbo:service interface="com.siqi.dubbo.api.service.OrderService" ref="orderService"/> <bean id="orderService" class="com.siqi.dubbo.service.OrderServiceImpl"/> <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/> </beans>
-
创建OrderService接口的实现类OrderServiceImpl
import com.siqi.dubbo.api.model.OrderDTO; import com.siqi.dubbo.api.service.OrderService; /** * 订单服务 * **/ public class OrderServiceImpl implements OrderService { @Override public OrderDTO getOrder(String orderNo) { System.out.println("Provider OrderService getOrder start, orderNo:" + orderNo); OrderDTO orderDTO = new OrderDTO(1L, "no1", "订单1"); System.out.println("Provider OrderService getOrder end, orderNo:" + orderNo + ", orderDTO:" + orderDTO.toString()); return orderDTO; } }
-
dubbo-provider模块启动类
import org.springframework.context.support.ClassPathXmlApplicationContext; public class DubboXMLProvider { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-provider.xml"); context.start(); System.out.println("xml provider 启动ing"); System.in.read(); } }
在provider模块中会对外暴露OrderServiceImpl服务
-
-
dubbo-consumer模块
-
pom.xml增加依赖
<!--引入api模块--> <dependency> <groupId>com.siqi</groupId> <artifactId>dubbo-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-zookeeper</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-metadata-report-zookeeper</artifactId> <version>2.7.3</version> </dependency>
-
配置consumer
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.siqi.dubbo.user.impl" /> <dubbo:application name="demo-xml-consumer" /> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:reference id="orderService" interface="com.siqi.dubbo.api.service.OrderService" check="false"/> </beans>
-
在consumer模块中的UserServiceImpl中RPC调用另一个模块的OrderServiceImpl
public interface UserService { String getOrderInfo(String orderNo); }
import com.siqi.dubbo.api.model.OrderDTO; import com.siqi.dubbo.api.service.OrderService; import com.siqi.dubbo.user.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Autowired private OrderService orderService; @Override public String getOrderInfo(String orderNo) { System.out.println("Consumer UserService getOrderInfo start, orderNo:" + orderNo); OrderDTO orderDTO = orderService.getOrder(orderNo); System.out.println("Consumer orderService.getOrderInfo end, orderNO:" + orderNo + ",orderDTO:" + orderDTO); return orderDTO.toString(); } }
-
-
启动一个zookeeper注册中心
-
启动DubboXMLProvider
-
启动DubboXMLConsumer
-
最终可以看到订单服务调用成功且就像调用本地api一样方便
Dubbo 负载均衡策略
- Random LoadBalance
- 随机,按权重设置随机概率。
- 在⼀个截⾯上碰撞的概率⾼,但调⽤量越⼤分布越均匀,⽽且按概率使⽤权重后也⽐较均匀,有利于动态调整提供者权重。
- RoundRobin LoadBalance
- 轮询,按公约后的权重设置轮询⽐率。
- 存在慢的提供者累积请求的问题,⽐如:第⼆台机器很慢,但没挂,当请求调到第⼆台时就卡在那,久⽽久之,所有请求都卡在调到第⼆台上。
- LeastActive LoadBalance
- 最少活跃调⽤数,相同活跃数的随机,活跃数指调⽤前后计数差。
- 使慢的提供者收到更少请求,因为越慢的提供者的调⽤前后计数差会越⼤。
- ConsistentHash LoadBalance
- ⼀致性 Hash,相同参数的请求总是发到同⼀提供者。
- 当某⼀台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
算法参⻅: http://en.wikipedia.org/wiki/Consistent_hashing
缺省只对第⼀个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />
缺省⽤ 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />
扩展
API与SPI
API(Application Programming Interface):大多数情况下,都是实现方定制接口并完成接口的不同实现,调用方仅仅使调用,无法定制
SPI(Service Provider Interface):调用方来定制实现接口,服务方调用接口,常见的例子有:
- 数据库驱动Driver
- 日志Log
- Dubbo扩展点开发
- Spring Boot自动装配机制
JDK SPI与Dubbo SPI的区别
JDK SPI:
-
配置:
jdk.impl.Dog jdk.impl.Cat
-
原理:
- 通过读取META-INF/services路径读取配置文件
- 获取到多个类的全路径名
- 使用类加载器加载类的全路径
-
不足:
- 无法按需加载。如果仅需要一个实现类,ServiceLoader还是会将接口的实现类全部载入并实例化一遍,如果我们只需要一个,其它不需要的,这样就会造成资源消耗浪费
- 不具备IOC功能,如果一个接口实现类依赖复杂,如何实例化?
- serverLoader非线程安全
Dubbo SPI:
- 配置
man=dubbo.impl.Man woman=dubbo.impl.Woman
- 原理:
- 先从缓冲中获取实例,如果没有则创建实例,接下去的创建实例都是线程安全的
synchronized
代码块 - 加载读取配置文件的所有 class,保存在一个
Map<String, Class<?>>对象
- 根据不同的name读取接口实现类class
- 利用dubbo ioc注入接口实现类的实例
- 初始化dubbo组件生命周期并维护到缓存中
- 先从缓冲中获取实例,如果没有则创建实例,接下去的创建实例都是线程安全的
泛化调用及原理
泛化调用就是不需要依赖于接口或api模块的jar包,可以按照类的全限定名进行RPC调用
// API⽅式 引⽤远程服务
// 该实例很重要,⾥⾯封装了所有与注册中⼼及服务提供⽅连接,请缓存
ReferenceConfig<GenericService> reference = new referenceConfig<GenericService>();
// 弱类型接⼝名
reference.setInterface("com.end.dubbo.api.service.OrderService");
// 声明为泛化接⼝
reference.setGeneric(true);
// ⽤com.alibaba.dubbo.rpc.service.GenericService可以替代所有接⼝引⽤
GenericService genericService = reference.get();
Object result = genericService.$invoke("getOrder", new String[] {java.lang.String" }, new Object[]{ "ccc" });
System.out.println(result);
总结
本文介绍了dubbo的发展历史以及为什么需要dubbo,并使用一个最简单的demo搭建一个dubbo项目;并介绍了dubbo的SPI实现原理、泛化调用等概念。在下一篇将深入介绍dubbo源码分析
参考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/17844.html