应用无状态
有对外访问的业务,公司自己搭了一套代理服务换IP。处理业务问题时,发现即使是同一个用户,连续的访问,代理IP也在一直变。就检查了代码,发现了一些问题。主要提一下应用无状态这个设计思想。
why and how
以这边的业务为例,需要维护一个可用的IP池,设计上应该尽量满足均衡使用,IP不可用时换新或者等待再次可用。均衡使用和无状态没多大关系不多提,这里就只关心IP可用性的维护。
一个很简单的做法就是,直接新定义一个数组,数组内的元素就是一个IP对象,给每个IP对象一个属性标记是否可用。这么做就是一个有状态的应用,每个IP的状态都保留在这个活动应用实例内。这么做是部分OK的,如果业务量不大,并且是单实例运行的话。
实际上目前应用开发普遍都用到了集群(听说达内都开始教docker了XD)。为了保证服务的高可用,一般生产环境都会通过k8s或者swarm配置多个应用副本,既可以分流,又可以保证在部分副本出现问题时,仍有副本可以正常提供服务(同时等待集群启动新的替换副本)。
因此,如果设计了有状态的应用,一个最大的问题就是,同一个应用的不同副本之间,状态是不方便直接沟通的。就如这个例子里,实际上是生成了双副本,那么用户访问时,第一次访问到了实例A,发现IP被封了,实例A就把自己维护的IP状态改成不可用;用户第二次访问时,如果再次访问到实例A,那么这个失效IP不会给用户,是正确的,如果访问到实例B,实例B并不知道这个IP已经失效,仍然给到了这个IP,就是错误的,没有起到维护可用IP池的功能了。而这个分流是随机的,如果没有提前考虑好,后续随机出现问题,排查起来还是很不方便的。
现在常用的方案是,需要保存的状态存放在redis内;应用保持无状态,需要的时候就去redis读写。
当然后续也有高并发下状态一致性的问题,解决方案也是根据具体业务需求来的,这一业务只要求大致准确、失败时最终检出,也就没有加锁,直接先读判断,bool取反,优先改成false即可。其他业务就看自己需求,这里不展开了喔。