基于传染机制的分布式组件
Martian-cloud 是 Martian 的官方分布式组件,基于传染机制,不再需要注册中心
http://mars-framework.com/img/ws-blank.png
这些服务之间是相互独立的,他们无法发现对方,所以我们需要做一些事
比如像这个样子 [图 1 ]
http://mars-framework.com/img/ws-one.png
也可以是这样子 [图 2 ]
http://mars-framework.com/img/ws-two.png
连接方式随意,只要别让任何服务落单即可
我们用图 1 来举例
<dependency>
<groupId>com.github.yuyenews</groupId>
<artifactId>mars-cloud-starter</artifactId>
<version>最新版,具体看《组件介绍》</version>
</dependency>
/*
这个注解的 serverName 跟你要调用的那个服务的 name 一致(配置类里 cloud 配置的 name )
beanName 不写的话,默认为类名首字母小写
*/
@MarsFeign(serverName="mars-demo",beanName="demoFeign")
public interface DemoFeign {
/*
这里面的所有方法,跟你要调用的那个 API 中的方法名一致
*/
返回类型 insert(DemoEntity entity);
/*
可以用 @MarsContentType 注解 来指定本次请求的 ContentType
*/
@MarsContentType(ContentType = ContentType.JSON)
返回类型 selectList(DemoEntity entity);
}
返回类型 result = MarsRestTemplate.request(服务 name,MarsApi 接口的方法名,new Object[]{参数对象 1,参数对象 2},返回类型.class, ContentType.FORM);
补充一下,服务之间发现的过程吧:
比如B的 IP和端口号是 196.168.112:8088.
那么我们可以先在A的配置里把这个路径写死,这样一来,A就知道B在哪里了。 当A启动时会先去B获取接口,B会把自己本地缓存的所有接口都给A(所有的,包括其他微服务的接口),
当A获取到这些接口以后,会从这些接口里提取出ip和端口号,比如提取出来的IP端口号列表为 [127.0.0.1:8088, 192.168.1.113:8090, 196.112.113:9090] , 提取出来以后,A会把自己的接口 广播给这三个服务,这三个服务收到广播以后,会将接口缓存到本地。
--------------------------以上只是启动时的过程。--------------------------
启动以后的过程也差不多:
首先有个定时任务,每隔一段时间都会轮询一次,轮询的过程如下:
遍历上面第3步提取出来的服务列表,从第一个开始 获取此服务上的接口,如果成功了,则停止遍历,如果没成功则继续遍历下一个,知道成功后停止。 如果上面第3步没有提取到接口,那么才会从配置中写死的那个服务中获取。
将第1步获取到的接口 融合到本地缓存中,同时从本地缓存中再次提取ip和端口号,然后将自己的接口 给这些服务发广播。
每个服务在启动时,启动后都是走的上面描述的流程,只要服务没落单,那么他会很快被传染到所有的病毒(接口)
--------------新加一个服务,如何被发现? --------------
很简单,只要在配置中 配置正在运行的任意一个服务的ip和端口号即可。
--------------服务宕机后 接口如何回收? --------------
这个,可以完全放心,我们是提供了解决办法的,有兴趣可以去看一下我的项目
1
xuanbg 2020-11-10 19:39:21 +08:00
没明白的是:A 是怎么连接 B 的?就是 A 使用 Feign 调用了 B 的接口吗?
|
2
NCE 2020-11-10 19:42:00 +08:00 1
接口多了会成为内部 DDOS 攻击么?😂
|
3
fatedier 2020-11-10 19:51:56 +08:00
一旦机器变多,服务变多,感觉是一场灾难,和 gossip 协议一样,小规模用用还可以,但是小规模的场景下,又没必要这么复杂。
|
4
xiaofan2 2020-11-10 20:04:37 +08:00 1
有啥好处?
|
5
Joker123456789 OP @xuanbg 第一次连接 是通过配置写死的,但是服务启动后,这个写死的东西就用不到了。 也就是说 在 A 里面先用配置写死连接到 B,一旦 A 启动以后,B 挂了也没事,因为 B 身上的病毒(接口) 已经传染给 A 了。 此时 A 想跟其他服务产生关系,可以通过传染过来的病毒(接口)进行关联
|
6
Joker123456789 OP @xiaofan2 好处不是写出来了吗? 不需要 注册中心啊,少维护了了一套 zk 或者 nacos 集群,就是好处。
|
7
Joker123456789 OP |
8
Joker123456789 OP @fatedier 嗯~ 可能我没理解你说的意思,也可能是你没理解我说的意思。
这么说吧,即使是现在流行的 分布式架构模式, 每个微服务本地也是缓存了一套接口的。 而我现在只是改变了,服务与服务之间 互相发现的 机制。 其他并没什么变化。 |
9
Joker123456789 OP @xuanbg 再补充一下,写死指的是,告诉 A,B 在哪里, 并不是把 B 的接口在 A 里写死, 千万别误解哦
|
10
xupefei 2020-11-10 20:24:16 +08:00 via iPhone
我没理解错的话,一个用户进来,传染的计算量是指数级的?
|
11
ManjusakaL 2020-11-10 20:26:04 +08:00
又重新实现了一套类 etcd 。。。
而且很多问题没法自恰。。 |
12
Joker123456789 OP @xupefei 如果有 N 个微服务,那么相对于任意一个微服务来说,他最多将自己的接口传染 N 次。 而且是在内网传播,纯内存操作。 传染出去的接口,仅仅是自己的接口,数量相对有限。
什么时候会传染别人的接口呢? 别人主动找他要的时候,才会给。 这样算的话,在一整个微服务中,不可能所有人都找这一个机器要接口吧? 只要稍微设置合理一点就好了 |
13
EminemW 2020-11-10 21:31:12 +08:00
怎么有点像路由表的原理
|
14
xuanbg 2020-11-10 21:52:46 +08:00
@Joker123456789 我倒是没误解,就是没想到 A 的配置文件里面写 B 的地址……扶额
|
15
xuanbg 2020-11-10 21:56:37 +08:00
这太糙了吧……注册中心地址是固定的,而服务的地址是一般是动态的。我们如果玩你这一套,我一下子都不知道怎么玩了都。
@Joker123456789 |
16
Joker123456789 OP @xuanbg 首先第一次部署,就是项目从 0-1 的上线,这个时候,肯定是需要规划 有几台服务器,哪个服务器部署哪个服务吧?
那就好办了啊,你开发的每个微服务,只需要配置一个这批服务器中的任意 ip:port 即可。 其他的,框架会自己解决。 然后如果想新加一个服务,只需要把新加的服务 连接到 正在运行的 任意一个服务即可。 |
17
Joker123456789 OP @xuanbg 对啊,但是这个写死的地址,只在服务启动时 用一下,后面都用不到了。所以 问题不大,哈哈哈。
|
18
Kirsk 2020-11-10 22:53:04 +08:00 via Android
你这个只适合集群 那问题来了 对比注册中心优点在哪 注册中心用来干嘛的? 问题实在太多了 楼主多想想
|
19
lix7 2020-11-10 23:16:10 +08:00 4
就是 gossip 的思想吧,但这么搞怎么实现之前可以通过注册中心去做的那些路由调度、熔断、权重、灰度....
|
21
myCupOfTea 2020-11-11 08:58:20 +08:00
每个服务都多了事,问题太多了吧
|
22
myCupOfTea 2020-11-11 08:58:41 +08:00
服务的下线上线,负载均衡,熔断啥的
|
23
Joker123456789 OP @myCupOfTea 其实你楼上说的 比较符合我的想法。
先来说说 有注册中心的时候: 1. 启动时会从注册中心获取一套接口,缓存在本地。 2. 定时给注册中心发送心跳告诉他 自己还活着。 3. 注册中心会给其他服务发送通知,告诉他们那些服务已经下线了,然后收到通知的服务会从本地删除相应的接口。 而我现在这套思路: 1. 上面第一条 依然保留,只不过他不是从 zk,nacos 获取一套接口,而是从配置文件中配置的的那个服务上获取 2. 上面第二点依然保留,只不过他不是心跳给 zk,nacos,而是心跳给其他服务。 3. 如果自己挂了,他是无法通知别人的,但是每个微服务本地的缓存 都有失效时间的,只要没按时收到心跳就会删除本地缓存 所以整个考虑下来,其实并没有多做什么事。 至于负载均衡,熔断啥的,本身也跟注册中心没啥关系吧, 负载均衡目前我是提供了的, 熔断 还在开发中。 |
24
Joker123456789 OP @Kirsk
可能是我理解的不够透彻吧, 我先说下我的理解,欢迎指正: 1. 注册中心主要做这三件事: 储存接口,服务上线通知, 服务下线通知。 2. 微服务在启动时会从注册中心获取一次接口,然后缓存在本地, 本地缓存的接口在收到 服务上下线通知后会更新。 3. 调用时,都是从本地缓存获取对应的接口,然后按照负载均衡的方式筛选 调用。 所以即使注册中心挂了,也不影响正在运行的微服务。 上面三个点整理出几个关键词:获取接口,本地缓存,上下线通知。 这几个关键词,目前我都是满足的。 其他跟注册中心无关的东西,比如负载均衡,熔断等,都不受影响 ----------------------------------------------------------------------------- 不过肯定还有很多我没想到的细节,欢迎指正与指教 |
25
Sunmxt 2020-11-11 10:54:25 +08:00 via Android
其实就是 gossip 吧?感觉很多问题想得有点粗糙了。就拿楼主提到的心跳机制来说,这个就决定了这个系统没法 scale up 。一个节点要给所有节点定时发心跳,节点数量为 n,网络中一段时间内的消息数量是 O(n^2)的,规模一大网络压力就上来了,很可怕。其他的,诸如一致性问题就不说了。
|
26
ppyzzz 2020-11-11 11:14:21 +08:00
少量服务实例可以用,多实例的话还是不行.轮询都会占不少的 CPU 资源,而且这个单服务 down 了以后时延性以及后面的处理也得考究
|
27
Joker123456789 OP @Sunmxt 是的,所以还有很长的路要走,我需要再好好考虑下细节,不断优化才行。
|
28
Kirsk 2020-11-11 13:18:13 +08:00
@Joker123456789 你只是做了一部分 服务发现 打个比方 通过你的做法 能提高稳定性么 什么场景下注册失败率高 传染机制可行
|
29
myCupOfTea 2020-11-11 13:45:12 +08:00
@Joker123456789 Sunmxt 说的没错,本来 1: n,编程了 n:n.想想感觉会不太好维护的
不过东西都是慢慢试出来的 |
30
tikazyq 2020-11-11 13:57:32 +08:00 1
楼主之前一直在推他开发了近 2 年却只有 200 多 star 的 Java 框架,扬言似乎要取代 SpringBoot,结果被 v2 众佬围观。
这一次又是准备革命分布式计算么?(doge) https://v2ex.com/t/695080 https://v2ex.com/t/695080 |
31
tikazyq 2020-11-11 14:00:29 +08:00 1
看到这里,忍不住贴一个百度百科地址,祝楼主好运 https://baike.baidu.com/item/%E5%94%90%C2%B7%E5%90%89%E8%AF%83%E5%BE%B7/59900
|
32
palmers 2020-11-11 14:00:34 +08:00
我理解 这样仅仅是为了去掉了注册中心 但是 似乎没有一个成熟的追踪路径 这样一旦出现问题 排查起来就非常的麻烦 而且 不维护注册中心 而出现问题的维护成本和维护注册中心带来的成本 我更倾向于维护注册中心 因为维护注册中心更有确定性
|
33
mritd 2020-11-11 14:06:30 +08:00
是个狠人
|
34
wysnylc 2020-11-11 14:14:04 +08:00
传播模式不可靠,而且无法校验"我"是否连上了整个服务而不是连上一个小的闭环
|
35
Joker123456789 OP @tikazyq 有屁可以直接放,2 年只有 200 多个有什么问题吗? 你别忘了,我做的这个领域可不是蓝海,而是被 spring 统治的红海。
你认知中的那些 随便发发文章就上千 star 的项目 1. 要么是 XX 管理系统,帮人偷懒用的 2. 要么只是依附于 spring 的小工具 3. 要么是某科技巨头发布的 4. 要么是蓝海的东西 请问有可比性吗? 最后,我还是那句话,有屁可以直接放,不用拐弯抹角,给我分享个百科是想干嘛?劝我放弃吗? 我还想说你病的不轻呢,你不感兴趣就滚出去,在这大放厥词干嘛? 你要真有脑子,就学学你口中的众佬,说说你的看法。 |
36
Joker123456789 OP @tikazyq 你这种人就是 闲着蛋疼, 看到别人做东西,就过来疯狂嘲笑,我建议你去看看精神科,我认真的建议你。
|
37
UmiKz 2020-11-11 14:38:13 +08:00
楼主的另辟蹊径,让我看到了无数大牛都没想总结的某些技术使用的套路本质,感谢~~~
|
38
woshiaha 2020-11-11 14:42:40 +08:00
拜占庭问题了解一下 这种群体互相检测心跳的方式可能会导致一个服务的状态在 A 的状态是宕机 但是在 B 的状态是存活 没做负载均衡的时候可能问题不大 但是要实现负载均衡的时候就有影响了
|
39
Joker123456789 OP @woshiaha 对,但这个是延迟到问题, 在 B 的状态 很快就会变成宕机的,只要 N 毫秒内没收到心跳,立刻会被判定为宕机。 不过这个延迟是无法避免的,即使是 zk 通知 也是有延迟的。
而且我的 心跳机制并不是群体互相检测的哦,而是有我告诉大家我还活着,并不是你帮我告诉大家我还活着。 这位大佬 [Sunmxt ] ,对我的意思理解的比较透彻,因为提出的问题比较犀利,也是我一直在想办法解决的问题 |
40
tikazyq 2020-11-11 16:22:24 +08:00
古人云:“不因言废人,不因人废言”
因为楼主恼羞成怒,我有些担心自己的言辞有偏见,就专门看了一下这个项目的代码。 不得不佩服,楼主的代码整体非常整洁,代码结构也很合理,在合适的位置配有注释,这个值得表扬,这个项目适合让新人提升阅读理解开源项目的能力。 但缺点也很明显: 1. 喜欢重复造轮子,例如 HTTP 的 Utility 部分竟然有单独重写 URL 参数的方法,这违背了 DRY 原则(重复造轮子的必要性能否解释下?) 2. 框架似乎是用 REST 来做的远程通信,这要求显式配置需要传播的节点 IP 地址和端口,不管是从安全还是效率上都让人堪忧 3. 层层嵌套的方法太多,这种组织方式显得很冗余,大大降低代码可读性,也有降低灵活性易用性的问题 4. 楼主没有写一些基本的部署教程或步骤,只有通过阅读源码来猜测框架是如何启动的,我甚至有些怀疑楼主是否能用这个框架跑通一个 demo,是否在实际分布式环境中做了测试,实际的效率、易用性、扩展性如何 暂时就这些反馈。 总之,除了这些缺点以外,真正让人持怀疑态度的是楼主没有给出令人信服的测试结果,推销你的框架好,总之要给点证据吧,这些都没有在楼主的描述中体现出来。因此,楼主项目不受人待见,是自然而然的事情,真的没必要大动肝火。建议多学习一下其他的框架,而不是搞这些看上去高大上的框架,酒香不怕巷子深,你这个东西好用,自然有人用,你这个东西不好用,你把它吹上天也没人鸟你。所以,楼主加油 |
41
tikazyq 2020-11-11 16:41:33 +08:00 1
另外阐述一下自己的不成熟的观点,真正的大佬不是做了多么牛逼的框架,而是这个框架提高了多少生产力,真正帮助了多少人,产生了多少真实价值。我深深感觉楼主深深陷入了自嗨的模式而不可自拔,所以推荐你去读一下堂吉柯德,如果你还是停留在自我欣赏自己的框架多有前景,自己的技术能力多么 nb,那么可能你只是现代版的堂吉柯德。以上
|
42
Joker123456789 OP @tikazyq
1. 在这个项目中 httpUtil 的作用是啥,您真的有有了解过吗? 仅仅看了一眼这个类就下结论了?? 建议你看清楚他到底是做啥的,我为什么要这么做,了解清楚在说话。 2. 需要显式??? 麻烦你再去看一遍好吗? 看清楚了再下结论。即使你没看过我的文档,至少也看过我现在被你喷的得这篇文章吧,我在文章里介绍的 两种调用方式 有涉及到显式的 URL 吗??有吗? 3. 这个我不知道你指啥,有则改之吧。 4. 请问 我官网的文档被你吃了吗??? 以及我文档里放的 demo 也被你一起吃了吗?? 基于你说的第 2 个点 我甚至怀疑你连这篇文章都没看,就狂喷了。 我现在怀疑你 去看代码仅仅是为了找茬,你带着这个目的去的,那就算了好吗?我不需要你的鼓励和支持,你可以出门左拐,只要别在这大放厥词就行,谢谢您了。 最后,我只对你大动肝火了,因为你没资格说我,却在这大放厥词,是个人都会反击你的。 最后,不受人待见,和没人用是两码事,请你回小学重新学一遍语文吧。 最后的最后,其他人的回复 比你友好多了, 他们都是在谈自己的看法,而不是像你一样大放厥词只会嘲笑和喷粪。 |
43
Joker123456789 OP @tikazyq 哈哈哈哈,自嗨模式?? 我自己花时间做了个东西出来,分享给大家看看,叫自嗨???
我还是那句话,你要是真的有脑子,就学学 你口中的 大佬, 好好学学他们, 说点自己的看法,而不是一个劲的嘲笑。 |
44
Joker123456789 OP 你说的第 2 个点 我在给你耐心说一次:
你见过 mysql 主从吗?? 主从连接是不是显式的配置了 ip ?? 你见过 zk 集群吗? 是不是在配置文件里都配置了 其他节点的 ip ? 还有一点!!!! 这个配置的 ip 和 具体要调用的接口是两码事,两码事,你好好理解一下吧,什么都不懂,就大放厥词。 至于你说的安全性,我就更是哈哈哈哈了,如果黑客都攻击到你服务器上,反编译你的 jar 包去看配置了,还有安全性可言吗??? 如果他没攻上去,配置又怎么会暴露??? 最后在给你说一次,这个 ip 和具体要调用的接口是两码事,两码事。调用接口不需要显式配置,不需要!! |
45
cominghome 2020-11-11 17:05:26 +08:00
感觉“不稳定”因素太多了,相比之下多维护一个注册中心简直香爆
|
46
myCupOfTea 2020-11-11 17:16:29 +08:00
其实显示的 url 也不是问题,url 写成 xxx-service.com 然后用 k8s 路由来映射到对应的 ip 上
|
47
PiersSoCool 2020-11-11 17:37:35 +08:00
就是 gossip 吧
提个问题,假设只有两个节点,A 和 B,启动的时候网络断了,启动之后连接上了,A 和 B 都觉得自己的信息正确,怎么破? |
48
Joker123456789 OP @PiersSoCool 获取接口和发送广播的动作是 轮询的,不是一次性的。 数据会被慢慢纠正。
为什么要用轮询,主要是为了 心跳机制, 不然服务下线了 其他节点是不知道的。 不过这个心跳机制,造成了 在某个时间内,集群里要发送的 消息次数能达到 n*n,对网络会造成一定的压力,所以目前不太适合大规模的微服务。 这也是我下一步要 解决的问题。 |
49
rrfeng 2020-11-11 18:06:39 +08:00 via Android
ES 集群支持类似机制。
|
50
liuhan907 2020-11-12 00:19:32 +08:00 via Android
所以说到底,这不就是最原始的 gossip 么,不带优化的那种…
|
51
Joker123456789 OP @liuhan907 首先呢,我也是发布了这篇文章后才知道 gossip 这个名词的, 后来我专门去大致了解了一下。
发现 gossip 只是将数据传染出去,并没有心跳机制,他比较适合做 [分布式数据存储] 或者 [主从数据存储] 方案,在 redis cluster 里就借鉴了 gossip 的思想。 不过我这个方案 也可以说是借鉴了 gossip 的思想,谁让他比我早呢, 不过你说的不带优化,我是不承认的,我只承认目前这个方案确实不适合大规模的微服务,因为正如 [Sunmxt ] 大神所说, 在某个时间内,整个服务需要发送的消息数量能达到 n*n 。 不过这个并不是没有优化的结果,而是为了心跳机制,我目前能想到方案里,只有这个最稳定,只是有点耗带宽。 我后面的重点,也是想办法解决这个问题,东西嘛,都是慢慢试错试出来的。 |
52
liuhan907 2020-11-13 00:00:49 +08:00 via Android
@Joker123456789 思路有些奇怪。集群成员维护和服务列表维护应该分为两个部分,杂糅在一起不便于扩展和维护。如果你喜欢去中心的协议,建议参考或者使用 consul 开源的 swimming 协议库,名字就叫 memberships 貌似。而服务列表维护可以参考微软开源的 Orleans 如何维护 actor 接口信息的。
另,我说没有优化一是说协议的发包数量,二是指数据同步的收敛速度。 |
53
jeesk 2022-12-24 03:08:13 +08:00
dubbo 早就有了,多播。
|