写代码如何合理使用和优化我们的机器资源(CPU、内存、网络、磁盘)

写代码脑子一定要绷紧一根弦,认知到我们所在的机器资源是有限的。机器资源有哪些?CPU、内存、网络、磁盘等,如果不做好保护控制工作,一旦某一资源满负荷,很容易导致出现线上问题。

1 CPU 资源怎么限制

* 
计算算法优化。如果服务需要进行大量的计算,比如推荐排序服务,那么务必对你的计算算法进行优化,比如笔者曾经对地理空间距离计算这一重度使用的算法进行了优化,取得了较好的效果,详见《地理空间距离计算优化》一文。
* 
锁。对于很多服务而言,没有那么多耗费计算资源的算法,但 CPU 使用率也很高,这个时候需要看看锁的使用情况,我的建议是如无必要,尽量不用显式使用锁。
* 
习惯问题。比如写循环的时候,千万要检查看看是否能正确退出,有些时候一不小心,在某些条件下就成为死循环,很著名的案例就是《多线程下HashMap的死循环问题》。比如集合遍历时候使用性能较差的遍历方式、String + 检查,如果有超过多个 String 相加,是否使用 StringBuffer.append?
* 
尽量使用线程池。通过线程池来限制线程的数目,避免线程过多造成的线程上下文切换的开销。
* 
JVM 参数调优。JVM 参数也会影响 CPU 的使用,如《发布或重启线上服务时抖动问题解决方案》。

2 内存资源怎么限制

* 
JVM 参数设置。通过 JVM 参数的设置来限制内存使用,JVM 参数调优比较靠经验,有一篇朋友写的好文可以参考《Linux 与 JVM 的内存关系分析》。
* 
初始化 Java 集合类大小。使用 Java 集合类的时候尽量初始化大小,在长连接服务等耗费内存资源的服务中这种优化非常重要。
* 
使用内存池/对象池
* 
使用线程池的时候一定要设置队列的最大长度。之前看过好多起故障都是由于队列最大长度没有限制最后导致内存溢出。
* 
如果数据较大避免使用本地缓存。如果数据量较大,可以考虑放置到分布式缓存如 Redis、Tair 等,不然 gc 都可能把自己服务卡死。
* 
对缓存数据进行压缩。比如之前做推荐相关服务时,需要保存用户偏好数据,如果直接保存可能有 12G,后来采用短文本压缩算法直接压缩到 6G,不过这时一定要考虑好压缩解压缩算法的 cpu 使用率、效率与压缩率的平衡,一些压缩率很高但是性能很差的算法,也不适合线上实时调用。有些时候直接使用 probuf 来序列化之后保存,这样也能节省内存空间。
* 
清楚第三方软件实现细节,精确调优。在使用第三方软件时,只有清楚细节后才知道怎么节约内存,这点我在实际工作中深有体会,比如之前在阅读过lucene的源码后发现我们的索引文件原来是可以压缩的,而这在说明文档中都找不到,具体参考《lucene索引文件大小优化小结》一文。

3 网络资源怎么限制

* 
减少调用的次数。经常看到有同学在循环里用 redis/tair 的 get,如果意识到这里面的网络开销的话就应该使用批量处理;又如在推荐服务中经常遇到要去多个地方去取数据,一般采用多线程并行去取数据,这个时候不仅耗费cpu资源,也耗费网络资源,一种在实际中常常采用的方法就是先将很多数据离线存储到一块 ,这时候线上服务只要一个请求就能将所有数据获取。
* 
减少传输的数据量。一种方法是压缩后传输,还有一种就是按需传输,比如经常遇到的 getData(int id),如果我们返回该 id 对应的 Data 所有信息,一来人家不需要,二来数据量传输太大,这个时候可以改为 getData(int id, Listfields),使用方传输相应的字段过来,服务端只返回使用方需要的字段即可。

4 磁盘资源怎么限制

打日志要控制量,并定期清理。1)只打印关键的异常日志;2)对日志大小进行监控报警。我有一次就遇到了第三方服务挂了,然后我这边就不断打印调用该第三方服务异常的日志,本来我的服务有降级方案,如果第三方服务挂了会自动使用其它服务,但是突然收到报警说我服务挂了,登上机器一看才知道是磁盘不够导致的崩溃;3)定期对日志进行清理,比如用 crontab,每隔几天对日志进行清理;4)打印日志到远端,对于一些比较重要的日志可以直接将日志打印到远端HDFS文件系统里;
备注
基础原则……控制资源的使用
posted @ 2018-11-23 10:17  艺术攻城狮  阅读(783)  评论(0编辑  收藏  举报