使用合适的维度拆分来求解问题
合理地分解问题,往往能让问题迎刃而解。
问题
工作中遇到一个问题。
有一些 IP, 有一些主机 H,需要对这些主机进行 IP 封禁,IP 封禁还有对应的端口(Port)和入站出站方向(Direction)。端口需要累加。比如第一次封禁了 (h1,ip1,port1-portN,in), 第二次又封禁了 (h1,ip1,portJ-portM,in),则封禁结果应该是 (h1,ip1,[port1-portN, portJ-PortM],in)。如果有N个IP(ip1-ipm) 对主机 (h1-hm) 封禁,则封禁结果是 m * n 个记录。且端口必须能够叠加。不同封禁方向也是不同的记录。同时,下发给客户端的封禁请求参数应当是每一个主机对其所有IP、端口、封禁方向的封禁信息(可以分批,但不能一个个地发)。
思考
这个问题涉及四个维度:主机、封禁IP、封禁端口、封禁方向。
需要做的事情:
- 在服务端数据库里插入对应的封禁请求记录;
- 向客户端发送封禁请求;
- 根据客户端处理封禁结果来更新封禁请求记录。
显然,不可能一个个去插入记录和发送请求然后更新请求。实现功能似乎并不困难,难的是要考虑性能。要达到性能要求,通常就是“批量、并发、异步、缓存”四件套。
首先从批量处理上思考。最开始思考的是,一次性查出封禁请求里所涉及的所有主机的已有封禁记录,然后与当前封禁请求做对比分析,计算出有哪些主机变更了IP及变更的端口、封禁方向,哪些主机只是新增IP、端口。变更了IP、端口、封禁方向,需要更新已有记录的端口,新增IP、端口则需要插入记录。除了数据库记录,还需要批量向主机发送封禁请求。想想都觉得似乎有点复杂。脑子转不过来。
心里涌出了碎碎念:
我:一定要端口叠加吗?端口叠加会增加问题排查复杂度。因为结果是很多次封禁操作的累加,必须找到所有封禁操作请求,而线上报问题过来很可能会比较晚,可能就找不到了某次历史操作请求,导致难以排查难以定位原因。用户既然要封禁一些IP和端口,如果结果不如用户所料而是多出了一些端口,用户不会觉得奇怪吗?覆盖还是累加究竟哪种场景居多呢?
假想产品经理:端口累加更自然一些。比如一个客户之前封禁了若干端口,现在又封禁一些端口,难道他还要把之前的端口重新封禁一遍?
我:但是这个端口叠加,好几个维度,确实会增加系统复杂度。有点搞不定啊!
假想产品经理:为什么客户端没有问题呢?
我:客户端当然没问题,因为只需要考虑一个主机。对一个主机的IP和端口加加减减就可以了啊。
我:要就此认输吗?十年工程师,这个都搞不定,怎么说得过去呢?
。。。
当没有现成方案来解决问题时,回到原点来思考问题。
绝大多数程序问题,都是结构化问题。程序=数据结构+算法。从技术角度广义来看,软件开发则是数据存储加业务流程的组合,即基本或高级点的 CRUD 加一些稍微复杂点的算法,最终砌成一佗平平无奇的流水线代码(程序员像不像未来的流水线工人呢?)。读者应该还记得之前写过的一篇“软件开发:组织大规模逻辑的技艺”的文章。绝大多数软件问题都是结构化问题。只要厘清结构,设计好算法,再施以一些工程手段(并发、异步、封装、多态、解耦、API、设计模式、容错等),问题就迎刃而解。
想到这里,突然有了一个想法:对啊,既然客户端是考虑一个主机,那么服务端为什么不能也是一次只考虑一个主机呢?因为给客户端发送封禁请求本身就是主机维度的,因此以主机维度来拆分封禁请求,是合理的。如果服务端一次只考虑一个主机,问题不是简化了么?至于多个主机,并发来搞就可以了啊。
之所以没考虑到这一点,是因为上一个版本才做过多台主机对一个IP 封禁,是把多台主机给拆分成多个批次进行处理(一个批次是若干台主机对一个IP),然后下发给客户端的是一个主机一个IP的封禁信息。看来是拆错了维度。要是上次就是一个批次是以一个主机一个IP的话,这次加个并发不就完事了么?上次之所以按照 IP 拆分为维度,是因为只有一个IP,会有很多台主机,如果一次处理一台主机一个IP的话,假设有 10w 台主机,那可想见的慢了。
求解
有了思路,就感觉豁然开朗了。
针对每个主机是容易处理的,因为单个封禁记录是以(Host, IP, Direction) 为唯一标识的,因此一个主机的一次封禁请求,只需要计算出针对一台主机有哪些唯一标识有变更,做一些加减即可。至于所有主机,开个并发慢慢搞。在一个主机一个批次里,数据库插入记录可以批量处理,而给客户端发送请求则只要一次操作。所以说,异步、批量、并发,是处理大量操作类请求(数据库访问+API请求)的利器。如果还不够,缓存来凑。
小结
遇到多维度问题时,选择一个合适的维度进行拆分,往往可以让问题迎刃而解。
应用性能问题,应用层求解通常就是“异步、并发、批量、缓存”四件套,数据层求解则是索引、分库分表分片。四管齐下,大部分性能问题都能解决,如果还有,要么是中间件的调优、要么是精简流程。读者可阅:“互联网应用服务端的常用技术思想与机制纲要”
你说这人吧,遇到费脑的不愿思考,整天写流水线代码也没意思,有时还是要有点挑战性的事情才行。