首先,整理笔记之前,特此感谢一下Dubbo的官方文档,因为里面好多内容都是和官方文档离不开的。
PART1:Dubbo概念篇
说白了Dubbo就是人家大公司自己出品的一款高性能、轻量级的开源 Java RPC 框架。 Dubbo 不光可以帮助我们调用远程服务,还提供了一些其他开箱即用的功能比如智能负载均衡
,咱自己写的那个功能都,可能都谈谈不上功能,都是打印个啥出来就完了),以及SOA服务治理方案。
实现基于产品应用程序
的一种自然模式。基于产品的应用程序是已经打包好并且拥有不同版本,可作为第三方插件下载
的。然后,很多公司也在开发、发布自己内部商业应用像有版本号、说明及可加载插件式的应用软件(这也是这种模式的特征)
。微内核系统可让用户添加额外的应用如插件到核心应用,继而提供了可扩展性和功能分离的用法。【常见的一些IDE,都可以看作是基于微内核架构设计的。绝大多数 IDE比如IDEA、VSCode都提供了插件来丰富自己的功能。】微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期
。Dubbo基于微内核架构,才使得我们可以随心所欲替换Dubbo的功能点。比如你觉得Dubbo 的序列化模块实现的不满足自己要求,没关系啊!你自己实现一个序列化模块就好了啊
!
Dubbo 不想依赖 Spring 等 IoC 容器,也不想自己造一个小的 IoC 容器(过度设计),因此采用了一种最简单的 Factory 方式管理插件 :JDK 标准的 SPI 扩展机制 (java.util.ServiceLoader)【ServiceLoader是一种加载服务实现的工具】
服务的 Provider 和 Consumer 只在启动时与注册中心交互
。注册中心通过长连接【长连接与短连接】感知 Provider 的存在【也就是防止你服务提供者突然挂了】,在 Provider 出现宕机的时候,注册中心会立即推送相关事件通知 Consumer
。
从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用
,如果调用失败,再选另一台调用–>服务消费者Consumer和提供者Provider,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心Monitor。
Dubbo的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务
。
官方推荐用Zookeeper作为注册中心
(也可以用Redis、Nacos等作为注册中心,得先安装并配置,然后启动、停止、查看状态等)。基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更
。推荐使用 Zookeeper 作为注册中心采用 key/map 存储,key 存储服务名和类型,map 中 key 存储服务 url,value 服务过期时间
。基于 Redis 的发布/订阅模式通知数据变更基于 XML 配置以及基于注解配置
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
服务失效踢出基于Zookeeper的临时节点原理
在Provider启动的时候
,会向 Registry 进行注册操作,将自己服务的地址和相关配置信息封装成 URL 注册到或者叫添加到 ZooKeeper 中,从而暴露自己的服务, Consumer 也是通过 URL 来确定自己订阅了哪些 Provider 的。
。然后 服务消费者通过负载均衡策略选择唯一的一个服务提供者的URL
,从而进行远程服务的调用。如何提供服务
、提供的服务名称是什么
、需要接收客户端或者说远程服务调用者发来的什么参数
、需要返回给客户端或者说远程服务调用者什么响应
;Invoker 就是 Dubbo 对远程调用的抽象
。基于 XML 配置以及基于注解配置
在Consumer启动的时候,会向 Registry 注册中心进行订阅操作并监听自己关注的Provider
。订阅操作会从 ZooKeeper 中获取
Provider 注册的 URL
,并在 ZooKeeper 中添加相应的监听器
。获取到 Provider URL 之后,Consumer 会根据负载均衡算法从多个 Provider 中选择一个 Provider 并与其建立连接
,最后发起对 Provider 的 RPC 调用。 如果 Provider URL 发生变更,Consumer 将会通过
之前订阅过程中在注册中心添加的监听器
,获取到最新的 Provider URL 信息
,进行相应的调整,比如断开与宕机 Provider 的连接,并与新的 Provider 建立连接。用于统计服务的调用次数和调用时间
。Provider 和 Consumer 在运行过程中,会在内存中统计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
。监控中心在上面的架构图中并不是必要角色
,监控中心宕机不会影响 Provider、Consumer 以及 Registry 的功能,只会丢失监控数据而已
。
负责加载、运行服务提供者
。必须有。被消费者调用的服务的地址注册到注册中心
)注册到Register(也就是告诉注册中心,我有一个服务,别人可以来调用我了
)Dubbo的总体的调用过程
:Dubbo 的工作原理
调用远程方法像调用本地的方法一样简单的一个关键
,真实调用过程依赖代理类,以 ServiceProxy 为中心。三大核心功能
(Dubbo的三大核心能力
)主要包含:或者说Dubbo的作用:
面向接口的远程方法调用
):dubbo-remoting模块, 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。Dubbo中提供了很多集群容错模式或者集群容错方式,或者说可靠、智能的容错和负载均衡【以下几种集群容错措施可以根据实际的业务场景进行配置选择,而且 Dubbo 给我们提供了 Cluster 扩展接口,我们可以自己定制集群的容错模式。】
。集群容错是指服务消费者调用服务提供者集群时发生异常时的处理方案。以 Dubbo 框架为例,提供了六种内置的集群容错措施。
Failover 是 Dubbo 默认的集群容错措施
。当出现调用失败时,会重新尝试调用其他服务节点,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作,但重试会带来更长延迟。
基于注册中心目录服务,使服务消费方能动态的查找服务提供方
(服务自动注册和发现能力),使地址透明,使服务提供方可以平滑增加或减少机器。
异步调用
。Dubbo是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。http
:基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实现
。多个短连接,传输协议 HTTP,对传输数据包不限,传入参数大小混合,提供者个数多于消 费者,
Dubbo SPI可以帮助我们动态寻找服务/功能(比如负载均衡策略)的实现
。】
我们将接口的实现类放在配置文件中,我们在程序运行过程中或者在服务加载的时候读取配置文件,通过反射加载实现类。这样,我们可以在运行的时候,动态替换接口的实现类
。和 IoC 的解耦思想是类似的。
当某个应用引入了该 jar 包且需要使用该服务时,JDK SPI 机制就可以通过查找这个 jar 包的 META-INF/services/ 中的配置文件来获得具体的实现类名【SPI 机制的具体实现本质上还是通过反射完成的。即:我们按照规定将要暴露对外使用的具体实现类在 META-INF/services/ 文件下声明。】,进行实现类的加载和实例化,最终使用该实现类完成业务功能。
】专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口
。服务接口和具体的服务实现分离
开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。我们如何扩展 Dubbo 中的默认实现呢
?
比如说我们想要实现自己的负载均衡策略(咱们自己的负载均衡策略就是DemoByHuLoadBalance类中的逻辑呗),我们创建对应的实现类 DemoByHuLoadBalance 实现 LoadBalance 接口或者 AbstractLoadBalance 类
。package com.xxx;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.RpcException;
public class DemoByHuLoadBalance implements LoadBalance {
public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
// ...
}
}
src
|-main
|-java
|-com
|-xxx
|-XxxLoadBalance.java (实现LoadBalance接口)
|-resources
|-META-INF
|-dubbo
|-org.apache.dubbo.rpc.cluster.LoadBalance (纯文本文件,内容为:xxx=com.xxx.DemoByHuLoadBalance)
Dubbo的SPI
其实是对java的SPI进行了一种增强
,可以按需加载实现类之外,增加了 IOC 和 AOP 的特性,还有自适应扩展机制
。
Dubbo的SPi和JAVA的SPI有什么区别
?
我要是只用得上其中一个,是不是实例化全部就显得有点浪费
。
在每次类加载的时候会先去找到 class 相对目录下的 META-INF 文件夹下的 services 文件夹下的文件,将这个文件夹下面的所有文件先加载到内存中,然后根据这些文件的文件名和里面的文件内容找到相应接口的具体实现类
**,找到实现类后就可以通过反射去生成对应的对象,保存在一个 list 列表里面,所以可以通过迭代或者遍历的方式拿到对应的实例对象,生成不同的实现。
需要遍历 SPI 配置文件中定义的所有实现类
,该过程中会将这些实现类全部实例化
。如果 SPI 配置文件中定义了多个实现类,而我们只需要使用其中一个实现类时,就会生成不必要的对象,不就导致资源的浪费了嘛
。Dubbo SPI 不仅解决了上述资源浪费的问题,还对 SPI 配置文件扩展和修改
。
Dubbo 将 SPI 配置文件改成了 KV 格式
按照 SPI 配置文件的用途
分为了三类目录:
兼容 Java SPI
。用户自定义
的 SPI 配置文件。使用的 SPI 配置文件
。只需要所有要传输的实体类或者说所有的POJO都要implements Serializable接口
就行了
Dubbo 默认使用的序列化方式是 hessian2
。
如果调用的是其他语言开发的服务的时候就不支持了
。主要原因是 JDK 自带的序列化方式序列化之后的字节数组体积较大,导致传输成本加大
。有跨语言需求
的话可以考虑使用。
可以直接通过Dubbo配置中的version版本来控制多个版本即可
。//老版本 version=1.0.0, 新版本version=1.0.1
<dubbo:service interface="com.xxxx.rent.service.IDemoService" ref="iDemoServiceFirst" version="1.0.0"/>
<dubbo:service interface="com.xxxx.rent.service.IDemoService" ref="iDemoServiceSecond" version="1.0.1"/>
有哪些负载均衡策略,也可以回答这四种负载均衡策略
)【负载均衡算法可以是多种多样的,客户端可以记录例如健康状态、连接数、内存、CPU、Load 等更加丰富的信息,根据综合因素进行更好地决策。】Random
:按权重随机,默认值,按权重设置随机概率
Round-Robin
:按权重轮询【Round-Robin 是最简单有效的负载均衡策略,并没有考虑服务端节点的实际负载水平,而是依次轮询服务端节点
】LeastActive
:最少活跃调用数(最后一次处理请求所花的时间。客户端根据服务端节点当前的连接数进行负载均衡,客户端会选择连接数最少的一台服务器进行调用
),相同活跃数的随机ConsistentHash
:一致性Hash,相同参数的请求总是发到同一提供者
。目前主流推荐的负载均衡策略,在服务端节点扩容或者下线时,尽可能保证客户端请求还是固定分配到同一台服务器节点。Consistent Hash 算法是采用哈希环来实现的,通过 Hash 函数将对象和服务器节点放置在哈希环上,一般来说服务器可以选择 IP + Port 进行 Hash,然后为对象选择对应的服务器节点,在哈希环中顺时针查找距离对象 Hash 值最近的服务器节点。负载均衡可以从配置文件中指定,也可以在管理后台进行配置修改
。事实上,它支持 服务端服务/方法级别、客户端服务/方法级别 的负载均衡配置。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载【其实就是说我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题】
。使用具有负载平衡而不是单个组件的多个组件可以通过冗余提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。Dubbo 提供了多种均衡策略【在 Dubbo 中,所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些公共的逻辑。】,默认为 random 随机调用
。我们还可以自行扩展负载均衡策略(参考Dubbo SPI机制)
。public abstract class AbstractLoadBalance implements LoadBalance {
static int calculateWarmupWeight(int uptime, int warmup, int weight) {
}
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
}
protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
int getWeight(Invoker<?> invoker, Invocation invocation) {
}
}
随机负载均衡(对加权随机算法的实现)。这是Dubbo默认采用的一种负载均衡策略
。
public class RandomLoadBalance extends AbstractLoadBalance {
public static final String NAME = "random";
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size();
boolean sameWeight = true;
int[] weights = new int[length];
int totalWeight = 0;
// 下面这个for循环的主要作用就是计算所有该服务的提供者的权重之和 totalWeight(),
// 除此之外,还会检测每个服务提供者的权重是否相同
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight;
weights[i] = totalWeight;
if (sameWeight && totalWeight != weight * (i + 1)) {
sameWeight = false;
}
}
if (totalWeight > 0 && !sameWeight) {
// 随机生成一个 [0, totalWeight) 区间内的数字
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
// 判断会落在哪个服务提供者的区间
for (int i = 0; i < length; i++) {
if (offset < weights[i]) {
return invokers.get(i);
}
}
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
}
最小活跃数负载均衡,相同活跃数的随机。活跃数指调用前后计数差
【使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。】。初始状态下所有服务提供者的活跃数均为 0(每个服务提供者的中特定方法或者叫服务都对应一个活跃数【活跃数是通过 RpcStatus 中的一个 ConcurrentMap 保存的,根据 URL 以及服务提供者被调用的方法的名称,我们便可以获取到对应的活跃数。也就是说服务提供者中的每一个方法的活跃数都是互相独立的。】,每收到一个请求后,对应的服务提供者的活跃数 +1,当这个请求处理完之后,活跃数 -1
。)
因此,Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样的话,我就优先把请求给活跃数少的服务提供者处理
。】public class LeastActiveLoadBalance extends AbstractLoadBalance {
public static final String NAME = "leastactive";
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size();
int leastActive = -1;
int leastCount = 0;
int[] leastIndexes = new int[length];
int[] weights = new int[length];
int totalWeight = 0;
int firstWeight = 0;
boolean sameWeight = true;
// 这个 for 循环的主要作用是遍历 invokers 列表,找出活跃数最小的 Invoker
// 如果有多个 Invoker 具有相同的最小活跃数,还会记录下这些 Invoker 在 invokers 集合中的下标,并累加它们的权重,比较它们的权重值是否相等
for (int i = 0; i < length; i++) {
Invoker<T> invoker = invokers.get(i);
// 获取 invoker 对应的活跃(active)数
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
int afterWarmup = getWeight(invoker, invocation);
weights[i] = afterWarmup;
if (leastActive == -1 || active < leastActive) {
leastActive = active;
leastCount = 1;
leastIndexes[0] = i;
totalWeight = afterWarmup;
firstWeight = afterWarmup;
sameWeight = true;
} else if (active == leastActive) {
leastIndexes[leastCount++] = i;
totalWeight += afterWarmup;
if (sameWeight && afterWarmup != firstWeight) {
sameWeight = false;
}
}
}
// 如果只有一个 Invoker 具有最小的活跃数,此时直接返回该 Invoker 即可
if (leastCount == 1) {
return invokers.get(leastIndexes[0]);
}
// 如果有多个 Invoker 具有相同的最小活跃数,但它们之间的权重不同
// 这里的处理方式就和 RandomLoadBalance 一致了
if (!sameWeight && totalWeight > 0) {
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexes[i];
offsetWeight -= weights[leastIndex];
if (offsetWeight < 0) {
return invokers.get(leastIndex);
}
}
}
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}
}
public class RpcStatus {
private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS =
new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();
public static RpcStatus getStatus(URL url, String methodName) {
String uri = url.toIdentityString();
ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.computeIfAbsent(uri, k -> new ConcurrentHashMap<>());
return map.computeIfAbsent(methodName, k -> new RpcStatus());
}
public int getActive() {
return active.get();
}
}
ConsistentHashLoadBalance 中没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者
。官方详细的源码分析
哈希算法最简单的做法就是进行取模运算,比如分布式系统中有 3 个节点,基于 hash(key) % 3 公式对数据进行了映射
。如果客户端要获取指定 key 的数据,通过下面的公式可以定位节点:hash(key) % 3,如果经过上面这个公式计算后得到的值是 0,就说明该 key 需要去第一个节点获取
。但是哈希算法有一个很致命的问题,如果节点数量发生了变化,这样会导致大部分映射关系改变,也就是在对系统做扩容或者缩容时,必须迁移改变了映射关系的数据,否则会出现查询不到数据的问题
。【如果我们对分布式系统进行缩容,比如移除一个节点,也会因为取模哈希函数中基数的变化,可能出现查询不到数据的问题。】
就需要我们进行迁移数据,比如节点的数量从 3 变化为 4 时,要基于新的计算公式 hash(key) % 4 ,重新对数据和节点做映射
。假设总数据条数为 M,哈希算法在面对节点数量变化时,最坏情况下所有数据都需要迁移,所以它的数据迁移规模是 O(M),这样数据的迁移成本太高了。所以,我们应该要重新想一个新的算法,来避免分布式系统在扩容或者缩容时,发生过多的数据迁移。所以一致性哈希算法应运而生,一致性哈希算法就很好地解决了分布式系统在扩容或者缩容时,发生过多的数据迁移的问题
。一致哈希算法是对 2^32 进行取模运算,是一个固定的值。可以把一致哈希算法是对 2^32 进行取模运算的结果值组织成一个圆环,就像钟表一样,钟表的圆可以理解成由 60 个点组成的圆,而此处我们把这个圆想象成由 2^32 个点组成的圆,这个圆环被称为哈希环
。【在一致哈希算法中,如果增加或者移除一个节点,仅影响该节点在哈希环上顺时针相邻的后继节点,其它数据也不会受到影响
。】一致性哈希算法虽然减少了数据迁移量,但是存在节点分布不均匀的问题
。】,这样就会带来一个问题,会有大量的请求集中在一个节点上。在这种节点分布不均匀的情况下,进行容灾与扩容时,哈希环上的相邻节点容易受到过大影响,容易发生雪崩式的连锁反应。所以,往下看因为实际中我们没有那么多节点。所以这个时候我们就加入虚拟节点,也就是对一个真实节点做多个副本
。】除了会提高节点的均衡度,还会提高系统的稳定性
。当节点变化时,会有不同的节点共同分担系统的变化,因此稳定性更高。比如,当某个节点被移除时,对应该节点的多个虚拟节点均会移除,而这些虚拟节点按顺时针方向的下一个虚拟节点,可能会对应不同的真实节点,即这些不同的真实节点共同分担了节点变化导致的压力。而且,有了虚拟节点后,还可以为硬件配置更好的节点增加权重,比如对权重更高的节点增加更多的虚拟机节点即可。因此,带虚拟节点的一致性哈希方法不仅适合硬件配置不同的节点的场景,而且适合节点规模会发生变化的场景加权轮询负载均衡。轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上
。比如假如有两个提供相同服务的服务器 S1,S2,S1的权重为7,S2的权重为3。如果我们有 10 次请求,那么 7 次会被 S1处理,3次被 S2处理
。但是,如果是 RandomLoadBalance 的话,很可能存在10次请求有9次都被 S1 处理的情况(概率性问题)。Dubbo 中的 RoundRobinLoadBalance 的代码实现被修改重建了好几次,Dubbo-2.6.5 版本的 RoundRobinLoadBalance 为平滑加权轮询算法
统一的且在网上唯一的地址
,该地址就叫 URL(Uniform Resource Locator,统一资源定位符),它是互联网的统一资源定位标志,也就是指网络地址。在实践中一般会使用域名,而不是使用具体的 host 和 port
。一般在 GET 请求中会将参数放到 URL 中,POST 请求会将参数放到请求体中
。Dubbo 中任意的一个实现都可以抽象为一个 URL
,Dubbo 使用 URL 来统一描述了所有对象和配置信息
,并贯穿在整个 Dubbo 框架之中:PART2:Dubbo实战篇
这边自己学习这个Dubbo时,也就是先用Spring和SpringMVC搭了个小框架(过程解释的不详细,可以看看blog的spring全家桶中关于Spring和SpringMVC的文章,里面有相关概念以及相关配置的解释),然后写两个类,一个用来模拟服务消费者,一个用来模拟服务提供者【服务提供者Provicer模块和服务消费者Consumer模块(版本二中或者说实际项目中这两个其实是两个项目,分别要部署在两台机器上)
】,然后就是两个版本,从本地类之间的调用这个版本(通过MAVEN依赖、Spring注入等实现两个类的调用)升级到
远程两个类的调用版本(通过Dubbo相关的配置)。
因为之前这个版本两个模拟项目中的service是不能单独启动的(咱们只是用了maven分模块的办法,让这两个模块产生了依赖,从而一个调用另一个),而人家分布式项目中每个单独的项目都是可以单独启动的
<dependency>
<groupId>org.apache.dubbo groupId>
<artifactId>dubboartifactId>
<version>${dubbo.version}version>
dependency>
<dependency>
<groupId>org.apache.curator groupId>
<artifactId>curator-frameworkartifactId>
<version>${ zookeeper.version}version>
dependency>
<dependency>
<groupId>org.apache.curator groupId>
<artifactId>curator-recipesartifactId>
<version>${zookeeper.version}vers ion>
dependency>
得把之前是jar项目的项目改成war项目,war项目才能单独启动
。并且那你想单独启动你内部肯定得集成tomcat,咱们用springboot时是因为人家springboot帮咱们内部已经集成好了tomcat,所以咱们在pom.xml中没有手动集成tomcat而已
。其实就是@Service
(标志这个类是服务层的一个bean,spring的IOC容器你赶紧来,赶紧把我加载进去到你的IOC容器中,你会别人会来调用我的)改一下
,因为我现在这个模拟服务提供者的service类不需要放到spring的IOC容器中了,我们需要把这个服务提供者的IP、端口、访问路径告诉Dubbo,dubbo记录到zookeeper中,然后等待服务消费者来进行调用,还是通过@Service,只不过是另一个包下的
(@Service//将这个类提供的方法(服务)对外发布。将访问的地址,也就是ip,端口,路径注册到dubbo中的注册中心中,由dubbo中的zookeeper记录下来;只不过不能用spring的包扫描了,得用dubbo的包扫描了
)那你怎么找到咱们电脑中安装的zookeeper呢
,这是在spring的配置文件中配置的主要用于项目技术栈版本管理
。【总的来说就是:创建一个maven项目,名称为spring-cloud-alibaba-example,去除src文件,修改pom文件】<?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>ah.wideth</groupId>
<artifactId>spring-cloud-alibaba-example</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<com-alibaba-cloud.version>2.2.7.RELEASE</com-alibaba-cloud.version>
</properties>
<!--对项目版本进行管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${com-alibaba-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
后续创建的项目都放到此目录下,只需要声明groupId和artifactId,会自动引用父项目spring-cloud-alibaba-example的版本
。与其说是父项目,不如说是 根项目
: 因为下面每学习一个新的技术,就会新建一个真正的父项目,而在对应的父项目下面又会创建许多的子项目。启动Nacos,启动服务提供者和服务消费者,调用服务消费者的getInfo方法,服务提供者会返回结果
PART3:dubbo-admin:管理平台,图形化的服务管理页面
配置重试次数
,不配置retries或者配置为0,都会重试两次,只有配置为 -1 或更小,才会不执行重试。采用标签形式:不配置retries会重试两次,配置为0或更小都不会重试。相比起Dubbo简单的四个模块,SpringCloud则是一个完整的分布式一站式框架,他有着一样的服务注册中心,服务提供者,服务消费者,管控台,断路器,分布式配置服务,消息总线,以及服务追踪等
;
巨人的肩膀:
moon聊技术
B站黑马视频
Dubbo官方文档
https://mp.weixin.qq.com/s/_5YMfQK1tmYbmRMldBPlaQ
javaGuide老师关于RPC的好文章
Github :https://github.com/apache/incubator-dubbo
官网:https://dubbo.apache.org/zh/
从 Motan 看 RPC 框架设计:http://kriszhang.com/motan-rpc-impl/
Motan 中文文档:https://github.com/weibocom/motan/wiki/zh_overview
Github:https://github.com/grpc/grpc
官网:https://grpc.io/
官网:https://thrift.apache.org/
Thrift 简单介绍:https://www.jianshu.com/p/8f25d057a5a9