微服务解惑

微服务与容器

微服务又指的是在传统应用架构的基础上,按照业务能力将系统拆分成多个服务,每个服务都是一个独立的应用,对外提供一些列的公共服务API,服务之间以轻量的方式互相调用。
微服务里的每个服务都是一个组件,通过编排组合从而达到独立、解耦、组件化、易维护、可复用、可替换、高可用的设计原则。微服务后,自动化部署以及运维是比较头疼的事,容器技术解决了这个问题。

  • 好的架构需要考虑后面的扩展以及修改
  • 好的架构是解耦的,需改一个地方不会影响另外一个地方
  • 好的架构是轻便灵活的,一个应用最好只解决一个问题,而不是叠加功能

微服务的标签

  • 单一职责
  • 面向服务
  • 自治
  • 易扩展
  • 流程化

微服务的不足

  • 时效性·服务间的调用延时可能导致系统相应慢的问题
  • 一致性·微服务在保证一致性上需要做更多的工作

微服务的价值

  • 资源价值,资源不足是自动扩容,资源过量时自动缩容;
  • 业务价值,工作量、人员数量、交付质量、交付周期;
  • 技术价值,技术是为业务来服务的(个人标注:技术也是业务的一部分而不只是为业务而服务)
  • 用户价值,用户体验好,服务上线快
  • 未来价值,技术不成为业务的瓶颈

微服务的小目标

  • 持续交付
  • 业务敏捷
  • 独立演进
  • 高可用
  • 高性能

微服务的拆与不拆

依据:数据模型、业务模型、关键指标,粒度平衡,边界合理

DevOPS

开发与运维是一个整体,devops是一种思维方式,微服务与devops是天生一对

SpringCloud特点

  • 功能齐全
  • 标准化
  • 简单方便
  • 按需取用
  • 轻量
  • 易扩展、易维护
  • 可复用性

分布式系统组件及操作

配置管理(Spring cloud config)、服务发现/调用(Feign)、断路器、智能路由(ZUUL)、微代理、控制总线、一次性Token、全局锁、决策竞选、分布式会话、集群状态。

注册中心(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、服务追踪(Sleuth,zipkin)、权限(string security)、接口可视化(Swagger)。

以上内容为《微服务那些事儿》读书笔记。

参考资料

[1]. 微服务那些事儿,纪晓峰著

开篇,马斯克们的Hyperloop

我们先来看张图,下图上部分是现在的高铁,它是跑在露天的轨道上的,下图是Elon Musk’s 在正吹的hyperloop,类似于跑在真空管道里的未来高铁。相比跑在露天轨道里的高铁,跑真空管道里的高铁好处多了:快,节能,安全,比飞机便宜。。。
技术是可以自己进化的,相信类似hyperloop的”高铁+真空管道”的模式就是未来的一种交通出行方式。

hyperloop

那么HYPERLOOP跟本文又有什么关系呢? 是不是有点扯远了?其实本文讲的就是类似给高铁加上真空管道的活,二者本质上是相同的。

管道,Unix/Linux的设计哲学

在Linux或者Unix系统里,有时候我们为了查询某个信息,会输入类似如下的命令行:

1
#cat *.log | grep –v ‘pipeline’ | sort –nr | head –n 10 | tail -5 | awk ‘{print $2}’ | wc –l  > /dev/stdout

这个命令行通过“|”来分隔多个命令,前面命令的输出是紧接着的后面命令的输入,命令之间通过“|”彼此相连,并且一个命令只做一件事情。这里的“|”就是管道,把一个程序的输出和另一个程序的输入连起来的一根管子。

在Unix/Linux里存在这样的管道命令设计哲学:

  • 程序是个过滤器
  • 一个程序只做一件事并且做到最好
  • 一个程序的输入是另外一个程序的输出

下图体现了这样的管道设计哲学,应用之间通过管道相连相互作用:

Uniux/linux pipeline

管道所要解决的问题是:高内聚,低耦合。它以一种“链”的方式将这些程序组合起来,让这些程序组成一条工作流,而每个程序又只作一件事情,给定输入,经过各个程序的先后处理,最终得到输出结果,如下图所示:

Uniux/linux pipeline

Unix/Linux在"每个程序只做一件事并且做好,每个程序的输出是对另一个程序的输入,可组合性"方面是做的非常成功的。但是,UNIX/Linux也存在一些局限性,比如:"仅单机,只支持一对一通信,无容错,仅字节流,数据处理能力有限等"。意思是说 linux/unix的这些管道命令只能在一台机器上跑,没有分布式,并且只能支持一个命令和另外一个命令之间的一对一的输入输出,无法一对多或多对一;无容错,假如管道坏了数据就出错不能恢复;只支持字节流,不支持数据格式的多样性;处理的数据量有限。

因此,我们希望可以找到一个数据处理解决方案,这个方案在保留这些Unix/linux管道的设计哲学优点的同时还能克服其缺点。 幸运的是,我们通过Flink+Pravega打造的第三代“流原生”(stream native)式的大数据处理平台实现了这种设计思想。

流原生,第三代大数据处理平台

下图体现了“流原生”(stream native)式的设计哲学,Flink是“流原生”的计算,Pravega是“流原生”的存储管道,Flink + pravega 是“流原生”的大数据处理平台。数据从pravega管道输入经过map算子计算,输出中间计算结果到pravega的管道里,数据又从pravega的管道里读入到filter算子里,再经过计算,中间结果放到了pravega管道里,再最后的计算结果经过聚合算子的计算放到了目的地的pravega的管道里。这个过程体现了算子编排和管道式编程的设计哲学。在这里pravega起了大数据处理平台里的管道的作用。

Stream processing pipeline

在Unix/Linux中,系统提供管道和命令,用于从一个进程到另一个进程获取字节流。

在“流原生”处理平台上,Flink提供流处理服务,pravega提供流存储服务,数据源自pravega,被Flink算子们处理后输出到pravega,这是一种将事件从一个流处理作业转移到另一个流处理作业的机制。 Flink和Pravega 所遵循的流处理平台设计哲学是:

  • 每个算子都只做一件事,并且做到最好
  • 每个算子的输出是另一个算子的输入
  • 可组合
  • 流式传输:数据是动态的,算子是静态的
  • 算子可编排
  • Pravega是最好的Flink搭档
  • 分布式,扩展到多台机器
  • 可进化的编码/解码

当前的流式处理平台一般是 Flink 加传统的存储类型,这种是”半流原生“式的大数据处理平台,计算是原生的流计算而存储却不是原生的流存储。
而Pravega就是专门给Flink们设计的原生流存储,它的数据传输方式类似于“管道”,不同于传统的块存储,文件存储以及对象存储,它是一个”管道式流存储“。

通过Flink + Pravega的组合可以实现 “流原生”(stream native)式的第三代大数据处理平台,未来已来。。。。。

思考题

最后给大家留个思考题,“流原生”(stream native)的概念有了,Flink + Pravega 也有了,而且二者的代码都是开源的(flink.apache.org, pravega.io),那么怎么把这些开源的东西产品化? 或者这个问题太伤脑筋,我们换个简单的问题:“今天中午吃什么?”

作者简介

常平,毕业于中国科学技术大学,获硕士研究生学历学位,10年+ 存储、布式系统、云计算以及大数据经验,曾就职于Marvell、AMD等,现就职于EMC,资深首席工程师,主要负责流式大数据处理平台的架构设计、编码及产品交付等。

阴阳五行

一说到阴阳五行就容易让人想到大街上的算命先生,然而阴阳五行学说却是中国古代解释世间万物的起源和多样性的哲学理论依据,是中国古代朴素的唯物论和自发的辩证法思想。

中国古代哲学的核心思想之一用“老子”的话来说就是:

“道生一、一生二、二生三、三生万物,万物负阴而抱阳,冲气以为和。”。

而五行学说讲的是:“金 木 水 火 土”这五行,五行相生又相克。木头烧火——木生火;火烧木头成灰——火生土,土长期聚在一起生石头、石头里炼金——土生金,金销水——金生水,水又生土。,水克火,火克金,金克木,木克土,土克水。

但是如下图,五行虽然相生相克但都是为“和”字而服务的,即平衡:

五行

解读开来就是:

“天道生阴阳,阴阳成五行,五行变化成万物,而万物的存在方式和相互关系一直在追求一种“和谐”。“道”在阴阳的相互作用下,产生五行,五行之间相互作用产生世间万物的无穷变化,并且阴阳之间对立消长,五行之间相生相克,自此万物得以和谐发展。借助于阴阳五行的核心要素以及由此而生的非核心要素关系把宇宙看成一个统一的整体,这样的整体:循环平衡、相生相克、有刚有柔、和谐统一

那么这些玄乎的哲学理论跟码农又有什么关系呢?对于本人这么个靠技术混饭吃卖身又卖艺的码农来说,这实在太重要,归纳成一个字就是”和”,对应到技术实现体系里就是一个理念 ”权衡“,英文叫tradeoff“tradeoff”这词实在是太妙了,啥都可以往上套,比如你十一准备到哪旅游啦,中午到哪吃饭啦,买哪里的房子啦,准备追哪个姑娘做老婆啦…….,都需要 tradeoff。技术如此人生又何尝不如是。

分布式系统

通常来讲设计分布式系统的时候需要考虑的最重要的核心要素有五个,这里不是说其他要素就不重要,这是指经过tradeoff过的五个最重要的核心要素,如下图:

分布式系统要素

  1. Capacity,容量,其实这个词翻译成”能力“会更合适,指的是分布式系统里的CPU,内存,硬盘,网络,文件描述符,socket连接数,老板的预期,开发周期,成本预算之类的限制条件,以下所有的要素都受 “容量”的限制,这是前提条件,就比如一辆车最多能跑多快,一个人最多能跳多高都是受自身“容量/能力”的限制的;

  2. Performant, performance + conformant, performant这词也是造的,指的是合适的性能,分布式系统的IOPS,TPS, QPS,Latency,Jitter之类的性能指标要求,性能受限于容量,性能同时又影响了可靠性以及可用性;

  3. Availability,可用性,可用性通常指的是产品或服务在随机时间内调用时处于可服务状态的概率,通常被定义为正常运行时间除以总时间(正常运行时间加停机时间),比如 5个9,6个9,还有个厂家都喜欢的号称的9个9之类的,可用性受容量的限制同时也受可伸缩性的影响,可用性又影响了性能;

  4. Reliability,可靠性,一般指的是出保证不出故障的概率,比如,企业级产品 5个9是保底,可测试性和可维护性通常被定义为可靠性当中的一部分,可伸缩性影响了可靠性,而可靠性又影响了可用性,同时性能又影响了可靠性,可靠性也影响着性能。

  5. Scalability,可伸缩性,这里很容易跟“可扩展性”混淆,可伸缩性可以指的是集群处理越来越多或越来越少的工作的能力,或者是为了适应这种增长或减少而扩大或缩小其能力的能力。可伸缩性影响了可用性,也影响了性能与可靠性,受限于容量。

当然还有另外一些由此而衍生的非核心要素,就不多做详细解释了,比如:

  • Testability,可测试性
  • Security,安全性
  • Observability,可观测性
  • Predictability,可预测性
  • Extensibility,可扩展性
  • Maintainability,可维护性
  • Serviceability, 可服务性

这些非核心要素虽然是非核心但是也不是说就不重要,是开源产品与商业产品差异的关键,关键在如何tradeoff

阴阳五行与分布式系统

将阴阳五行理论与分布式系统设计理论结合起来解读就是:

分布式系统里的“道”就是“产品”,“阴阳“ 就是 ”功能“ 与 “非功能”,五行就是 ”容量、性能、可用性、可伸缩性以及可靠性“,阴阳五行衍生的一些其他关系对应分布式系统五要素衍生的一些其他要素。

用人话来讲就是 开发产品的时候需要考虑功能与非功能两个方面,而要保证产品质量又需要考虑”容量、性能、可用性、可伸缩性以及可靠性“这些核心要素,但是也不能忽略由此而生的一些非核心要素。

那么从这些理论到产品又需要怎么做才能落地呢? 那自然是需要 懂得如何把从这些概念性的、功能的、非功能的、这些核心的、非核心的要素进行设计实现成代码,这就涉及到 “术”的层面了,“道”的层面可以通过看书看论文获得,而“术”的获得除了自身努力还得靠机会,而且每个人的悟性还不一样,这些个”术“以后有空慢慢讲。

思考题

最后给大家留一个思考题: 前面提过老子曰:”道生一、一生二、二生三、三生万物,万物负阴而抱阳,冲气以为和。“, 三之后就是万物,为什么不是 五、不是六、不是七之类的呢?为什么三之后就是万物了?

作者简介

常平,毕业于中国科学技术大学,获硕士研究生学历学位,10年+ 存储、布式系统、云计算以及大数据经验,曾就职于Marvell、AMD等,现就职于EMC,资深首席工程师,主要负责流式大数据处理平台的架构设计、编码及产品交付等。


注:

  1. 这个用五行解释分布式系统的观点,以前在一个业内微信群里提出并且聊过,所以这个解读的方式为本人原创非COPY.
  2. 个人愚钝,悟性有限,欢迎拍砖,砖多了我就拿回去砌墙。

参考资料:

[1]. https://baike.sogou.com/v7556185.htm

本页介绍如何在Kubernetes上部署Flink作业和会话群集。

设置Kubernetes

请参照Kubernetes的设置指南来部署Kubernetes集群。如果您想在本地运行Kubernetes,我们建议使用MiniKube来部署集群。

注意:如果使用MiniKube,请确保minikube ssh 'sudo ip link set docker0 promisc on'在部署Flink群集之前执行。否则,Flink组件无法通过Kubernetes服务自行引用。

Kubernetes上的Flink会话群集

Flink会话群集作为长期运行的Kubernetes部署来执行,请注意,可以在会话群集上运行多个Flink作业。在部署了集群之后,每个作业都需要提交到群集。

一个基本的部署在Kubernetes上的Flink会话群集一般会有三个组件:

  • 一个运行JobManager的deployment或job
  • 一个TaskManagers池 deployment
  • 一个公开JobManager的REST和UI端口的service

在Kubernetes上部署Flink会话群集

使用会话群集的资源定义,采用kubectl命令启动群集:

1
2
3
kubectl create -f jobmanager-service.yaml
kubectl create -f jobmanager-deployment.yaml
kubectl create -f taskmanager-deployment.yaml

然后,您可以通过kubectl proxy按以下方式访问Flink UI :

第一步,保证kubectl proxy在终端中运行

第二步,在浏览器里输入 http://localhost:8001/api/v1/namespaces/default/services/flink-jobmanager:ui/proxy

如果要终止Flink会话群集,可以使用如下命令:

1
2
3
kubectl delete -f jobmanager-deployment.yaml
kubectl delete -f taskmanager-deployment.yaml
kubectl delete -f jobmanager-service.yaml

Kubernetes上的Flink作业集群

Flink作业集群是运行单个作业的专用集群,这项作业是打包在flink镜像里的,因此,不需要提交额外的作业对象,步骤如下:

创建特定于作业的镜像

Flink作业集群镜像需要包含启动集群的作业的用户代码jar。因此,需要为每个作业构建专用的容器镜像。请按照这些说明构建Docker镜像。

在Kubernetes上部署Flink作业集群

要在Kubernetes上部署作业集群,请按照这些说明进行操作。

高级群集部署

GitHub上提供了早期版本的Flink Helm chart

附录

会话群集资源定义

部署使用的最新镜像 flink:latest 可在Docker Hub上找到。该镜像是用这个工具 https://github.com/docker-flink/docker-flink 构建的

jobmanager-deployment.yaml

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
"
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: flink-jobmanager
spec:
replicas: 1
template:
metadata:
labels:
app: flink
component: jobmanager
spec:
containers:
- name: jobmanager
image: flink:latest
args:
- jobmanager
ports:
- containerPort: 6123
name: rpc
- containerPort: 6124
name: blob
- containerPort: 6125
name: query
- containerPort: 8081
name: ui
env:
- name: JOB_MANAGER_RPC_ADDRESS
value: flink-jobmanager
"

taskmanager-deployment.yaml

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
"
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: flink-taskmanager
spec:
replicas: 2
template:
metadata:
labels:
app: flink
component: taskmanager
spec:
containers:
- name: taskmanager
image: flink:latest
args:
- taskmanager
ports:
- containerPort: 6121
name: data
- containerPort: 6122
name: rpc
- containerPort: 6125
name: query
env:
- name: JOB_MANAGER_RPC_ADDRESS
value: flink-jobmanager
"

jobmanager-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"
apiVersion: v1
kind: Service
metadata:
name: flink-jobmanager
spec:
ports:
- name: rpc
port: 6123
- name: blob
port: 6124
- name: query
port: 6125
- name: ui
port: 8081
selector:
app: flink
component: jobmanager
"

作者标注

经过验证, 到当前版本为止 flink-1.7 snapshot,构建 flink docker镜像需要采用这个flink docker 构建工具 https://github.com/docker-flink/docker-flink,按照flink官方代码库里的构建出来的flink镜像有些功能不能用,比如 flink-standalone模式,report metrics等。

Docker设置

Docker Hub上有关于Apache Flink的Docker镜像,可用于部署flink群集。Flink镜像库还包含用于创建容器映像以部署flink工作集群的一些工具以及说明。

Flink会话群集可用于运行多个业务。在部署后,每个业务都需要提交到集群才能跑起来。

Docker镜像

Flink镜像库托管在docker hub,提供了flink1.2.1以及之后的版本镜像。

注意: Docker镜像是由个人提供的社区项目,它们并不是Apache Flink PMC的官方版本(作者标注:所以需要用这个个人的构建工具,而不是官方代码库里的构建工具)。

Flink作业集群

Flink作业集群是运行单个作业的专用集群,这是镜像内容的一部分,因此,不需要额外的工作。

Docker镜像

Flink作业集群镜像需要包含启动集群的作业的用户代码jar。因此,需要为每个作业构建专用的容器镜像。该flink-container模块包含一个build.sh脚本,可用于创建此类镜像。有关详细信息,请参阅说明。(作者注:这个是官方的构建方式,试过有问题,比如跑 flink-standalone再 report metrics)

Flink与Docker Compose

Docker Compose是一种很方便的用于在本地启动一组Flink Docker容器的方式。

GitHub上提供了集群部署实例作业群集示例的配置文件。

用法

启动集群

$docker-compose up

以deamon的方式启动集群

$docker-compose up -d

集群扩展 N 个 TaskManagers

$docker-compose scale taskmanager=<N>

销毁集群

$docker-compose kill

当拉起一个Flink群集后,您可以访问 http:// localhost:8081的Web UI ,在界面里您还可以将作业提交到群集。

如果要通过命令行将作业提交到会话群集,必须将JAR复制到JobManager容器里并从那里执行作业。

例如:

1
2
3
$ JOBMANAGER_CONTAINER=$(docker ps --filter name=jobmanager --format={{.ID}})
$ docker cp path/to/jar "$JOBMANAGER_CONTAINER":/job.jar
$ docker exec -t -i "$JOBMANAGER_CONTAINER" flink run /job.jar

概述

通常,分布式存储系统以及分布式缓存系统习惯采用分布式哈希(DHT)算法来实现数据的分区分配(路由)以及负载均衡,普通的分布式hash算法通过增添虚拟节点,对物理的热点区间进行划分,将负载分配至其他节点,从而达到负载均衡的状态,但是这并不能保证集群的负载就一定很是的均衡。

而一种改进过的一致性Hash算法,即带边界因子的一致性Hash算法,其严格控制每个节点的负载从而能获得更好的负载均衡效果[1][2]。

普通的DHT算法

假设有8个Object,通过下图的DHT算法:

  1. object 0,1,2映射到了虚拟节点vNode0 : object 0,1,2 –> vNode0
  2. Object 3,4,5 映射到了vNode1:object 3,4,5 –> vNode1
  3. Object 6映射到 vNode2:object 6 –> vNode2
  4. Object 7映射到 vNodeN:object 7 –> vNodeN

distributed-DHT-1

distributed-DHT-2

很明显,Vnode0和vNode1 都落了三个 object,而 vNode2和vNodeN 都只落了 1个Object,这里的DHT算法负债均衡因子并不是很好。

带负载边界因子的DHT算法

假设有8个Object,通过如下图的DHT with bounded loads算法:

distributed-DHT-3

distributed-DHT-4

第一轮映射:

  1. object 0,1,2 需要映射到了虚拟节点vNode0,但是vNode0的权重因子是 2,因此只完成了 object 0,1 –> vNode0, object 2不能映射到节点 vNode0;
  2. Object 3,4,5 需要映射到了虚拟节点vNode1:但是vNode1的权重因子是 2,因此只完成了 object 3,4 –> vNode1, object 5不能映射到节点 vNode1;
  3. Object 6映射到 vNode2:object 6 –> vNode2
  4. Object 7映射到 vNodeN:object 7 –> vNodeN

第二轮映射:

  1. Object 2 映射到 vNode1,但是vNode1权重因子=0, 不能被接收,继续往下一个节点走,发现vNode2 权重因子是2,还剩权重因子1,可以被映射,因此 object 2–>vNode2
  2. Object 5 映射到 vNode2,但是vNode2现在的权重因子=0, 不能被接收,继续往下一个节点走,发现vNodeN 权重因子是2,还剩权重因子1,可以被映射,因此 object 5–>vNodeN

最终的映射结果是:

  1. object 0,1映射到了虚拟节点vNode0 : object 0,1 –> vNode0
  2. Object 3,4 映射到了vNode1:object 3,4 –> vNode1
  3. Object 2,6映射到 vNode2:object 2,6 –> vNode2
  4. Object 5,7映射到 vNodeN:object 5,7 –> vNodeN

很明显,Vnode0,vNode1,vNode2, vNodeN 每个节点都分到2个 object,
显然带负载边界因子的DHT算法负债均衡比普通的DHT算法来的好。

这些节点的负载因子可以从IO,CPU,MEM,Disk,Network等输入因子计算出来。

作者简介

常平,毕业于中国科学技术大学,获硕士研究生学历学位,10年+ 存储、布式系统、云计算以及大数据经验,曾就职于Marvell、AMD等,现就职于EMC,资深首席工程师,主要负责流式大数据处理平台的架构设计、编码及产品交付等。

参考资料

[1] https://research.googleblog.com/2017/04/consistent-hashing-with-bounded-loads.html

[2] https://medium.com/vimeo-engineering-blog/improving-load-balancing-with-a-new-consistent-hashing-algorithm-9f1bd75709ed

如果说互联网和云计算使得对象存储在存储市场上与块存储、文件存储三分天下,相应的业务需求直接奠定了对象存储与块存储、文件存储并列存储江湖一哥的地位,那么接下来也许我们需要为下一场数据变革的大事做好准备 – 万物互联这样的商业场景将给数据存储带来极大的商业挑战和技术挑战。

万物互联下的数据

纵观人类历史,各种技术变革都是以人类活动为中心,然后发明各种工具。石器时代,原始人发明了石器以及用火从而提升了生活品质和社会文明。现代社会,人类为了解决各种寂寞空虚冷吃穿住用行、生理和心理上的各种需求从而发明了各种社交空间、社交工具、网络购物、生活服务APP等,为了更好的服务这些应用场景,挖掘这些场景所生产的数据的价值,从而有了今天的各种大数据技术。

在互联网时代,数据主要来源于网页、APP以及一些相应的日志系统,而在万物互联的世界,数据还可以来源于有各种传感器、工业设备、监控设备、检测设备、智能家居、自动驾驶等。大数据的四个特征:数据量、时效性、多样性、价值密度在万物互联的场景下被进一步的深化,这就意味着商业成本以及技术成本的增加。

理论奠定技术的基础,业务驱使技术的变革。在万物互联的智能时代,我们有一个愿景: 能够将万物互联下生成的海量原始数据转化为可用的信息以及行为决策,并且这个转换的时间差需要能够接近于零。而需要实现这个愿景,从技术角度来看,需要有计算层面的解决方案也需要有存储层面的,如今在计算层面已经有Flink、Spark等这类成熟的分布式计算应用,然而在存储层面还没有。

流数据与流存储

在万物互联的场景下,各种传感器以及设备生成的数据有其原生的属性,这种数据自带时间戳、实时性要求高,而且是 “流数据”

首先流数据在百度百科里是这样被定义的:

流数据是一组顺序、大量、快速、连续到达的数据序列,一般情况下,数据流可被视为一个随时间延续而无限增长的动态数据集合。应用于网络监控、传感器网络、航空航天、气象测控和金融服务等领域。

从数据的生产与传输场景来看流数据具有几个与众不同的带有破坏性的特性:

  1. 数据随时间延续而无限增长,这意味着数据的无限性;
  2. 数据到达的速度有快有慢、负载有高有低,这意味着灵活又细粒度的资源弹性需求;
  3. 数据有序、无序、持久化以及复杂的传输环境而又要保证数据处理结果的唯一正确性。

这是三个特性转换成存储技术的语义对应着: 无限性、可伸缩性以及恰好一次:持久化、有序、一致性以及事务。

存储的视角来说,每种类型的数据都有其原生的属性和需求,对应有最佳的适用场景以及最合适的存储系统。跑在数据库里的数据对实时性和可靠性要求非常的高,因此适合采用块存储系统。文件共享场景下需要向用户共享文件,多个用户可以共享读取一个文件,因此适合采用文件存储系统。而互联网网页与APP里的文件、图像、视频可以看作一个个的数据对象又需要租户隔离以及无限扩展,因此又非常适合采用对象存储系统。那么目前又有哪种存储系统最适合用于 “流数据”呢?

正如当前技术条件下最适合 “流数据”计算的是类似Flink这样的分布式流计算应用,最适合“流数据”的应当是分布式流存储系统。

分布式流存储系统

产品定位

分布式流存储系统的产品定位是给万物互联这样的应用场景服务的,从技术角度来看它具有自身的特点,正如标题里提到的三个关键词: “分布式”、“流”、“存储”。首先是分布式的,它具有分布式系统本身所具有的一切能力,接着表示是专门给流式数据设计和实现的,最后的存储表示的是一个原生的存储解决方案,它讲究数据的 可靠性、持久化、一致性、资源隔离等,它从 存储的视角处理流数据。分布式流存储针对 “流数据” 的自身属性以及相应的特殊的业务需求场景做了专门的设计与实现,下面从 命名空间、业务场景、无限性、可伸缩性、恰好一次、字节流、数据管道、租户隔离、海量小文件、数据治理、流式架构的角度依据 最佳实践原则 讲述了为什么需要专门设计和实现一个流式存储系统。

命名空间

通常,块存储系统以分区、目录、文件,文件存储系统以目录、文件,以及对象存储以租户、桶、对象来定义数据的存储路径以及命名空间,而流存储系统则以范围(scope)、流(stream)、段(segment)、事件(event)来描述数据的存储路径以及命名空间。

类型 命名空间
块存储 分区、目录、文件
文件存储 目录、文件
对象存储 租户、桶、对象
流存储 范围、流、段、事件

在流存储系统里,如下图所示,数据的组织形式被抽象成范围、流、段和事件,范围由流组成,流由段组成,段由事件组成,事件由字节(bytes)组成。

流的组成

业务场景

可穿戴设备、自动驾驶与工业厂房

可以想象一下这样的业务场景:某个商家销售了几千万个智能手表,这些智能手表可以记录每个用户每天走了多少步,同时还可以分析过往的历史数据,用柱状图给用户展示历史数据,如下图所示:

步数分析

考虑到信息安全,用户A是不能看到用户B的数据的,那么就需要按智能手表为单位进行租户隔离,这种的场景下就有几千万个租户,同时每个租户还有自己的存储空间配额,比如给每个智能手表分配5GB 存储空间。光是这样的租户隔离场景,依据最佳实践的系统设计原则,不管是块存储系统、文件存储系统、对象存储系统还是Kafka这样的消息系统,按他们本身的隔离特性以及支持的租户规模都是难以在单个系统里支持这样的租户隔离场景。但是用流存储来实现就很方便,比如以智能手表的业务场景为例:

  • 默认分配5GB存储空间给一个智能手表,然后定义一个智能手表类型的命名空间用于与其他智能设备进行隔离,给每个智能手表分配一个流,每个智能手表上报的字节数据以事件为单位存储在流内的段里。
  • 也可以这样来定义:给每个智能手表分配一个5GB 存储空间的命名空间,手表里的每个传感器都对应一个流,每个传感器以事件为单位上报字节数据存储到流的段里。

还可以想象一下这样的业务场景:自动驾驶。采用分布式流存储的话,我们可以这样处理自动驾驶的数据:给每一辆无人车定义一个1TB存储空间的范围,车上的每个传感器都归属于一个流,传感器上报的事件都在段内持久化。再假设每辆车都有1000个传感器(实际情况只多不少),那么10万辆车就需要定义1亿个流,可以想象要进行这种规模的隔离也就只有这种专门针对流数据而设计的流存储系统能够支持。

在工业互联网的场景下,还可以这样定义工业设备的数据:给一个厂房里的每台设备定义一个范围,每台设备里的每个传感器都对应一个流,传感器上传的事件数据保存在流内的段里,这样就很方便的对工业设备进行了大规模的租户数据隔离。

因此,以“范围、流、段、事件”的方式很方便的进行了大规模的租户隔离保证了用户信息安全同时又进行了存储资源配额的隔离。

大数据处理平台

万物互联场景下无限量的数据给数据处理技术带来巨大的挑战与压力,不同的应用场景意味着不同的数据处理要求与复杂度,要把这些不同的甚至矛盾的数据处理要求都很好的综合在一个大数据处理系统里,对现有的大数据处理技术来说是个非常大的挑战,比如无人车的处理要求毫秒甚至纳秒级的数据处理实时性、而有些工业设备数据只需要分析历史数据,要让一个大数据处理系统既能能处理历史数据又能提供毫秒级甚至纳秒级的实时性处理能力还能应对各种不同格式不同传输场景的数据,而且每种数据处理都能达到这些应用场景原生指标的处理需求。相信这样的场景对工程技术人员来说是个很大的挑战。为了解决上述问题,按照现有的成熟的技术能力,通常开发人员采用类似Lambda架构(如下图)这样的大数据处理平台来处理大数据。

Lambda架构

Lambda架构即支持批处理也支持实时处理,能应对数据的多样性、具有容错功能、复杂性分离、能处理流式数据也能处理历史数据等优点,但是缺点也很明显:批处理一套独立的数据处理路径,实时处理又一套数据处理路径,然后还要合并结果再输出展示,同时系统里同样的数据存在存储多份的问题,比如同样的数据在Elasticsearch里有、HDFS里有、ceph里有、Kafka里也有,除了这些甚至还存在其他一些复杂的存储组件,而且同样的数据还都是多份冗余的,因此存储成本太高太过于复杂。Lambda架构里为了提供一个功能却引入一个组件,在复杂之上堆积复杂,存储成本、开发与运维成本都太过于复杂。

那么应当如何解决Lambda架构带来的这些缺点?以数据流向为核心重构大数据处理平台是一个比较好的方案,它具体包括数据的采集、聚合、传输、缓存、持久化、处理、展示等。依据这种设计理念我们可以推出一个端到端的原生的流式大数据处理平台:原生的流式计算加上一个原生的流式存储并且可以平衡商业成本与技术成本。

流式计算可以采用Flink,然而并没有发现当前有合适的流式存储可以使用,如果采用Flink加上传统的文件存储或者块存储、对象存储的方式,也只能认为是半原生的大数据处理平台:计算是原生的流式计算而存储却不是原生的流式存储

因此,综合思考万物互联场景下的数据处理场景也需要一个原生的分布式流存储系统,重构Lambda架构里的存储栈,使得分布式流计算加上分布式流存储即为原生的流式大数据处理系统,同时还能很好的平衡商业成本与技术成本之间的关系。

数据无限性

无限性是分布式流存储最为重要的设计原则。从流数据的角度来看,数据是大量、快速、连续而又无限的,这就给流存储系统的设计与实现带来极大的困难,无限的数据使得存储系统必须能支持连续且无限规模的数据流,光这一点就对存储系统的可扩展性要求非常的高,同时还要求存储系统能够根据到达的数据量动态而又优雅地进行扩容与缩容。从技术与成本的角度来看,数据无限性意味着冷热数据分离,长期不用的数据淘汰到长期存储系统里,热点数据需要缓存,同时还需要能支持历史数据的读取与实时数据的读取与写入。

可伸缩性

可伸缩性也是分布式流存储最为重要的设计原则之一,而且流存储里的可伸缩性要求还是自动化的资源细粒度的可伸缩。通常,在云原生的场景下,资源的缩放是以主机、虚机或容器为单位的,这样的缩放对流存储来说粒度太大。在流存储的场景下需要能够以数据的“流段”为单位,比如一个流段2MB,那么就需要能支持一次自动扩容或缩容2MB的存储空间。另外在流存储里还要求写入与读取对数据子集的操作是解耦分离的,并且写入与读取二者之间跟数据流段还要有一个合理的平衡。

恰好一次

恰好一次也是分布式流存储最为重要的设计原则之一,恰好一次意味着数据的可持久化、有序、一致性以及事务性的支持。持久性意味着一旦得到确认,即使存储组件发生故障,写入的数据也不会丢失。有序意味着读客户端将严格按照写入的顺序处理数据。一致性意味着所有的读客户端即使面对存储故障、网络故障也都会看到相同的有序数据视图。事务性写入对于保证Flink这样的计算应用处理结果的完全正确是非常必要的。

字节流

分布式流存储里采用字节流的格式组织数据而不是像消息系统里采用消息报文的方式,这意味着接口的通用性。二进制的字节流是与数据格式无关的,字节流可以组成事件封装在分布式存储的流段里。而消息系统里数据是消息头消息体的格式封装的,在兼容性上不如字节流。

数据管道

在存储界通常喜欢用跑车、卡车、渡轮来比喻块存储、文件存储以及对象存储,打个比方来说块存储类似跑车:极快、极稳、装的人少、成本高;文件存储类似卡车:快、稳、装的人比跑车多,但是没跑车那么快;对象存储类似渡轮:可以装非常多的货,讲究量大、成本低;那么分布式流存储像什么呢? 在我们的定义里它就像管道:数据如同流水一般流过管道,又快又稳源源不断而又永无止境

租户隔离

分布式流存储从一开始设计的时候就将”租户隔离“作为其基本特性进行实现,”隔离“是分布式流存储的最基本的特性之一,在分布式流存储里租户隔离不只是租户B绝对不能看的到租户A的任何信息这样的信息安全层面的隔离,它支持范围、流、段、事件层面的隔离还将支持的租户规模作为设计的目标之一,在分布式流存储里单集群需要能支持千万量级起的租户数,另外还有资源、命名、可视空间、权限以及服务质量层面的隔离。

海量小文件

对巨量小文件的支持是分布式流存储的设计原则之一。正如前面提到的,万物互联下的海量数据来源于传感器,而传感器上传的数据都是类似温度、地理位置、告警信息这样的几个字节几个字节的小数据,这就意味着在万物互联的场景下会有巨量的小数据上传,而且90%以上的数据操作行为都是写入。为了保证数据写入的性能以及可靠性、正确性、持久性以及保证介质的使用寿命降低成本,这也需要存储系统针对这种业务场景进行专门的设计。

在分布式流存储里每个事件第一步是被仅附加写入一个缓存的段内进行封装的,在段达到一定的尺寸(比如64MB)后会被封闭不再写入,这时再将整个段写入下一级的持久化存储里。通过这样的设计,实现小数据在缓存里封装成大块的数据,再将大块数据写入持久化存储设备的方式保证了存储系统整体的性能。

数据治理

当前的大数据处理平台,不管是Kappa架构还是lambda架构,数据的存储都是多组件化、多份化的。比如同样的数据在Kafka里有、在HDFS里有、在Elasticsearch里又有,有些用户还使用了更多的存储中间件,而且这些数据还是多份冗余的。这一方面增加了数据的存储成本,另一方面也降低了数据的可信性、可靠性、合规性,给数据标准化以及数据的重复利用带来了困难,不利于数据的分享、合规、降低成本以及安全可靠地支持业务和决策。数据治理也是分布式流存储的基本设计原则之一,通过使用分布式流存储,大数据处理平台的架构可以进化成”分布式流计算+ 分布式流存储“这样的原生流式数据处理平台架构。

流式架构

下图体现了”分布式流计算+ 分布式流存储“这样的原生流式大数据处理平台的架构理念。

流式架构

这个架构体现了 “流原生”(stream native)式 的设计哲学,“流原生”的计算加上“流原生”的存储管道组成了“流原生”的大数据处理平台。数据从分布式流存储输入经过map算子计算,输出中间计算结果到分布式流存储里,数据又从分布式流存储里读入到Filter算子里,再经过计算,中间结果放到了分布式流存储里,再最后的计算结果经过聚合算子的计算放到了目的地的分布式流存储里。这个过程体现了算子编排和管道式编程的设计哲学,在这里分布式流存储起了大数据处理平台里的管道的作用。

同时,在分布式流存储里数据的存储单位是流段,当输入的数据速率或者负载增加时,流段就会自动扩容,通过流协议联动,流计算应用的算子也相应扩容。相应的,如果输入的数据速率或负载降低,流段就自动收缩,通过流协议联动,流计算应用的算子也相应的缩容,所有这些行为都是自动完成的,无需人工干预,这种行为体现了分布式流存储的细粒度可伸缩性。

小结

综上所述,在万物互联的智能世界里,为了实现将海量数据近实时转化成信息和决策的愿景,除了流式计算应用还需要一个流式存储系统,未来已来,已有开源的分布式流存储系统正走在这条路上。另本文仅为作者愚见,与任何组织机构无关,作者能力也很有限,如有不足之处欢迎留言批评指正。

问题思考

最后给大家留一个思考题:如果让你来设计一个分布式流存储产品,你会如何定义它的产品灵魂?

作者简介

常平,中科大硕,10年+数据相关经验,主要工作背景为分布式系统、存储、缓存、微服务、云计算以及大数据,现就职于DELL EMC。个人技术博客:https://changping.me

版权申明

本文的版权协议为 CC-BY-NC-ND license:https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh ,可以自由阅读、分享、转发、复制、分发等,限制是需署名、非商业使用(以获利为准)以及禁止演绎。