第一部分: 什么是日志

什么是日志

image-20200428143046866

  • 新记录被添加到尾部
  • 读取顺序从左至右
  • 每个记录都会被赋予一个唯一的顺序的编号
    • 日志记录的顺序定义了一个“时间”的概念
    • 日志的编号可以理解为一个时间戳(相对时间)
  • 日志和文件或者(数据)表没有区别
  • 日志的目的是:用于记录发生了什么和什么时候发生的 ==> 分布式系统所关心的核心问题
  • 日志的类型:
    • 应用日志:非结构化的错误信息或者应用跟踪信息
    • 数据日志:为程序访问设计 这里讨论的类型

数据库中的日志

作为一种ACID模型的实现细节,日志的作用:

  • 保证数据结构的同步
  • 崩溃恢复时的数据索引
  • 使用日志记录将要被修改的记录的信息
  • 日志记录在数据上发生了什么

作为数据库之间的同步的方法 (MySQL主从同步通过日志实现)

分布式系统中的日志

分布式系统的设计核心问题: 如何根据事件发生的顺序在多台机器上更新对应的数据并保持同步

也就是: 使得多台机器都对接收的相同的分布式日志进行相同的操作

日志解决了两个问题:

  • 有序的变化 (事件顺序)
  • 分布式的数据

状态机复制原理

如果两个确定性的进程以相同的状态开始并且以相同的顺序获得相同的输入,则产生相同的输出并且结束在相同的状态(状态机的性质)

  • 确定性意味着处理过程过程与时间无关
  • 状态指进程留在机器上的任何数据
  • 确定性的处理过程就是确定性的(deterministic processing is deterministic)

日志的目的是排除了输入流中的全部的不确定性,确保处理相同数据的每个节点状态都保持同步

这种方法的好处在于索引日志的时间戳可以作为每个节点状态的唯一ID来表达每个节点的状态

分布式系统中两种复制和处理数据的方法:

  • 主-主模型:每个节点都到日志读取操作复制到本地进行操作和状态的保持,例如每个节点读取 ”+1“,”*2“操作到本地进行计算然后保存结果
  • 主-备模型:选举一个主节点进行数据的操作,把操作的结果记录到日志里供其他节点读取,例如主节点进行”+1“,”*2“的操作,把操作的结果”1“,”3“,”6“记录到日志给其他节点读取

主-主模型

主-备模型

这也揭示了为什么记录的顺序是数据一致性的关键:操作的顺序不同就会导致完全不一样的结果(运算顺序不同结果也会不同)

一些分布式一致性协议

  • Paxos
  • ZAB
  • RAFT
  • Viewstamped Replication

日志和表的二象性

把日志类比成银行账户的交易记录的话

  • 表: 当前账户的状态
  • 日志: 表的所有状态的备份

所以可以看到日志所能表达的信息量是比表多很多的,但是相对于的存储的成本也高很多

第二部分: 数据集成

数据集成指:一个组织的所有数据对这个组织所有的服务和系统可用

马斯洛的层次需求理论

  • 收集所有相关的数据
  • 放到适当的处理环境中
  • 建议可靠完整的数据流
  • 有了数据和处理过程:
    • 关注更好的数据建模
    • 更好的可视化,报表,处理算法,数据预测

两个难题

  • 事件数据流水线
    • 记录发生了什么而不是产生的结果
    • 数据量比传统数据量级大很多,处理更加困难
  • 专用数据系统的爆发: 集成更加困难

日志结构的数据流

  • 提取所有组织的数据并放入用于实时订阅的日志中心
  • 每个数据源都可以建模成日志
  • 日志的概念为每次更改提供了逻辑时钟
  • 日志可以充当缓冲区,是数据的产生和消耗可以异步进行
  • 目标系统只需要知道日志而不需要知道数据源系统的任何信息

image-20200428160306069

在LinkedIn

大量的数据专用系统:

  • Search
  • Social Graph
  • Voldemort (key-value store)
  • Espresso (document store)
  • OLAP query engine
  • …………

原先的数据管道架构:

image-20200428163507582

直接使用管道对接数据源的问题:

任何时候任何管道都可能出问题,在坏数据上进行操作只会产生更多坏数据

在实践中意识到的几件事:

  1. 虽然很混乱,但是建立的管道是有价值的
  2. 需要对数据管道更深入的支持才能对数据进行可靠的加载
  3. 数据覆盖率仍然很低

需要的架构: 一个数据源只连接一个单一的管道 ==> kafka

image-20200428163527737

ETL和数据仓库的关系

数据仓库

  • 一个干净,集成的数据仓库,其结构支持分析
  • 定期从数据源提取数据
  • 将其拼凑成可以理解的形式
  • 加载到一个中心数据仓库
  • 不能对数据进行实时处理

ETL

  • 提取和清理数据的过程
  • 将数据重组成用于查询的结构

数据仓库团队的经典问题

  • 通常不了解数据仓库中的数据如何使用
  • 创建难以提取或者很难获取的数据
  • 很难转换成可用的形式

一个更好的形式

image-20200428164628144

  • 一个中心管道
  • 定义良好的用于添加数据的API
  • 组织的可扩展性
    • 可以提供完整数据集的搜索功能
    • 亚秒级的数据流监控
  • 进行特定的清理或转换的位置
    • 由数据生产者完成后添加到中心日志(最好的方法, 可以保证中心日志系统数据的规范性)
    • 在日志中心进行实时转换(会产生转换日志)
    • 由加载数据的系统进行处理
  • 在添加数据时保留原有的数据格式,需要的特定操作在处理原始数据时进行
  • 只有针对目标系统的聚合操作才应该加到加载过程中

日志文件和事件

优势: 支持解耦的事件驱动的系统

构建可伸缩的日志

  • 日志分片
  • 通过批量读写优化吞吐量
  • 避免无用的数据拷贝

image-20200428165518804

  • 每个分片的日志是有序的,但是分片之间没有全局的次序
  • 由写入者决定消息发送到特定的日志分片
  • 每个分片通过可配置数字指定数据复制的复本个数,每个复本都有一个分片日志完全一致的一份拷贝
  • 对于顺序读写可以方便地优化

第三部分:日志与实时流处理

  • 日志是流处理的核心
  • 流处理是连续数据处理的基础架构
  • 计算模型是通用的
  • 具有低延迟产生结果的能力
  • 数据的收集方法是真正的驱动力:
    • 分批收集的数据自然进行分批处理
    • 连续收集数据时自然进行连续的处理
  • 连续处理可以平滑的处理资源并且降低延迟

流处理的另一个观点

  • 处理数据时引入了时间概念
  • 不需要数据的静态快照
  • 可以控制输出的频率
  • 不需要等待数据集收集结束再进行处理
  • 是批处理的一种泛化

其实只有很少的公司拥有实时数据流,因为缺少实时的数据收集

在金融领域实时数据流已经成为标准并且流处理已经成为了瓶颈

流处理覆盖了实时响应服务和离线批处理架构之间的差距

日志解决的最大问题是使得多订阅者可以获得实时的数据输入

数据流图

  • 扩展了早期对数据集成中数据订阅的想法
  • 根据订阅的数据进行计算得出新的订阅数据
  • 可以封装处理的复杂性
  • 可以把所有对组织中数据获取,转换和数据流处理操作视为一系列的日志以及对其写入过程进行处理的结合
  • 日志在其中的作用:
    • 使得每个数据集可以有多个订阅者并且保证有序
    • 提供了对数据处理的缓冲

image-20200428172047446

有状态的实时处理

需要在流处理的某个大小的时间窗口内进行更复杂的计数、聚合和关联操作

如何支持类似”表”的东西并将其与处理过程分开:

  • 把流转换为同一位置的表
  • 把状态保持在本地表或者索引中
  • 允许崩溃重启恢复
  • 处理过程的状态也保持为日志

日志合并

  • 两种数据清理的形式:

    • 事件数据,Kafka支持仅维护一个窗口的数据
    • 键值存储数据,使用压缩技术,通过重放技术来重建源系统的状态
  • 删除过时的记录但是保证日志包含了源系统的完整备份

  • 不再重现原系统曾经的所有状态,只保留最新的状态

image-20200428173223740

第四部分:系统构建

  • 分拆系统
    • 把整个组织系统和数据流视为单个分布式数据库
    • 面向系统的查询只是特定的索引
    • 流处理是一种完善的触发器和视图的实现机制
    • 将每个系统的许多小实例合并为几个大集群
  • 对于现有系统过多的复杂度,三个可能的进化方向

日志在分布式系统中的地位

日志能做什么:

  • 处理数据的一致性问题
  • 提供节点之间的数据复制
  • 提供”提交”语义(在保证写入不丢失时才确认写入)
  • 提供系统的外部数据订阅
  • 提供从失败副本恢复丢失数据或者引导新副本的能力
  • 处理节点之间的数据重平衡

这也是一个分布式系统所需要做的

如何工作

分为两个逻辑部分:

  • 日志: 按顺序捕获状态变化
  • 服务层
    • 服务节点存储查询所需索引
    • 服务节点订阅日志
    • 按照日志存储顺序尽快写入本地索引
    • 接收到请求时
      • 比较请求需要的时间戳和自己的索引位置
      • 延迟请求直到索引位置超过请求时间戳,保证数据正确性

image-20200428175431735

分布式系统中比较复杂的事

  • 处理故障节点
  • 在节点之间移动分区

处理方法:

  • 使日志保留一个数据窗口和分区数据存储的快照结合
  • 日志保留完整的数据副本并进行垃圾回收

image-20200428175515805

以日志为核心的分布式系统

  • 把查询相关的因素和系统的可用性与一致性解耦
  • 日志是一种特别有效的存储机制
  • 日志系统只执行线性的读取和写入
  • 系统的强度取决于日志的使用方式

资料

XMind

在看了【软件分享 | 硬核】真●全栈工程师的桌面是什么样的?之后决定尝试使用一下xmind来做一些知识和思路的整理,在使用了一段时间之后发现确实是不错,就是画流程图有点累人.前几个blog也是用xmind来整理知识的架构了.

学了点前端

在摸🐟期间尝试学习了一点前端,看了一些JavaScript的语法和历史,大概了解一下node js. 从一个Java程序员的视角来看也就是一个JVM吧,npm也就是maven.然后就开始尝试切入前端的组件架构,决定先学一学react.js

然后开始看React.js 小书学一点前端框架,虽然一开始也看了一下typescript但是还是决定先练练手再学学ts这样的强类型语言和整合.

如何阅读一本书

鉴于已经完全忘记了这本书的内容,所以决定重拾起来再看一遍,顺便用思维导图整理整理.

RAINDROP

需求的来源是现在同时使用chrome和Firefox导致标签的整理变得很困难,所以就尝试找一个页面整理和收集的工具,在一顿尝试之后目前觉得还不错.

Huginn

主要来源一个自己的想法,想把各个网站上收藏的东西都收集到一个地方.

在发现了rsshub之后想到可以把视频列表做成rss源,于是就找到了Huginn这个工具

现在的尝试是把YouTube的视频列表进行rss然后保存到raindrop中,鉴于raindrop提供的openAPI,所以就变得比较简单了.

另一个是TTRSS现在收藏的网页,通过发布RSS的形式也进行了一次RSS然后保存到raindrop中.

后面打算进行一个全集成,把全部的东西都RSS起来聚合到一起.现在还在探索有没有更好的方案.

The Log: What every software engineer should know about real-time data’s unifying abstraction

据说是史诗级程序员必读文章,于是就看了一下. 详细的整理应该还能再憋一篇blog出来吧

Bash 脚本教程

ruanyifeng推荐教程,看了一遍写的还是非常不错的,很详细.

现代 JavaScript 教程

一个很不错的js教程网站,主要是使用导航的形式可以分类查看

leancloud_counter_security插件生成的leancoud.memo问题

之前在配置了leancloud_counter_security插件之后就没有在意了,但是最近注意到没有收到ci的构建邮件了就去重新看了一下ci配置,果然还是问题重重.

Travis CI 整合 leancloud_counter_security

因为一开始做ci的整合的时候对ci脚本并没有很深的理解,所以也就是拿来就用,在熟悉了ci后的现在再来审视脚本就感觉有很多可以调整的地方.

首先是完全忘记了安装插件 … 因为之前集成的时候只是在本地测试了一下,并没有注意到ci脚本需要修改而遗留的问题

npm install hexo-leancloud-counter-security

leancloud.memo

这个文件主要是因为在blog过多之后如果继续使用白嫖的leancoud服务的话就容易报Too Many Request的问题而做的改进,在每次deploy的时候会对数据做一个本地备份,而在ci脚本中,之前完全没有意识到这个问题,从而导致了每次在ci服务器上生成的.memo文件生成完就被丢弃了,可以说完全没有起作用,所以主要也是要解决leancloud.memo的持久化问题.

思路

因为在配置Travis CI的时候已经授权过一个access token了,所以可以直接借用这个token来进行操作.

直接使用https的形式来进行文件的push:

git push -u https://${Travis_CI}@github.com/TangMisaka23001/TangMisaka23001.github.io.git source

只需要每次在deploy之后往源文件的仓库把memo文件的更新push进去就可以了.

添加的脚本如下:

1
2
3
4
5
6
7
8
# leancloud统计相关
# checkout命令比较玄学,个人并不是很理解因为clone下来的应该就是source分支的代码
# 当时也被这个坑了很久,猜测可能是在deploy的时候影响了仓库的分支吧
- git checkout source
- git add source/leancloud.memo
# [skip ci] 用于跳过因为这次commit而产生的ci构建防止构建套娃 (因为现在只检测 source分支有变动就会进行一次构建)
- git commit -m "update leancloud.memo [skip ci]"
- git push -u https://${Travis_CI}@github.com/TangMisaka23001/TangMisaka23001.github.io.git source

所以说持续保持ci脚本的正确性还是很重要的 !!!

现在ci脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
language: node_js   #设置语言
node_js: stable #设置相应的版本
notifications: #开启邮件通知
email:
recipients:
- mikasatang@gmail.com
on_success: always
on_failure: always
cache:
directories:
- node_modules #据说可以减少travis构建时间
before_install:
- npm install -g hexo-cli
install:
- npm install #安装hexo及插件
- npm install hexo-deployer-git --save
- npm install hexo-git-backup --save
- npm install hexo-leancloud-counter-security
script:
- hexo clean
- hexo g #生成
after_script:
# 替换同目录下的_config.yml文件中gh_token字符串为travis后台刚才配置的变量,注意此处sed命令用了双引号。单引号无效!
- sed -i "s/gh_token/${Travis_CI}/g" ./_config.yml
# 部署博客相关命令
- echo "misakatang.cn" > ./public/CNAME
- cp LICENSE ./public
- cp README.md ./public
- git config --global user.name "misakatang"
- git config --global user.email "mikasatang@gmail.com"
- hexo deploy
# leancloud统计相关
- git checkout source
- git add source/leancloud.memo
- git commit -m "update leancloud.memo [skip ci]"
- git push -u https://${Travis_CI}@github.com/TangMisaka23001/TangMisaka23001.github.io.git source
branches:
only:
- source #只监测这个分支,一有动静就开始构建
0%