当前位置: 首页 > 科技观察

日均TB级数据,携程支付统一日志框架

时间:2023-03-17 12:35:04 科技观察

一、背景支付中心作为携程集团的公共部门,主要负责交易、实名卡绑定、开户、收款等。对涉及的交易相关资金流以及用户实名认证,部分用户操作的中间数据需要长期保存,以应对内控/审计要求。目前研发应用多,日志量大,格式不一,对日志的存储和使用提出了很大的挑战。因此,支付数据和研发团队共同开发了统一的日志框架。2.总体架构图核心模块包括:日志生产、日志采集、日志解析。调用流程如下:研发应用/服务接入基于log4j2扩展的统一日志组件,并将日志发送至Kafka。周期性启动消费kafkatopic的camusjob将日志写入hdfs。T+1启动MR作业读取camus写入的hdfs内容加载到hive表中。3、日志生产——研发统一日志组件支付基于log4j2,定制多个Appender,将应用日志以服务调用的形式发送给Kafka,由log_process_service服务统一处理,提交给携程公共基础日志框架如:CLOG、CAT、ES,各个应用不需要关心公司的日志框架,由日志平台处理。其优点:不同的日志框架对应不同的Appenders,方便日志框架接入的扩展。采用AOP编程,对接入业务侵入性小,接入简单。定义了丰富的java注解,方便日志配置输出。可打印日志包括但不限于:类名、方法名、方法入参、返回值、异常等,支持敏感字段脱敏。存在问题:日志格式不规范:研发应用数百个,研发人员众多,日志格式千差万别,给数据分析和使用带来很大挑战。存储时间短:目前公司在线CLOG存储系统只能查询最近几天的数据,ES可以存储更长时间的数据,不支持批量查询。由于数据量巨大,基本的离线CLOGhive表只能做到T+2,无法满足T+1的报表需求。因此,支付数据团队在研发团队统一日志组件的基础上,结合数据分析和数据存储生命周期,开发了统一的日志框架。3.1统一日志购买点设计支付研发团队负责数百个服务或应用。支持的业务包括:路由、鉴权、免密、卡务、订单、钱包实名、电子支付等。不同的业务可以拆分App、h5、线上、线下等项目,整合这些数据非常棒挑战。如果任意指定各个系统的研发点,会给BI数据分析带来很大困难,数据分析的准确性也难以保证。因此,支付数据根据业务特点定义了一套统一的日志嵌入点规范。字段定义主要基于日常分析需求,致力于简化数据的使用。所以一般原理都是json的形式。目前的原始日志由tag/message两部分组成,其中tag的数据结构为Map,tag中一般通过关键数据来获取数据,通过message获取详细数据。标签和消息的格式为:[[$tag]]$message。目前,有两种类型的标准字段:规范字段和通用字段。3.1.1规范字段的格式规范字段要求应用研发有一定的参与度,并提供符合字段命名的类和方法。部分字段名及定义如下:字段名字段类型描述serviceNamestring调用服务名tagMapkeyvalue信息messagestring原始日志requeststring接口请求参数responsestring接口返回值requesttimestring日志请求时间responsetimestring日志请求时间其中tag可以灵活填写,主要扩展字段如下:名称字段类型描述versionstringapp版本号platstring平台信息refnostring序列号3.1.2公共字段格式日志框架自动获取属性,无需开发代码即可打印。字段名称字段类型描述applicationidstring携程应用唯一标识号logtimestring日志生成时间3.2partition/bucket字段的定义目前离线数据分析都是基于hive引擎,hive的分区和bucket设计对查询性能影响很大,尤其是logvolume在庞大的场景下,分区字段的选择尤为关键。比如用户进入支付收银机的场景可能有上百种,每种场景都会有多个服务调用。服务调用的频率在不同的场景下差别很大,占用的空间也很大。因此,为每个场景分配一个唯一的场景号,可以通过场景号进行分区,可以高效的分析数据,而且分区过多也可能导致小文件较多,对hadoop影响较大名称节点。这时候结合buckets可以优化查询效率,避免分区无限制野蛮增长导致的小文件过多的问题。4.日志采集日志采集框架基于LinkedIn开源项目Camus。Camus使用MapReduce读取Kafka数据,然后将其写入hdfs。由于没有reduce阶段,所有的数据处理和写入都在Map端,很少出现数据倾斜。Camus接入简单,扩展方便,所以针对目前的业务需求,我们进行了二次开发:自定义decoder/partitioner,nativedecoder/partitioner支持的hdfs落地路径主要是基于日期,比较粗糙,无法适应业务需求。因此,自定义解码器将原始日志分区字段提取出来,代入分区器生成具有业务意义的hdfs输出路径,为特定时间范围内的数据刷新提供了高效的解决方案。自定义provider,原生StringRecordWriterProver只支持文本文件的登陆,占用空间大,压缩后无法并行拆分,容易错列错行,而orc格式数据有效节省了占用空间hdfs,查询效率高,可以切分,有利于日志解析作业的高效执行。其中,在配置Camusjob的过程中需要注意以下问题:4.1Camus任务执行执行频率设置Error:java.io.IOException:targetexists.thefilesize(614490vs616553)isnotthesame。由于kafka消息存储天数有限,单个分区大小有限(服务器配置:log.retention.bytes),携程端3天10G。如果camus与kafka同步的频率较低,可能会出现数据丢失的情况。所以需要根据日志量来设置camus调度任务的执行频率,防止数据丢失。任务重叠执行Error:java.io.IOException:targetexists.thefilesize(614490vs616553)isnotthesame.camus从kafka读取数据,任务必须作为单例执行,任务执行完成后会更新kafka的offset,如果一个任务执行多次,数据大小不会对齐。此时需要将配置路径下的数据全部删除,重启任务才能完成修复。4.2如何控制camus登陆文件的大小当Kafka各分区写入数据量不均衡时,每个分区会写入一个hdfs文件,如果研发日志写到Kafka的某个分区,则hdfs会导致该分区对应的文件占用大量空间。如果恰好文件不可分割,在极端情况下,只有一个线程去解析大文件,降低了数据读写的并发度,延长了数据解析时间。遇到这种问题最好的解决办法是:临时解决方案:研发日志分布式写入kafka分区,不要导致某一类数据写入一个分区;高效解决方案:数据端采用可拆分的输入格式进行数据拆分;4.3写Orc文件格式注意事项OrcwritetimeoutAttemptID:attempt_1587545556983_2611216_m_000001_0Timedoutafter600secorc文件的写入速度比文本文件慢很多。如果同时写入的文件很多,或者内存回收时间长,map方法在600秒内不会被读取。写入或状态更新,作业将被终止。解决方案是将默认任务超时从10分钟增加到20分钟。mapreduce.task.timeout=1200000OOM内存溢出超出physicalmemorylimits.Currentusage:2.5GBof2.5GBphysicalmemoryused;4.2GBof5.3GBvirtualmemoryused.Killingcontainer.如果在orc中写文件的时候OOM比较多,需要增加map执行的内存。mapreduce.map.memory.mb=8096mapreduce.map.java.opts=-Xmx6000m5.统一的日志分析鉴于日志分析工作主要集中在MapReduce的Map端,Map端可以很容易的通过调整参数控制map的个数,提高数据解析的并发度,MapReduce主要分为几个核心阶段:inputformat、map、shuffle、reduce等,通过优化每个阶段的执行时间,提高日志解析的速度可以显着改善。5.1Inputsplit优化影响MR作业的map数量主要包括:文件数量:如果不使用CombineFileInputFormat,那么小文件不会被合并,每个文件至少要经过一个map处理。当小文件过多时,频繁启动和回收线程对性能也有影响,同时对集群中其他作业资源的分配也有影响。文件属性:当文件较大且可以拆分时,系统会生成多张图来处理大文件。inputsplit块按照MR的最小单位对文件进行拆分,一个split对应一个MapTask。前期的日志解析程序性能比较高。一天的全量日志解析大概需要25分钟,中间任务执行时间从25分钟延迟到4小时。分区中,日志量巨大,导致少量map在读取大文件时执行时间特别长。经过分析,发现text+snappy文件不能拆分,只能用map来处理。camus数据格式由text+snappy改为orc+snappy格式,开发了支持orc文件格式的CombineFileInputFormat,减少了对小文件的需求。Hadoop对计算资源的果断占用也提高了作业的并发性。5.2Shuffle优化使得map的输出更均匀的映射到reduce端。由于默认的分区策略是对map的输出keyhash取reduce个数取模,容易造成数据倾斜。解决办法是加个时间戳或者重写分区函数。5.3批处理日志分析MR的当前输出将作为hive表的数据源,hive表会根据业务流程进行分区。所有数据分析结果路径为:日期+业务流程,可能有上百个业务流程,使用MultipleInputs/MultipleOutputs可以实现一个mapreduce作业中多输入多输出的功能,适应业务自定义分析,归一化后扔到reduce端。5.3.1空文件的产生过程中会产生大量的临时小文件和大小为0的小文件,增加hdfsnamenode的内存压力。同时空文件也会导致spark表查询失败,可以通过LazyOutputFormat修复。5.3.2重复文件创建MultipleOutputs输出文件一般命名为name-r-nnnnn,其中name与程序指定的文件名有关,nnnnn代表reduce任务号。在处理大量数据时,reduce端可能会重复创建已有文件,导致任务运行很长时间没有成功。中间产生了大量的小文件,给Hadoopnamenode造成了很大的压力,影响了整个集群的响应时间。解决方法是:在reduce端写入数据时,需要捕获异常。一旦数据写入异常,相应的写入reduce文件将被删除,程序终止。由于MR支持高可用,当一个reduce任务失败时,它会自动重试。如果一定次数的重试仍然失败,则整个任务失败。每次重试都避免了重复创建现有文件,从而导致NN响应时间迅速下降。5.4reduce个数的调整目前日志解析的reduce端主要用于写入orc数据。reduce次数少的时候,会造成reduce内存溢出,reduce次数多的时候,可能会造成很多小文件,占用集群资源太多,可以计算一下输入map端文件和总占用空间,动态计算需要reduce多少,达到合理利用资源的目的。6.日志治理日志落地带来的一个问题是存储空间的快速增长。目前,支付中心每天新增TB级ORC压缩原始数据,并持续增长。支付数据侧根据研发和产品需求对不同类型的日志进行分类,并对不同类型的日志设置不同的存储期限,主要分为:研发故障日志、审计日志、数据分析日志等;同时,日志写在camus导入hdfs时,由于按照业务分区登陆,产生了大量的小文件。需要合并这些小文件并设置TTL,以免对hadoopnamenode造成较大影响。七、总结与展望目前,TB级数据的日平均分析时间在30分钟以内完成。未来计划将日志系统导入到clickhouse等对实时性要求高的场景进行联动和使用,以支持精细化的业务运营和分析。【作者简介】应铭,携程数据研发专家,负责支付线下数仓建设和BI业务需求,对并行计算、大数据处理和建模有浓厚兴趣。