Structured Logging 须要更好的基础设施支持

English Version: https://taowen.gitbooks.io/tsdb/content/indexing/indexing.htmlhtml

现状

所谓 structured logging 就是往日志文件里打json格式的日志,而后按照固定字段去检索的需求。这种需求目前通常是用这样的一个基础设施来知足的:git

业务进程 => 日志文件 => 同机的日志搜集agent => 日志解析 => kafka => 日志索引程序 => elasticsearch

对于日志的存储和检索需求来讲,用上面设置的 elasticsearch 能够说已经解决得很好了(惟一查询DSL比较古怪的问题,用SQL也能够解决 https://github.com/taowen/es-monitor)。github

存在的主要问题就是如今的设置是用“非结构化日志”来支持“结构化日志”的需求。由于整个基础设施假定输入的数据都是无格式,而且面向行处理的,因此整个导入的过程没法为结构化日志优化,没有办法得到原本能够达到的性能。问题表现为三个主要的点上:数据库

  • 应用程序必须在可靠性和高性能之间选择日志方案json

  • 数据在多个集群之间倒腾,致使占用大量机房内网带宽以及提升了端到端的延迟后端

  • 低效的数据封包格式,以及按行处理服务器

可靠性 v.s. 高性能

当业务进程须要提供一份流式的事件作为分析用途的时候,就每每须要在高可靠性和高性能之间选择。通常来讲有这样两种选择:app

  • 业务进程 => kafka (直接写kafka)socket

  • 业务进程 => 日志文件 => 采集agent => kafkaelasticsearch

选择第一种方式会很是可靠。首先少了中间环境,其次写kafka是同步过程,甚至可让kafka作到所有replica都ack才返回success。可是第一种方式的主要问题是一旦kafka出问题了就可能会影响到业务进程自己。若是业务进程决定不一样步写kafka,那又有丢数据的风险。

第二种方式所以变得有吸引力,一方面日志文件写入不影响业务进程自身的速度,同时持久化的文件必定程度上能够保证数据不丢失。可是目前尚未特别靠谱的方案来保证这条路径和第一种方式同样可靠。

使用方须要在这两种模式之间进行选择说明如今的基础设施还不够完善。理想的kafka写入方式应该兼具可靠性和高性能:

  1. 首先业务进程经过本地socket报告事件给本机的agent

  2. 本机的agent经过memory mapped file 落磁盘保证事件不丢

  3. agent批量把聚集的事件可靠写入到 kafka 集群里

也就是在业务进程和远程的kafka集群之间添加一个本地的可靠队列。基本上rsyslog的设计是知足这样的条件的,可是实际使用中发现rsyslog仍然不够完美,能够为structured logging定制一个更好的agent:

  • 更好的性能

  • 对结构的原生支持,并能够在入口处校验格式

  • 批量聚集的事件能够把小的event打成大的封包格式,甚至能够把行转成列压缩,并利于后端按列处理

add a local https://github.com/OpenHFT/Chronicle-Queue before kafka would be awsome

多集群之间倒腾数据

分布式系统最多见的口号是你不用关心数据在哪里,咱们帮你把数据自动分布到多台机器上去。问题是现实状况是一个完整的流程须要多个分布式系统来彼此配合,可是它们都各自为政,本身都是一个独立的王国。最差的状况是这样

clusterA 1                      clusterB 1
clusterA 接口机 => clusterA 2 => clusterB 接口机 => clusterB 2
                   clusterA 3                      clusterB 3

每一个集群都提供一个数据的入口,而后从这个入口转发一层到本身后端。从一条数据的角度来讲,他须要不断地在机器之间倒手,序列化反序列,路由再路由,才能到达最终的目的地。对于一个常见的日志集群来讲,数据就是这样地不断地被倒腾。对于非结构化日志来讲,这样的倒腾是必要的,由于日志要通过不断的再处理,可是对于结构化日志的场景来讲,就是浪费:

业务进程集群 => 日志解析集群 (或者kafka入库程序集群) => kafka集群 => ES入库程序集群 => ES集群

理想的设置是

业务进程集群 => 本地agent => kafka/es 集群

数据经过本地agent直接录入到 kafka/es混合的集群里。这种混合没法经过简单地同机部署kafka和es来达到,由于这两个分布式系统都是假定本身掌控数据的分布的。只有修改kafka和es让两个系统深度融合才能实现,让kafka作为es的write ahead logging(WAL,在es中叫translog)才能达到最佳的效果。这个kafka/es混合集群就是一个混合的事件存储

  • kafka提供事件的顺序消费支持

  • es提供事件的检索支持

  • es利用kafka作为本身的WAL,实现索引的可靠创建

低效地封包格式和按行处理

由于整个基础设施是为非结构化日志准备的,因此一些习惯地作法也是为了方便非结构化日志而存在的,好比:

  • 每一个事件封装成一个json

  • 事件之间用\n换行符隔开

这种封包格式相比正则解析日志是巨大的进步了,可是仍然是很是低效的。对于结构化日志咱们有更好的选择

  • 事件能够用带schema信息的格式,避免反复出现key/value里的key。好比pb,avro这样的格式

  • 事件能够被打包成一个大的包,包内能够按列表示数据,进行高效压缩

  • 事件之间能够用头信息指定本身的包的尺寸来隔开,比扫描\n更快

除了包格式的选择上,处理方式上也是从头至尾都是按行处理的模式。最佳的处理方式应该是这样的

按行产生事件 => n行批量组包成一个块 => 块内按列进行处理和存储

在记录处理的offset上时,每一个offset对应一个块而不是具体的一行事件。多个行打包成一个高效的按列组织的块方便批量处理(特别是CPU有针对性的向量化优化)。

在Elasticsearch/Lucene内部也缺少高效地结构化数据入库(最终是写入lucene的doc-values文件)的渠道。从源头就假定输入是按行组织的JSON文档。并且内部是以List<Number>而不是long[]来表示数据。原始的数据若是产生就是有格式的,彻底能够避免一堆中间对象产生。直接产生就是long[]的形式,而后简单传输到Elasticsearch服务器就以long[]进行文件写入。

总结

Elasticsearch是一个很好的存储事件的数据库,很是好地解决了存储和检索两大难题。可是对于结构化的数据入库支持并非很好,理想地状况应该是这样的:

  • 提供一个本地agent,实现数据可靠地高性能入kafka

  • kafka/elasticsearch深度融合,避免集群拷贝产生的额外负担,kafka直接做为elasticsearch的WAL存在

  • 数据以高效的格式封包,而且可以从源头开始就按列处理,从头至尾都是long[]而不是List<Map<String, Object>>

相关文章
相关标签/搜索