分布式系统服务单点问题的探讨

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/houzhuoming1/article/details/85040635

分布式系统服务单点问题的探讨


在分布式系统中,单点问题是一个比较常见的问题,对于单点问题可以分为有状态服务的单点问题和无状态服务的单点问题。

无状态服务的单点问题

对于无状态的服务,单点问题的解决比较简单,因为服务是无状态的,所以服务节点很容易进行平行扩展。比如,在分布式系统中,为了降低各进程通信的网络结构的复杂度,我们会增加一个代理节点,专门做消息的转发,其他的业务进行直接和代理节点进行通信,类似一个星型的网络结构。
无状态单点
无状态多节点
参考上面两个图,图中proxy是一个消息转发代理,业务进程中的消息都会经过该代理,这也是比较场景的一个架构。在上图中,只有一个proxy,如果该节点挂了,那么所有的业务进程之间都无法进行通信。由于proxy是无状态的服务,所以很容易想到第二个图中的解决方案,增加一个proxy节点,两个proxy节点是对等的。增加新节点后,业务进程需要与两个Proxy之间增加一个心跳的机制,业务进程在发送消息的时候根据proxy的状态,选择一个可用的proxy进行消息的传递。从负载均衡的角度来看,如果两个proxy都是存活状态的话,业务进程应当随机选择一个proxy。
那么该解决方案中会存在什么问题呢?
主要存在的问题是消息的顺序性问题。一般来说,业务的消息都是发送、应答,再发送、再应答这样的顺序进行的,在业务中可以保证消息的顺序性。但是,在实际的应用中,会出现这样一个情况:在业务进程1中,有个业务需要给业务进程3发送消息A和消息B,根据业务的特性,消息A必须要在消息B之前到达。如果业务进程1在发送消息A的时候选择了proxy1,在发送消息B的时候选择了proxy2,那么在分布式环境中,我们并不能确保先发送的消息A一定就能比后发送的消息B先到达业务进程3。那么怎么解决这个问题?其实方案也比较简单,对于这类对消息顺序有要求的业务,我们可以指定对应的proxy进行发送,比如消息A和消息B都是使用proxy1进行发送,这样就可以保证消息A比消息B先到达业务进程3。
整体来说,对于无状态的服务的单点问题的解决方案还是比较简单的,只要增加对应的服务节点即可。

有状态服务的单点问题

相对无状态服务的单点问题,有状态服务的单点问题就复杂多了。如果在架构中,有个节点是单点的,并且该节点是有状态的服务,那么首先要考虑的是该节点是否可以去状态,如果可以,则优先选择去除状态的方案(比如说把状态存储到后端的可靠DB中,可能存在性能的损耗),然后就退化成了一个无状态服务的单点问题了,这就可以参考上一节的方案了。
但是,并不是所有的服务都是可以去状态的,比如说对于一些业务它只能在一个节点中进行处理,如果在不同的节点中处理的话可能会造成状态的不一致,这类型的业务是无法去除状态的。对于这种无法去除状态的单点的问题的解决方案也是有多种,但是越完善的方案实现起来就越复杂,不过整体的思路都是采用主备的方式。
简单主备
第一个方案就是就是增加一个备用节点,备用节点和业务进程也可以进行通信,但是所有的业务消息都发往Master节点进行处理。Master节点和Slave节点之间采用ping的方式进行通信。Slave节点会定时发送ping包给Master节点,Master节点收到后会响应一个Ack包。当Slave节点发现Master节点没有响应的时候,就会认为Master节点挂了,然后把自己升级为Master节点,并且通知业务进程把消息转发给自己。该方案看起来也是挺完美的,好像不存在什么问题,Slave升级为Master后所有的业务消息都会发给它。但是,如果在Master内部有一些自己的业务逻辑,比如说随机生成一些业务数据,并且定时存档。那么当Master和Slave之间的网络出现问题的时候,Slave会认为Master挂了,就会升级为Master,同样会执行Master的相应的业务逻辑,同样也会生成一些业务数据回写到DB。但是,其实Master是没有挂的,它同样也在运行对应的业务逻辑(即使业务进程的消息没有发给旧的Master了),这样就会出现两个Master进行写同一份数据了,造成数据的混乱。所以说,该方案并不是一个很好的方案。
那么怎么解决可能会出现多个Master的问题?
换个角度看,该问题其实就是怎么去裁决,哪个节点是Master的问题。
方案一:引入第三方的服务进行裁决。
我们可以引入ZooKeeper,由ZooKeeper进行裁决。同样,我们启动两个主节点,“节点A”和节点B。它们启动之后向ZooKeeper去注册一个节点,假设节点A注册的节点为master001,节点B注册的节点为master002,注册完成后进行选举,编号小的节点为真正的主节点。那么,通过这种方式就完成了对两个Master进程的调度。
ZooKeeper托管主节点

ZooKeeper
ZooKeeper有一套机制,可以保证不会出现多个Master的情况,具体可以参考:
https://segmentfault.com/a/1190000012185322
方案二: 通过选举算法和租约的方式实现Master的选举
对于方案一的缺点主要要多维护一套ZooKeeper的服务,如果原本业务上并没有部署该服务的话,要增加该服务的维护也是比较麻烦的事情。这个时候我们可以在业务进程中加入Master的选举方案。目前有比较成熟的选举算法,比如Paxos和Raft。然后再配合租约机制,就可以实现Master的选举,并且确保当前只有一个Master的方案。但是,这些选举算法理解起来并不是那么地容易,要实现一套完善的方案也是挺难的。所以不建议重复造轮子,业内有很多成熟的框架或者组件可以使用,比如微信的PhxPaxos。
Paxos选举
比如上图的方案中,三个节点其实都是对等的,通过选举算法确定一个Master。为了确保任何时候都只能存在一个Matster,需要加入租约的机制。一个节点成为Master后,Master和非Master节点都会进行计时,在超过租约时间后,三个节点后可以发起“我要成为Master”的请求,进行重新选举。由于三个节点都是对等的,任意一个都可以成为Master,也就是说租期过后,有可能会出现Master切换的情况,所以为了避免Master的频繁切换,Master节点需要比另外两个节点先发起自己要成为Master的请求(续租),告诉其他两个节点我要继续成为Master,然后另外两个节点收到请求后会进行应答,正常情况下另外两个节点会同意该请求。关键点就是,在租约过期之前,非Master节点不能发起“我要成为Master”的请求,这样就可以解决Master频繁切换的问题。

展开阅读全文

关于(伪)分布式数据检索请教与探讨

08-16

现在搞一套 省 市 县 多级独立部署运营的平台,但数据检索时,每个独立平台都能检索到其他节点平台上的数据,rn现在想的方案有两种:rn一种是各个独立平台的元数据信息,定时同步汇聚到一个总库中,每个独立平台检索数据时,检索这个总库就行了,rn另一种方案是, 平台开放数据检索接口, 其一平台检索时,分别向其他平台发送检索请求,然后汇聚结果返回给前端。rn总感觉这两种方案都不是特别完善,也或者是本人能力有限,没有更好的处理方法,特向各位兄弟请教。rn下面是为了方便大家对问题的理解,我画的一个图, 里面采用了分布式索引搜索引擎的一些东西做参考,大家不一定非得参照这个的考虑,rnrn[img=https://img-bbs.csdn.net/upload/201308/16/1376624662_658164.jpg][/img]rnrn容我再赘述一下,是这样子的, 搞的是一套资源管理平台产品。rn这套产品可能在多个市 、多个县 都可能分别独立部署。rn这个产品除了资源这块,还有其他功能,也就是说各个独立的平台中用户不一样,数据等都不一样,rn而需求就是,我其中任意一个平台的用户在检索资源时,rn比如A市平台的一个用户,在自己的平上,要能检索到所有其他平台上的数据,比如B省其他平台的资源。rn不知道明白我的意思了么。rnrnDFS:分布式存储文件系统。rnLucene\hadoop: apache下面的子项目,做全文检索等分布式搜索引擎相关的框架rnelasticsearch:基于Lucene实现,跟apache的solr类似的功能。 论坛

关于异或问题探讨探讨

06-26

原贴rnhttp://topic.csdn.net/u/20110511/14/778edaef-bf88-46fb-b7f6-e26b1c966ab9.htmlrnrn楼rn#15rn#18rnrn好吧,我可能没有解释清楚,鉴于原贴已经结贴,这里解释一下:rnrn直接写1)a=a+b;rn b=a-b;rn a=a-b;rn或者 2)a=a*b;rn b=a/b;rn a=a*b;rn的结果1是对的,2一般不对rnrnrn补码表示的相同长度整数a,brn+-的情况是对的,而且C语言这种情况的溢出,一般不会引发异常。rn结果正确并不是凑巧,因为补码表示就是同余意义上的余数(如int a,b,是对模2^32的同余),+-法就是完全剩余系上的同余加减rnrn因为有 a + b === a+b(mod m) a - b === a-b(mod m)rn且 a * b === a*b (mod m) 但是不一定有a mod m / b mod m === a/b mod mrnrn所以*/的一般不对,你可以说*会溢出,而/会丢失精度,其实溢出并不是主要问题,丢失精度才是。rn按照补码表示的分析, 普通整数的*/在int下并不是逆运算,在那个帖子里面没有给a,b的范围或者说类型,所以在下并没有写出rna=a*b;rnb=a/b;rna=a*b;rn这种式子。rnrnint下a*b的逆运算应该是a*b^(-1) 其中b^-1是b对模2^32的数论倒数,也就是任意一个x, 满足b * x % 2^32 == 1,当然这种x可能不存在,例如b=2。这时候可以做变换,b=2, ~b=-3,这又是另一个话题。rnrn小结是:只要满足op1, op2在a,b的整数类型下是可逆的,同时op1是可交换的:rn 对于所有可能的a,b:rn a op1 b = c 能推出 c op2 b = a (1)rn a op2 b = d 能推出 d op1 b = a (2)rn a op1 b = b op1 a (3)rnrn那么rna = a op1 brnb = a op2 brna = a op2 brn能够交换a,b的值rn简证:rn设a = a', b = b'rn1: a 变为 a' op1 b'rn2: b 变为 a' op1 b' op2 b' = a' (根据 (1)rn3: a 变为 a' op1 b' op2 a' = b' op1 a' op2 a' = b' 根据(3)交换 根据(2)化简rnrn所以有3步以后a 变为了 b', b变为了a'rn证毕rnrnrn这样的两个操作op1, op2,能够交换a,b,而不需要考虑其他问题,如剩余系上的加减法没有的溢出问题,这是来自数学的说明,而不是计算机工业的说明。rnrnrnrnrnrn 论坛

CreateService 创建系统服务 问题

03-31

现在正在研究怎么编写windows服务,有个问题想请教大家:rn [code=C/C++]SC_HANDLE schService = CreateService( rn schSCManager,// SCManager databasern pName, // name of service rn pName,// service name to display rn SERVICE_ALL_ACCESS,// desired access rn SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS , // service type rn SERVICE_AUTO_START,// start type rn SERVICE_ERROR_NORMAL, // error control type rn pPath, // service's binaryrn NULL, // no load ordering grouprn NULL, // no tag identifierrn NULL, // no dependenciesrn NULL, // LocalSystem accountrn NULL //passwordrn ); [/code] rnrn现在我想做一个用来监听端口的服务程序,但发现几个问题:rn 1、服务器端,我编写了一个windows服务,在服务进程中开来个线程来监听某个端口,接受客户端了连接消息。 rn 2、现在服务能正常运行,测试是,客户端在本机,即连接“127.0.0.1”,服务器端能正常接受到客户端的连接。 rn 问题是:我用另外一台机子测试时,连接ip为"192.168.0.12",此时报“10060”错误。 rn rn 同样的代码,我拿出来,直接放在应用程序里是可以正常工作的。 rnrn我怀疑是CreateServcie();使用不正确,这种情况下最后三个参数该怎么设置?rn最后三个参数依次为:rn 服务依赖(不知是不是这样) rn 账户名 rn 密码rn但我试了下都都不成功,说“参数不正确”。rn希望得到大家的指点。rn 论坛

没有更多推荐了,返回首页