0%

天池比赛经历

前言

第一次参加这种类似黑客马拉松的比赛,感觉还是挺新奇的。

虽然成绩不咋好,但是毕竟第一次参加,还是记录一下。

成绩

写了大概5天吧。

Score从900 -> 3118 -> 6454 -> 49051 -> 75348 -> 100089 -> 116530

看时间消耗的话,正好卡在20s这儿,排名,6月29号看65名这儿。

前几名都是2s多的都是什么神仙啊。
我看我GC日志,单台机器的STW都不止2s了。

流程

对于聚合服务器而言,就是不断的请求流处理机器,获得出错的日志的traceId,取出交集作为Response响应,发送给所有服务器。

流处理机器获得响应后,再次把需要聚合的所有日志发送给聚合服务器。

而对于流处理的服务器而言:

  1. 建立与日志文件的Http请求,获得InputStream对象,不断的read,一行一行的获取日志。
  2. 在内存中建立索引
  3. 配合聚合服务器做出响应

这里涉及到一个问题,就是怎么判断一个traceId的所有日志能不能丢。

这里比赛放出了个限定条件:

为了方便选手解题,相同traceId的第一条数据(span)到最后一条数据不会超过2万行。方便大家做短暂缓存的流式数据处理。真实场景中会根据时间窗口的方式来处理,超时数据额外处理。

所以基本上,我们不断的read日志的时候,已经有2w条没有出现TraceId=X了,那么就可以认为下面不会再出现TraceId=X的日志了。

如果索引中TraceId=X的日志,没有出现Error,就可以丢掉了。

优化思路

  1. SpringMVC改成自定义协议,减少Http协议的解析时间,效果:低
  2. 传输数据使用更好的序列化工具,减少Http传输体积和反序列化时间,效果:低
  3. HttpClient使用连接池,减少TCP每次都进行握手的时间,效果:低
  4. 调整GC,很多对象都是朝生夕死,调大年轻代内存,效果:一般
  5. 分析日志,手写方法提取出spanId和startTime和tag,不用String.split(),效果:显著
  6. 使用Http Range方法拉取trace文件,分批处理,最后合并checksum的map,效果:显著
  7. 使用生产者消费者模型,生产者线程拉取Http的Stream,解析成Model,消费者通过阻塞队列拉取,效果:待验证

感触

其实这里有两个性能瓶颈是很容易想到的,因为这里最大的问题就是数据量非常大,单文件2500w行。

  1. Http请求数据,IO性能
  2. parse日志,提取spanId
  3. 建立日志的索引

IO性能这个基本无解,没什么好的优化方法,或者换C++是个好思路(手动狗头)

parse日志这个也基本无解,这里的parse其实还算简单的

建立日志的索引倒是考究很多。

还有个就是不能太迷信多线程,这里简单提一个例子,就是parse日志的过程中,我曾经改成了多线程的方法,但是发现CPU消耗上去了,但是性能几乎没啥变化,本地测甚至还有点下降。

吐槽

官方的题目中,明明写着是面向数据流的,然后还允许通过Range方式拉取,这不打脸么。