巅峰战队|PowerJob日志饱受好评的秘诀:小但实用的分布式日志系统( 二 )


这个抉择其实并不难 , 用一下简单的排除法就能获取正确答案:

  1. 存内部还是存外部?PowerJob 作为任务调度中间件 , 最小依赖一直是需要牢牢把控的指导思想 。 因此 , 在已知最小依赖仅为数据库的情况下 , 似乎不太可能使用外部的存储介质 , 至少不能把收到的日志直接发送到外部存储介质 , 否则又是一波庞大的 QPS , 会对依赖的外部组件有非常高的性能要求 , 不符合框架设计原则 。 因此 , 在线日志的第一级存储介质应该由 server 本身来承担 。
  2. 存内存还是磁盘?既然确定了由 server 来存储原始数据 , 那么就面临内存和磁盘二选一的问题了 。 但 , 这还用选吗?成百上千万的文本数据存内存 , 这不妥妥的 OutOfMemory 吗?显然 , 存磁盘 。
经过一波简单的排除法 , 日志的一级存储方案确定了:server 的本机磁盘 。 那么 , 存磁盘会带来什么问题呢?
且不说文件操作的复杂性和难度 , 一个最简单的需求就能让这个方案跌入万丈深渊 , 那就是:排序 。
众所周知 , 日志必须按时间排序 , 否则根本没法看 。 而 PowerJob 又是一个纯粹的分布式系统 , 显然不可能指望所有的日志数据按顺序发到 server , 因此对日志的再排序是一件必须要做的事情 。 但让我们来考虑一下难度 。
  • 首先 , 日志是纯文本数据 , 要想做排序 , 首先要将整个日志文件变为一堆日志记录 , 即分行 。
  • 其次 , 分完行后 , 由于日志是给人看的 , 时间肯定已经被转化为 yyyy-MM-dd HH:mm:ss.SSS 这种方便人阅读的格式 , 那么将它反解析回可排序的时间戳又是一件麻烦事 。
  • 最后 , 也是最终 BOSS , 就是排序了 。 要知道 , 之所以会选择磁盘存储这个方案 , 是因为没有足够的内存 。 这也就意味着 , 这个排序没办法在内存完成 。 外部排序的难度和效率 , 想必不用我多说了吧 。 同时 , 我也相信 , 大部分程序员(包括我在内)应该从来没有接触过外部排序 , 这趟浑水 , 我又何必去趟呢?

巅峰战队|PowerJob日志饱受好评的秘诀:小但实用的分布式日志系统3.2 H2 数据库简介那么 , 有没有什么技能使用磁盘做存储 , 又有排序能力的框架/软件呢?世上会有这等好事吗?你别说 , 还真有 。 而且是远在天边 , 近在眼前 , 可以说是和程序员形影不离的一样东西——数据库 。
“等等 , 你刚才不是说 , 不拿数据库作为一级存储介质吗?怎么滴 , 出尔反尔?”
“哼 , 年轻人 。 此数据库非彼数据库 , 这个数据库啊 , 是 powerjob-server 内置的嵌入式数据库 H2”
【巅峰战队|PowerJob日志饱受好评的秘诀:小但实用的分布式日志系统】H2 是一个用 Java 开发的嵌入式数据库 , 它本身只是一个类库 , 即只有一个 jar 文件 , 可以直接嵌入到应用项目中 。 嵌入式模式下 , 应用在 JVM 中启动 H2 数据库并通过 JDBC 连接 。 该模式同时支持数据持久化和内存两种方式 。
H2 的使用很简单 , 在项目中引入依赖后 , 便会自动随 JVM 启动 , 应用可以通过 JDBC URL 进行连接 , 并在 JDBC URL 中指定所使用的模式 , 比如对于 powerjob-server 来说 , 需要使用嵌入式磁盘持久化模式 , 因此使用以下 JDBC URL 进行连接:
jdbc:h2:file:~/powerjob-server/powerjob_server_db同时 , H2 支持相当标准的 SQL 规范 , 也和 Spring Data Jpa、MyBatis 等 ORM 框架完美兼容 , 因此使用非常方便 。 在 powerjob-server 中 , 我便通过 Spring Data Jpa 来使用 H2 , 用户体验非常友好(当然 , 多数据源的配置很不友好!) 。
综上 , 有了内置的 H2 数据库 , 日志的存储和排序也就不再是难以解决的问题了~


推荐阅读