西安薇電█ 188★2335★0811 ████试管选性别███供卵试管婴儿代孕███选性别生男孩 ████试管包出生████代孕男孩████代孕包出生███代孕选性别██试管婴儿███代孕生男孩█████
1. 针对一个高并发的应用,你是否会选择打印访问日志?
2. 针对分布式的应用,你是否会选择将所有日志打印到日志中心?
解决方案:
1. 如果如果你选择为了性能,不打印日志,那无可厚非。但是你得考虑清楚,出问题的时候是否能够做到快速排查?
2. 你觉得日志分布在各台机器上很方便,那不用日志中心也行!
如果,你还是会选择打印大量的访问日志,如果你还是会选择打印日志到日志中心,那么本文对你有用!
如果自己实现一个日志中心,不说很难吧,也还是要费很大力气的,比如性能,比如容量大小!
所以,本文选择阿里云的 loghub 作为日志中心,收集所有日志!
loghub 常规操作:
在提出本文主题之前,咱们要看看loghub自己的方式,以及存在的问题!
在官方接入文档里,就建议咱们使用 logProducer 接入。
其实 logProducer 已经做了太多的优化,比如当日志包数据达到一定数量,才统一进行发送,异步发送等等!
至于为什么还会存在本篇文章,则是由于这些优化还不够,比如 这些日志发送仍然会影响业务性能,仍然会受到内存限制,仍然会抢占大量cpu。。。
好吧,接入方式:
1. 引入maven依赖:
<dependency> <groupId>com.aliyun.openservices</groupId> <artifactId>aliyun-log-logback-appender</artifactId> <version>0.1.13</version> </dependency>
2. logback中添加appender:
<appender name="LOGHUB-APPENDER" class="appender:com.aliyun.openservices.log.logback.LoghubAppender"> <endpoint>${loghub.endpoint}</endpoint> <accessKeyId>${loghub.accessKeyId}</accessKeyId> <accessKey>${loghub.accessKey}</accessKey> <projectName>${loghub.projectName}</projectName> <logstore>test-logstore</logstore> <topic>${loghub.topic}</topic> <packageTimeoutInMS>1500</packageTimeoutInMS> <logsCountPerPackage>4096</logsCountPerPackage> <!-- 4718592=4M, 3145728=3M, 2097152=2M --> <logsBytesPerPackage>3145728</logsBytesPerPackage> <!-- 17179869184=2G(溢出丢弃) , 104857600=12.5M, 2147483647=2G, 536870912=512M--> <memPoolSizeInByte>536870912</memPoolSizeInByte> <retryTimes>1</retryTimes> <maxIOThreadSizeInPool>6</maxIOThreadSizeInPool> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> </appender> <root level="${logging.level}"> <appender-ref ref="STDOUT"/> <appender-ref ref="LOGHUB-APPENDER" /> </root>
3. 在代码中进行日志打印:
private static Logger logger = LoggerFactory.getLogger(MyClass.class); logger.warn("give me five: {}", name);
看似高效接入,存在的问题:
1. loghub日志的发送是异步的没错,但是当发送网络很慢时,将会出现大量内存堆积;
2. 堆积也不怕,如上配置,当堆积内存达到一定限度时,就不会再大了。他是怎么办到的?其实就是通过一个锁,将后续所有请求全部阻塞了,这想想都觉得可怕;
3. 网络慢我们可以多开几个发送线程嘛,是的,这样能在一定程度上缓解发送问题,但是基本也无补,另外,日志发送线程开多之后,线程的调度将会更可怕,而这只是一个可有可无的功能而已啊;
针对以上问题,我们能做什么?
1. 去除不必要的日志打印,这不是废话嘛,能这么干早干了!
2. 在网络慢的时候,减少日志打印;这有点牵强,不过可以试试!
3. 直接使用异步线程进行日志接收和发送,从根本上解决问题!
4. 如果使用异步线程进行发送,那么当日志大量堆积怎么办?
5. 使用本地文件存储需要进行发送的日志,解决大量日志堆积问题,待网络畅通后,快速发送!
考虑到使用异步线程发送日志、使用本地磁盘存储大量日志堆积,问题应该基本都解决了!
但是具体怎么做呢?
如何异步?
如何存储磁盘?
这些都是很现实的问题!
如果看到这里,觉得很low的同学,基本可以撤了!
下面我们来看看具体实施方案:
1. 如何异步?
能想像到的,基本就是使用一个队列来接收日志写请求,然后,开另外的消费线程进行消费即可!
但是,这样会有什么问题?因为外部请求访问进来,都是并发的,这个队列得线程安全吧!用 synchronized ? 用阻塞队列?
总之,看起来都会有一个并行转串行的问题,这会给应用并发能力带去打击的!
所以,我们得减轻这锁的作用。我们可以使用多个队列来解决这个问题,类似于分段锁!如果并发能力不够,则增加锁数量即可!
说起来还是很抽象吧,现成的代码撸去吧!
1. 覆盖原来的 logProducer 的 appender, 使用自己实现的appender, 主要就是解决异步问题:
<appender name="LOGHUB-APPENDER" class="com.test.AsyncLoghubAppender"> <endpoint>${loghub.endpoint}</endpoint> <accessKeyId>${loghub.accessKeyId}</accessKeyId> <accessKey>${loghub.accessKey}</accessKey> <projectName>${loghub.projectName}</projectName> <logstore>apollo-alarm</logstore> <topic>${loghub.topic}</topic> <packageTimeoutInMS>1500</packageTimeoutInMS> <logsCountPerPackage>4096</logsCountPerPackage> <!-- 4718592=4M, 3145728=3M, 2097152=2M --> <logsBytesPerPackage>3145728</logsBytesPerPackage> <!-- 17179869184=2G(溢出丢弃) , 104857600=12.5M, 2147483647=2G, 536870912=512M--> <memPoolSizeInByte>536870912</memPoolSizeInByte> <retryTimes>1</retryTimes> <maxIOThreadSizeInPool>6</maxIOThreadSizeInPool> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> </appender>