网易七鱼 Android 高性能日志写入方案
本文来自网易云社区
作者:网易七鱼 Android 开发团队
前言
网易七鱼作为一款企业级智能客服系统,对于系统稳定性要求很高,不过难保用户在使用中不会出现问题,而 Android SDK 安装在用户的手机上,同时由于 Android 碎片化的问题,对于 Android SDK 的问题排查就显得尤为困难,因此记录下用户的操作日志就显得极为重要。
声明:网易七鱼仅记录操作日志,用于还原问题,不会记录用户的隐私信息。
初始方案
一开始,网易七鱼记录日志的方式是直接通过写文件,当有一条日志要写入的时候,首先,打开文件,然后写入日志,最后关闭文件。这样做的问题就在于频繁的IO操作,影响程序的性能,而且七鱼为了保证消息的及时性,还维护了一个后台进程,当其中一个进程进行日志写入时,另一个就会被锁在门外等着,问题就愈发严重。使用这种方案虽然当前看上去对程序的影响不大,但是随着日志量的增加,更多的IO操作,一定会造成性能瓶颈。
下面我们来分析下直接写入文件的流程:
用户发起 write 操作
操作系统查找页缓存 a.若未命中,则产生缺页异常,然后创建页缓存,将用户传入的内容写入页缓存 b.若命中,则直接将用户传入的内容写入页缓存
用户 write 调用完成
页被修改后成为脏页,操作系统有两种机制将脏页写回磁盘 a.用户手动调用 fsync() b.由 pdflush 进程定时将脏页写回磁盘
可以看出,数据从程序写入到磁盘的过程中,其实牵涉到两次数据拷贝:一次是用户空间内存拷贝到内核空间的缓存,一次是回写时内核空间的缓存到硬盘的拷贝。当发生回写时也涉及到了内核空间和用户空间频繁切换。
而且相对于机械硬盘,SSD 存储还有一个“写入放大”的问题。这个问题主要和 SSD 存储的物理结构有关。当 SSD 被全部写过一遍之后,再写入的数据是不可以直接更新,只可以通过覆盖重写,在覆盖之前需要先擦除数据。但写入的最小单位是 Page,擦除的最小单位是 Block,而 Block 远大于 Page,所以在写入新数据时就需要先把 Block 上的数据读出来和要写入的数据合并在一起,再把 Block 擦除,最后把读出来的数据重新写入到存储上,这样导致实际写入的数据可能远远大于最开始需要写入的数据。
没想到简单的写文件竟然涉及了这么多操作,只是对于应用层透明而已。
既然每写一次文件会执行这么多次操作,那么我们能不能将日志缓存起来,当达到一定的数量后再一次性的写入磁盘中呢?
这样确实能够大量减少 IO 次数,但是却会引发另一个更严重的问题——丢日志
把日志缓存在内存中,当程序发生 Crash 或进程被杀后就无法保证日志的完整性,而且由于七鱼存在多进程,也无法保证多进程下日志的顺序。
一个完善的日志方案,需要满足
高效,不能影响系统性能,不能因为引入了日志模块而造成应用卡顿
保证日志的完整性,如果不能保证日志完整,那么日志收集就没有意义了
对于多进程应用,要保证最终看到的日志顺序的准确性
高性能方案
既然无法减少写入次数,那么我们能不能在写文件的过程中去优化呢?
答案是可以的,使用 mmap
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系,函数原型如下
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。
同时 mmap 能够保证日志的完整性,mmap 的回写时机:
内存不足
进程退出
调用 msync 或者 munmap
不设置 MAP_NOSYNC 情况下 30s-60s(仅限FreeBSD)
当映射一个文件后,程序就会在 native 内存中申请一块相同大小的空间,因此建议每次映射一小段内容,如 64k,写满后再重新映射文件后面的内容。
日志写入性能和完整性的问题解决了,那么如何保证多进程下日志的顺序呢?
由于 mmap 是采用共享内存的方式写入数据,如果两个进程同时映射一个文件,那么一定会造成日志覆盖的问题。
既然不能直接保证顺序,那我们只能退而求其次,两个进程分别映射不同的文件,每天合并一次,合并时对日志进行排序。
继续优化
根据上述方案,设计 jni 接口,打包 so,引入 SDK,看似没什么问题了,但是作为一款 SDK,总觉得包含 so 不太友好,在一定程度上会增加接入的难度。
那么能不能不用 so 呢?
其实 Java 中已经提供了内存映射的实现——MappedByteBuffer
MappedByteBuffer 位于 Java NIO 包下,用于将文件内容映射到缓冲区,使用的即是 mmap 技术。通过 FileChannel 的 map 方法可以创建缓冲区
MappedByteBuffer raf = new RandomAccessFile(file, "rw");MappedByteBuffer buffer = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, position, size);
为了测试 MappedByteBuffer 的效率,我们把 64byte 的数据分别写入内存、MappedByteBuffer 和磁盘文件 50 万次,并统计耗时
方法 | 耗时 |
---|---|
内存 | 384ms |
MappedByteBuffer | 700ms |
磁盘文件 | 16805ms |
可以看出 MappedByteBuffer 虽然不及写入内存的性能,但是相比较写入磁盘文件,已经有了质的提升。
总结
本文主要分析了直接写文件记录日志方式存在的问题,并引申出高性能文件写入方案 mmap,兼顾了写入性能和完整性,并通过补偿方案确保多进程下日志的顺序。最后发现了内存映射在 Java 层的实现,避免了引入 so。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区。
相关文章:
【推荐】 常用数据清洗方法大盘点
【推荐】 浅谈由管理者角色引出的B端产品设计思考点
【推荐】 Android TV 开发 (1)
转载于:https://www.cnblogs.com/zyfd/p/9728139.html
网易七鱼 Android 高性能日志写入方案相关推荐
- java调用七鱼返回文件_网易七鱼 Android 高性能日志写入方案
本文来自网易云社区 作者:网易七鱼 Android 开发团队 前言 网易七鱼作为一款企业级智能客服系统,对于系统稳定性要求很高,不过难保用户在使用中不会出现问题,而 Android SDK 安装在用户 ...
- Android应用客服系统解决方案---网易七鱼客服
应用客服系统 现在好多应用都有客服系统需求,常见问题可以通过智能客服解决,疑难问题可以联系人工客服实时解答.比较常见的购物应用的客服功能. 七鱼客服 七鱼 Android SDK Github 网易七 ...
- android网易七鱼客服系统
一.优点 满足Web.App.微信公众号等全渠道的在线客户服务,支持客户信息展示,丰富沟通方式,超过100 项数据报表等功能 .一句话就是功能强大,聚合多个平台. 二.接入步骤: 1.在网易七鱼(ht ...
- Android高性能日志模块-Xlog 正篇
Android高性能日志模块-Xlog 前言 日志可以帮助我们定位问题,记录当前程序的运行状态.与后端开发不同的是,Android中的Log原生支持的仅是本地调试和信息记录,并不能很方便地定位远程问题 ...
- 前端dashboard框架_微前端在网易七鱼的实践
一.前言 网易七鱼是提供围绕客户服务与智能营销的 SaaS 平台.在七鱼业务中,有在线系统.呼叫系统.机器人.工单系统.数据大屏等业务线,它们分布在两个业务端,管理端和客服端.这两个端的功能框架类似, ...
- 微前端在网易七鱼的实践
一.前言 网易七鱼是提供围绕客户服务与智能营销的 SaaS 平台.在七鱼业务中,有在线系统.呼叫系统.机器人.工单系统.数据大屏等业务线,它们分布在两个业务端,管理端和客服端.这两个端的功能框架类似, ...
- 网易服务器修改权限等级,网易七鱼客服管理_七鱼客服管理人员权限设置_企业服务汇...
编者按:网易七鱼的客服权限设置功能能够方便企业针对不同角色的客服人员分配不同的系统操作权限,客服权限的分配主要包括了对系统设置权限.数据查看范围及导出权限.功能使用权限这三大类权限的分配,其中系统设置 ...
- 网易七鱼携手全球时刻,用AI打造社交新零售服务闭环
成立于2016年4月的全球时刻由杭州介百文化创意有限公司推出,定位于精品社群电商APP,以"分享改变生活"为品牌理念,为用户苛选全球好物,传递美好的生活方式.全球时刻以社交分享为主 ...
- 网易AI平台开放多项技术,助力网易七鱼智能客服升级
杭州2018年9月11日电 /美通社/ -- 人工智能如何影响生活一直是个热议话题.近日,网易七鱼在其新媒体渠道上构想了一座"七鱼智慧生活街区",从与盼达用车.网易云音乐.来伊份. ...
- 网易七鱼客服 发起客服-触发两条会话
今天集成网易七鱼客服的时候,每次调用的时候,客服端都会收到两条会话信息 这其实是同一个人来的,本来挺简单的东西,硬是搞了一天. 出现这个原因,主要是因为我们在分配客服的时候,需要为客户指定同一个客服, ...
最新文章
- ftp上传当天文件的方法_五种方法将文件上传到FTP服务器
- CI框架 守护进程nohup让PHP以常驻内存的形式执行订阅消息
- 程序员面试100题之十六:二叉树中两个节点的最近公共父节点
- wasm-simd入门(卷积指令)
- C++:05---class和struct
- 程序员面试金典 - 面试题 17.18. 最短超串(双指针+哈希)
- b站的服务器在哪个位置,b站用的是哪个云服务器
- 《驯狮记——Mac OS X 10.8 Mountain Lion使用手册》——2.3 Dock
- 95-140-140-源码-transform-双流操作的实现
- Spring学习总结(11)——Spring JMS MessageConverter介绍
- pycharm检测不到python编译器_1.2 搭建python+pycharm编程开发环境
- 【网络优化|渣速必看】合理设置MTU,提升网络速度
- HWP转Word说明
- android 应用变量,真正免root的应用变量详细使用教程
- 三数之和java实现ArrayList-leetcode算法编程-探索字节跳动面试
- 笔记本手机都能用的充电器,做的只有乒乓球大小,AOHi 65W氮化镓充电器体验
- can总线不加末端电阻_RS485总线专题讲解,从原理入手!
- Q1营收增长66%,Snap继续靠“广告业务”发家能长期利好么?
- Linux-fsck.ext4
- 《软件工程与计算(卷二)》-Chapter9-10-软件体系结构
热门文章
- 2015 CCPC 这次,我为自己鼓掌
- ispalpha函数与islower
- linux gdb 执行命令,Linux中gdb命令起什么作用呢?
- opengl 图片处理 卷纸弯曲效果_神笔马良——基于 OpenGL 的涂鸦框架
- java接口抽象方法_Java接口、接口和抽象类:
- php对接海康视频教程_手把手教你php对接海康api
- 单片机控制24v电压_整车控制器-硬件篇
- iphone7home键按压失灵_iPhone home键失灵怎么办 iPad home键失灵解决办法【详解】
- 不能忽视的情绪 -- 喜怒哀惧,也不过度关注
- 价值连城 ImageNet图像分类大神 Andrej Karpathy的采访 给AI 深度学习从业者的建议