前言
第一次参加这种类似黑客马拉松的比赛,感觉还是挺新奇的。
虽然成绩不咋好,但是毕竟第一次参加,还是记录一下。
成绩
写了大概5天吧。
Score从900 -> 3118 -> 6454 -> 49051 -> 75348 -> 100089 -> 116530
看时间消耗的话,正好卡在20s这儿,排名,6月29号看65名这儿。
前几名都是2s多的都是什么神仙啊。
我看我GC日志,单台机器的STW都不止2s了。
流程
对于聚合服务器而言,就是不断的请求流处理机器,获得出错的日志的traceId,取出交集作为Response响应,发送给所有服务器。
流处理机器获得响应后,再次把需要聚合的所有日志发送给聚合服务器。
而对于流处理的服务器而言:
- 建立与日志文件的Http请求,获得InputStream对象,不断的read,一行一行的获取日志。
- 在内存中建立索引
- 配合聚合服务器做出响应
这里涉及到一个问题,就是怎么判断一个traceId的所有日志能不能丢。
这里比赛放出了个限定条件:
为了方便选手解题,相同traceId的第一条数据(span)到最后一条数据不会超过2万行。方便大家做短暂缓存的流式数据处理。真实场景中会根据时间窗口的方式来处理,超时数据额外处理。
所以基本上,我们不断的read日志的时候,已经有2w条没有出现TraceId=X了,那么就可以认为下面不会再出现TraceId=X的日志了。
如果索引中TraceId=X的日志,没有出现Error,就可以丢掉了。
优化思路
- SpringMVC改成自定义协议,减少Http协议的解析时间,效果:低
- 传输数据使用更好的序列化工具,减少Http传输体积和反序列化时间,效果:低
- HttpClient使用连接池,减少TCP每次都进行握手的时间,效果:低
- 调整GC,很多对象都是朝生夕死,调大年轻代内存,效果:一般
- 分析日志,手写方法提取出spanId和startTime和tag,不用String.split(),效果:显著
- 使用Http Range方法拉取trace文件,分批处理,最后合并checksum的map,效果:显著
- 使用生产者消费者模型,生产者线程拉取Http的Stream,解析成Model,消费者通过阻塞队列拉取,效果:待验证
感触
其实这里有两个性能瓶颈是很容易想到的,因为这里最大的问题就是数据量非常大,单文件2500w行。
- Http请求数据,IO性能
- parse日志,提取spanId
- 建立日志的索引
IO性能这个基本无解,没什么好的优化方法,或者换C++是个好思路(手动狗头)
parse日志这个也基本无解,这里的parse其实还算简单的
建立日志的索引倒是考究很多。
还有个就是不能太迷信多线程,这里简单提一个例子,就是parse日志的过程中,我曾经改成了多线程的方法,但是发现CPU消耗上去了,但是性能几乎没啥变化,本地测甚至还有点下降。
吐槽
官方的题目中,明明写着是面向数据流的,然后还允许通过Range方式拉取,这不打脸么。