[小心得]对erlang(函数式语言)的新理解——有关gen_server, supervisor的小小理解

时间:2022-06-01 21:03:31

今天我一同事把困扰他多天的问题解决了,在谈论他解决问题的原因时启动了这个话题:

      关于对数据处理的问题:我先举个小例子来说明下要解决的是什么样的问题:说当一大堆数据过来时首先由项目A来接收然后进行简单的处理,把处理后的数据再发给项目B进行深层次的处理。在由A发给B的过程中需要用什么机制进行管理,B怎么来消化这些数据(假定数据是大量的)。

      由于数据是大量的(A发过来的数据不稳定,这一刻可能多,下一刻可能少),可能A过来数据的速度比B处理的速度快,这样就会导致有很多的数据规程到B的“邮件列表”里,而当B邮件列表时的数据增大到一定程度时,就会导致B的处理速度下降,这样“恶性循环”就形成了。当到达某一定程序之后这个结点就会死掉,或者我们不得不把结点重启。那怎么解决这个问题呢?

      于是缓冲就用上了,我们在A和B之间加一个缓冲池(我们用redis来做这件事)。于是过程变成这样了:A过来的数据存放到redis中,B不断的从redis中取出数据进行后面的计算,于是这样就解决了把B接收数据gen_server中的“邮件列表”撑大,撑死的问题。到目前为止我们的观点是一样的;我们的分歧是在后面:

      上面的那个问题暂时是解决了,但还没有从根本上把问题搞定。上面只是解决了B中“邮件列表”的问题,但如果B的处理能力一直小于A的发送能力就会导致erdis的数据不断变大;或者虽然B的处理能力大于A的发送能力,但还是可以给B一个好一点的处理机制,就是在B的处理机制上我们有分歧。

       开始的时候我们几个观点都不同,后来他们渐渐统一了思想,只有我一个人坚持原来的观点。之前的分歧不说了(也不是重点)就说最后我的观点和其他人观点的不同吧:

  在B中首先有一个取数据的管理模块,它从redis里取数据然后发送给处理环节。处理环节有两种方式,一种是开始时启动一个supervisor,它来启动N个gen_server,每个gen_server都会等待数据过来,过来数据后就开始工作,再增加一个超时机制,如果多长时间没有过来数据的话,就让它自动死掉。管理模块可以控制gen_server的数量(比如在A--->B,A<B, 即gen_server数量最小为A,最大为B),可以根据情况来增加或减少gen_server(这儿可以用这个办法:当gen_server小于A时,就停止减少,当gen_server大于B时就停止增加);另一种是每次manager取出N条数据,然后生成一个supervisor,这个supervisor再生成M个child gen_server,然后开始处理,gen_server也有超时机制,如果一段时间没有接收到数据就自己死掉,当这些数据处理完之后,所有的gen_server死掉后,让supervisor也死掉。

  有一种情况,supervisor每生成一个子gen_server就会在它的一个进程表里增加一条记录,也就是说如果supervisor生成的子gen_server太多的话,会导致这个进程表的数据太大,到了一定程度就会出问题。对了,我开始是主张第一次方法的,我认为只要有办法解决这个进程表数据增多的问题,就可以用这种方法,而且我认为gen_server作为erlang的内置框架是可以有办法实现这种功能的。

但最后这句话打动了我:如果启动进程池用的资源很小的话,就可以让它频繁的重启。要学会用函数式编程的思想来解决问题。

下面说的这个和上面的无关是另一个话题:所谓的“模块不要为框架买单”:

所谓gen_server就是一个小小的框架,如果你在它进而实现逻辑功能的话,当逻辑功能改变时,需要修改gen_server源码,更新时,系统会自动重启gen_server,这样可能会导致数据的丢失,如果你把gen_server中的数据都放到一个专门的模块里,逻辑功能改变时,你只需要重新修改、编译这个模块,只升级这个模块的话,gen_server不会重启。