信息产业如何把握时代机遇?
|
负载均衡其实就是解决一个分活的问题,对应到分布式系统,一般在逻辑服的前面都会安放一个负载均衡器,比如NGINX就是经典的解决方案。负载均衡不限于分布式系统,对于多线程架构的服务器内部,也需要解决负载均衡的问题,让各个worker线程的负载均衡。 ### 多线程、协程并行化 虽然硬件架构的复杂化对程序开发提出了更高的要求,但编写充分利用多CPU多核特性的程序能获得令人惊叹的收益,所以,在同样硬件规格下,基于多线程/协程的并行化改造依然值得尝试。 多线程不可避免要面临资源竞争的问题,我们的设计目标应该是充分利用硬件多执行核心的优势,减少等待,让多个执行流畅快的奔跑起来。 对于多线程模型,如果把每一个要干的活抽象为一个task,把干活的线程抽象为worker,那么,有两种典型的设计思路,一种是对task类型做出划分,让一类或者一个worker去干特定的task,另一种是让所有worker去干所有task。 第一种划分,能减少数据争用,编码实现也更简单,只需要识别有限的竞争,就能让系统工作的很好,缺点是任务的工作量很可能不同,有可能导致有些worker忙碌而另一些空闲。 第二种划分,优点是能均衡,缺点是编码复杂性高,数据竞争多。 有时候,我们会综合上述两种模式,比如让单独的线程去做IO(收发包)+反序列化(产生protocol task),然后启动一批worker线程去处理包,中间通过一个task queue去连接,这即是经典的生产者消费者模型。 协程是一种用户态的多执行流,它基于一个假设,即用户态的任务切换成本低于系统的线程切换。 ### 通知替代轮询 轮询即不停询问,就像你每隔几分钟去一趟宿管那里查看是否有信件,而通知是你告诉宿管阿姨,你有信的时候,她打电话通知你,显然轮询耗费CPU,而通知机制效率更高。 ### 添加缓存 缓存的理论依据是局部性原理。 一般系统的写入请求远少于读请求,针对写少读多的场景,很适合引入缓存集群。 在写数据库的时候同时写一份数据到缓存集群里,然后用缓存集群来承载大部分的读请求,因为缓存集群很容易做到高性能,所以,这样的话,通过缓存集群,就可以用更少的机器资源承载更高的并发。 缓存的命中率一般能做到很高,而且速度很快,处理能力也强(单机很容易做到几万并发),是理想的解决方案。 CDN本质上就是缓存,被用户大量访问的静态资源缓存在CDN中是目前的通用做法。 ### 消息队列 消息队列、消息中间件是用来做写请求异步化,我们把数据写入MessageQueue就认为写入完成,由MQ去缓慢的写入DB,它能起到削峰填谷的效果。 消息队列也是解耦的手段,它主要用来解决写的压力。 ### IO与逻辑分离、读写分离 IO与逻辑分离,这个前面已经讲了。读写分离是一种数据库应对压力的惯用措施,当然,它也不仅限于DB。 ### 批处理与数据预取 批处理是一种思想,分很多种应用,比如多网络包的批处理,是指把收到的包攒到一起,然后一起过一遍流程,这样,一个函数被多次调用,或者一段代码重复执行多遍,这样i-cache的局部性就很好,另外,如果这个函数或者一段里要访问的数据被多次访问,d-cache的局部性也能改善,自然能提升性能,批处理能增加吞吐,但通常会增大延迟。 另一个批处理思想的应用是日志落盘,比如一条日志大概写几十个字节,我们可以把它缓存起来,攒够了一次写到磁盘,这样性能会更好,但这也带来数据丢失的风险,不过通常我们可以通过shm的方式规避这个风险。 指令预取是CPU自动完成的,数据预取是一个很有技巧性的工作,数据预取的依据是预取的数据将在接下来的操作中用到,它符合空间局部性原理,数据预取可以填充流水线,降低访存等待,但数据预取会侵害代码,且并不总如预期般有效。
哪怕你不增加预取代码,硬件预取器也有可能帮你做预取,另外gcc也有编译选项,开启它会在编译阶段自动插入预取代码,手动增加预取代码需要小心处理,时机很重要,最后一定要基于测试数据,另外,即使表现很好,但代码修改也有可能导致效果衰减,而且预取语句执行本身也有开销,只有预取的收益大于预取的开销才是值得的。 (编辑:四平站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
