IoT前沿 | 流數(shù)據(jù)處理難?一切都在計(jì)劃之中
各位讀者,《IoT前沿》欄目已經(jīng)進(jìn)行了三期啦~
之前的內(nèi)容里,我們已經(jīng)對(duì)Pravega的特性和優(yōu)勢(shì)進(jìn)行了介紹,相信大家對(duì)它已經(jīng)有了一個(gè)完整的了解。
現(xiàn)在問題來了,既然Pravega歪瑞酷,能夠解決未來流數(shù)據(jù)處理的難題,那么我要什么時(shí)候才能用上這項(xiàng)技術(shù)呢?
咳咳,雖然正式推出市場(chǎng)還在戴爾易安信的計(jì)劃之中...
但是,Pravega的發(fā)行版已經(jīng)開放,可以供大家下載使用了!
俗話說“眼過百遍不如動(dòng)手一遍”,Pravega說得再好,那也是王婆賣瓜,不如讓各位讀者親自動(dòng)手部署一番,因此在今天的這篇文章里,我們將為大家詳細(xì)介紹Pravega的部署方法。
Pravega從入門到精通,從這里開始~
作者簡(jiǎn)介
滕昱
滕昱:就職于Dell EMC中國(guó)研發(fā)集團(tuán),非結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)部門團(tuán)隊(duì)并擔(dān)任軟件開發(fā)總監(jiān)。2007年加入Dell EMC以后一直專注于分布式存儲(chǔ)領(lǐng)域。參加并領(lǐng)導(dǎo)了中國(guó)研發(fā)團(tuán)隊(duì)參與兩代Dell EMC對(duì)象存儲(chǔ)產(chǎn)品的研發(fā)工作并取得商業(yè)上成功。從2017年開始,兼任Streaming存儲(chǔ)和實(shí)時(shí)計(jì)算系統(tǒng)的設(shè)計(jì)開發(fā)與領(lǐng)導(dǎo)工作。
劉晶晶
劉晶晶:現(xiàn)就職于DellEMC,10 年+分布式、搜索和推薦系統(tǒng)開發(fā)以及架構(gòu)設(shè)計(jì)經(jīng)驗(yàn),現(xiàn)從事流存儲(chǔ)相關(guān)的設(shè)計(jì)與開發(fā)工作。
周煜敏
周煜敏:復(fù)旦大學(xué)計(jì)算機(jī)專業(yè)研究生,從本科起就參與Dell EMC分布式對(duì)象存儲(chǔ)的實(shí)習(xí)工作?,F(xiàn)參與Flink相關(guān)領(lǐng)域研發(fā)工作。
Pravega屬于戴爾科技集團(tuán)IoT戰(zhàn)略下的一個(gè)子項(xiàng)目。該項(xiàng)目是從0開始構(gòu)建,用于存儲(chǔ)和分析來自各種物聯(lián)網(wǎng)終端的大量數(shù)據(jù),旨在實(shí)現(xiàn)實(shí)時(shí)決策。其結(jié)合了戴爾易安信PowerEdge服務(wù)器,并無縫集成到非結(jié)構(gòu)化數(shù)據(jù)產(chǎn)品組合Isilon和Elastic Cloud Storage(ECS)中,同時(shí)擁抱Flink生態(tài),以此為用戶提供IoT所需的關(guān)鍵平臺(tái)。

戴爾科技集團(tuán)IoT解決方案集合了戴爾科技家族的力量,覆蓋從邊緣到核心再到云端
1 云原生與Pravega
隨著容器技術(shù)和云服務(wù)的發(fā)展,Kubernetes和云原生已經(jīng)重定義了應(yīng)用設(shè)計(jì)和開發(fā)的一些方面。
Pravega從設(shè)計(jì)之初就是云原生應(yīng)用,可以在各大公有/私有云平臺(tái)上進(jìn)行部署和運(yùn)行。
- 它的組件都是以低耦合的微服務(wù)形式存在,通過運(yùn)行多個(gè)服務(wù)實(shí)例保證高可用性。
- 每個(gè)服務(wù)實(shí)例運(yùn)行于單獨(dú)的容器中,使用容器實(shí)現(xiàn)服務(wù)的相互隔離。
- 可以使用容器編排工具(如Kubernetes)進(jìn)行統(tǒng)一的服務(wù)發(fā)現(xiàn)、治理和編排,提高資源利用率,降低運(yùn)營(yíng)成本。
同時(shí),Pravega團(tuán)隊(duì)通過第三方資源機(jī)制擴(kuò)展了Kubernetes的API,開發(fā)了能夠使得Pravega集群的創(chuàng)建、配置和管理更高效和自動(dòng)化的Operator,包括 Pravega Operator和Zookeeper Operator,通過他們可以使得Pravega在Kubernetes環(huán)境中快速創(chuàng)建集群和動(dòng)態(tài)擴(kuò)展。這些Operator以及其他相關(guān)的容器鏡像會(huì)上傳至Pravega在DockerHub官方的鏡像倉(cāng)庫(kù):https://hub.docker.com/u/pravega中,用戶也可以直接拉取使用,源代碼也在GitHub網(wǎng)站:https://github.com/pravega上公開。
2 Pravega核心組件及交互
Pravega能夠以一致的方式靈活地存儲(chǔ)不斷變化的流數(shù)據(jù),主要得益于控制面(Control Plane)和數(shù)據(jù)面(Data Plane)的有機(jī)結(jié)合。兩者都由低耦合的分布式微服務(wù)組件管理,前者主要由控制器(Controller)實(shí)現(xiàn),后者主要由段存儲(chǔ)器(Segment Store)實(shí)現(xiàn),它們通常以多實(shí)例的形式運(yùn)行在集群中。除此以外,一個(gè)完整的Pravage集群還包括一組Zookeeper實(shí)例,一組Bookkeeper實(shí)例,以及用于提供第二層的存儲(chǔ)的服務(wù)或接口。 它們的關(guān)系如圖:

控制器是Pravega的控制中心,對(duì)外提供 JAVA 和 REST 接口,接收客戶端對(duì)于Stream的創(chuàng)建、刪除、讀寫等請(qǐng)求;對(duì)內(nèi)負(fù)責(zé)Segment的管理和集群的管理??蛻舳藢?duì)于Stream的讀寫請(qǐng)求在控制器中被分拆為對(duì)Segment的請(qǐng)求,控制器確定需要使用哪些Segment,從而分發(fā)給相應(yīng)的段存儲(chǔ)器來操作。
段存儲(chǔ)器(Segment Store)提供了Segment的管理入口,實(shí)現(xiàn)了Segment的創(chuàng)建、刪除、修改和讀取功能。數(shù)據(jù)被存儲(chǔ)在一層和二層存儲(chǔ)上,由段存儲(chǔ)器負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和降層操作。其中一層存儲(chǔ)由低延遲的Bookkeeper擔(dān)任,通常運(yùn)行于集群的內(nèi)部;二層存儲(chǔ)由容量大且成本較低存儲(chǔ)的擔(dān)任,一般運(yùn)行于集群的外部。
Zookeeper做為集群的協(xié)調(diào)者, 它維護(hù)可用的控制器和段存儲(chǔ)器列表,控制器會(huì)監(jiān)聽它們的變化。當(dāng)一個(gè)控制器從集群中刪除時(shí),它的工作會(huì)被其他的控制器自動(dòng)接管。當(dāng)段存儲(chǔ)器發(fā)生變化時(shí),控制器也會(huì)將段容器重新映射以保證系統(tǒng)的正常運(yùn)行。
3 Pravega的部署
了解Pravega***方法就是自己動(dòng)手部署一個(gè),然后跑一下Pravega示例程序:
https://github.com/pravega/pravega-samples
單機(jī)版部署
單機(jī)版部署是最快捷的方式,你只需要從Pravega Release Github:https://github.com/pravega/pravega/releases下載一個(gè)Pravega發(fā)行版,解壓后運(yùn)行:
bin/pravega-standalone
單機(jī)版部署只能用來學(xué)習(xí)和測(cè)試,不能用于生產(chǎn)環(huán)境中,程序一旦關(guān)閉所有的數(shù)據(jù)也會(huì)丟失。
集群部署
Pravega可以運(yùn)行于多個(gè)主機(jī)所組成的集群中,也可以運(yùn)行于云平臺(tái)中。這里我們只介紹Kubernetes環(huán)境下的部署,其他的方式參考:
http://pravega.io/docs/latest/deployment/deployment/
運(yùn)行之前,需要保證你擁有一套Kubernetes環(huán)境,可以是公有云上的Kubernetes服務(wù)(如GKE,Amazon EKS),或者是分布式集群上自建的Kubernetes環(huán)境(如通過Kubeadm),以及命令行工具kubectl,helm。
1首先,在你的Kubernetes環(huán)境中創(chuàng)建一個(gè)Zookeeper集群。
Zookeeper集群可以使用Zookeeper Operator來創(chuàng)建,你可以直接使用deploy文件夾中的資源描述文件來部署。
git clone https://github.com/pravega/zookeeper-operator && cd zookeeper-operator
# 創(chuàng)建名為 ZookeeperCluster 的自定義資源定義(custom resource definition)
kubectl create -f deploy/crds/zookeeper_v1beta1_zookeepercluster_crd.yaml
# 創(chuàng)建 Zookeeper Operator 的服務(wù)賬號(hào)、角色和角色綁定,并部署 Zookeeper Operator
kubectl create -f deploy/default_nsall_ns/rbac.yaml
kubectl create -f deploy/default_nsall_ns/operator.yaml
# 部署 Zookeeper 集群,根據(jù)該資源描述文件,將會(huì)創(chuàng)建有三個(gè)節(jié)點(diǎn)的 Zookeeper 集群
kubectl create -f deploy/crds/zookeeper_v1beta1_zookeepercluster_cr.yaml
2然后,為Pravega第二層存儲(chǔ)創(chuàng)建單獨(dú)的持久化存儲(chǔ)卷(PV)及持久化存儲(chǔ)卷聲明(PVC)。
這里我們使用NFS Server Provisioner:https://github.com/kubernetes/charts/tree/master/stable/nfs-server-provisioner ,其他的方式請(qǐng)參考Pravega Operator的自述文件。
NFS Server Provisioner是一個(gè)開源工具,它提供一個(gè)內(nèi)置的NFS服務(wù)器,可以根據(jù)PVC聲明動(dòng)態(tài)地創(chuàng)建基于NFS的持久化存儲(chǔ)卷。
通過helm chart創(chuàng)建nfs-server-provisioner,執(zhí)行helm install stable/nfs-server-provisioner將會(huì)創(chuàng)建一個(gè)名為nfs的存儲(chǔ)類(StorageClass)、nfs-server-provisioner服務(wù)與實(shí)例、以及相應(yīng)的服務(wù)賬戶和角色綁定。
新建一個(gè)持久化存儲(chǔ)卷聲明文件pvc.yaml,這里storageClassName指定為nfs。當(dāng)它被創(chuàng)建時(shí),NFS Server Provisioner會(huì)自動(dòng)創(chuàng)建相應(yīng)的持久化存儲(chǔ)卷。pvc.yaml內(nèi)容如下:
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pravega-tier2 spec: storageClassName: "nfs" accessModes: - ReadWriteMany resources: requests: storage: 50Gi
通過kubectl create -f pvc.yaml創(chuàng)建該持久化存儲(chǔ)卷聲明,你會(huì)發(fā)現(xiàn)相應(yīng)的持久化存儲(chǔ)卷也被創(chuàng)建。
3接著,部署一個(gè)Pravega Operator。
你可以直接使用deploy文件夾中的資源描述文件部署:
git clone https://github.com/pravega/pravega-operator && cd pravega-operator kubectl create -f pravega-operator/deploy
這里會(huì)創(chuàng)建一個(gè)名為PravegaCluster自定義資源定義(Custom Resource Definition)、服務(wù)賬號(hào)、角色、角色綁定,并把Pravega Operator部署到Kubernetes集群中。
4***,修改資源描述文件并創(chuàng)建Pravega集群。
資源描述文件cr.yaml指定了Zookeeper地址、各組件的實(shí)例數(shù)和存儲(chǔ)空間。完整文件可以從這里獲得:
https://github.com/pravega/pravega-operator/tree/master/example
apiVersion: "pravega.pravega.io/v1alpha1"
kind: "PravegaCluster"
metadata:
name: "pravega"
spec:
# 配置 zookeeper 集群的地址
zookeeperUri: example-zookeepercluster-client:2181
# 配置 bookkeeper,建議至少三個(gè)實(shí)例
bookkeeper:
replicas: 3
...
pravega:
# 配置控制器實(shí)例,建議至少兩個(gè)實(shí)例
controllerReplicas: 2
# 配置段存儲(chǔ)器實(shí)例,建議至少三個(gè)實(shí)例
segmentStoreReplicas: 3
# 配置第二層存儲(chǔ),使用之前創(chuàng)建的持久化存儲(chǔ)卷聲明
tier2:
filesystem:
persistentVolumeClaim:
claimName: pravega-tier2
...
根據(jù)描述文件(cr.yaml)創(chuàng)建一個(gè)Pravega 集群:
kubectl create -f pravega-operator/example/cr.yaml
集群創(chuàng)建成功后,你可以通過以下命令查看集群的運(yùn)行狀態(tài):
kubectl get all -l pravega_cluster=pravega
4創(chuàng)建一個(gè)簡(jiǎn)單的應(yīng)用
讓我們來看看如何構(gòu)建一個(gè)簡(jiǎn)單的Pravega應(yīng)用程序。最基本的Pravega應(yīng)用就是使用讀客戶端(Reader)從Stream中讀取數(shù)據(jù)或使用寫客戶端(Writer)向Stream中寫入數(shù)據(jù)。兩個(gè)簡(jiǎn)單的例子都可以在Pravega示例中的gettingstarted應(yīng)用程序中找到:
https://github.com/pravega/pravega-samples/tree/master/pravega-client-examples/src/main/java/io/pravega/example/gettingstarted
要正確實(shí)現(xiàn)這些應(yīng)用,首先了解一下Pravega是如何高效并發(fā)地讀寫Stream:
- 為了實(shí)現(xiàn)并發(fā)地讀寫,Stream被分為一個(gè)或多個(gè)Segment,系統(tǒng)可以根據(jù)I/O負(fù)載動(dòng)態(tài)調(diào)整Segment的數(shù)目。
- 寫數(shù)據(jù)時(shí),多個(gè)Writer可以同時(shí)向多個(gè)Segment追加數(shù)據(jù)而無需知道它們的變化,由路由鍵(Routing key) 保證順序的一致性。
- 路由鍵是一個(gè)字符串,控制器會(huì)根據(jù)它的哈希值而決定該事件將會(huì)被派發(fā)到哪個(gè)Segment中。具有同樣路由鍵的事件會(huì)被派發(fā)到同一個(gè) Segment,這樣可以保證它們能以一致的順序被訪問。
- 如果Segment發(fā)生了變化,具有相同路由鍵的事件也會(huì)一致的被映射到新的Segment中。
- 讀數(shù)據(jù)時(shí),讀者組(ReaderGroup) 中的一組Reader可以同時(shí)從不同的Segment中讀數(shù)據(jù)。
- 一個(gè)ReaderGroup包含一個(gè)或多個(gè)Reader,每個(gè)Reader從一個(gè)或多個(gè)Segment中讀數(shù)據(jù)。
- 為了保證每個(gè)事件只被讀取一次,一個(gè)Segment只能被當(dāng)前ReaderGroup中的一個(gè)Reader讀。
- 一個(gè)ReaderGroup可以從一個(gè)或多個(gè)Stream中讀數(shù)據(jù),不同的ReaderGroup是相互獨(dú)立的。
- 寫數(shù)據(jù)只能向Stream的尾部追加,讀數(shù)據(jù)可以從指定位置讀。

5使用Writer向流中寫數(shù)據(jù)
示例HelloWorldWriter舉例說明了如何使用EventStreamWriter向Stream中寫一個(gè)事件。 我們來看一下其中最關(guān)鍵的run()方法:
❶ 使用StreamManager創(chuàng)建一個(gè)Scope。
StreamManager streamManager = StreamManager.create(controllerURI); final boolean scopeCreation = streamManager.createScope(scope);
StreamManager是創(chuàng)建、刪除和管理stream及scope的接口,通過指定一個(gè)控制器地址與控制器通信。
❷ 使用StreamManager創(chuàng)建一個(gè)Stream。
StreamConfiguration streamConfig = StreamConfiguration.builder()
.scalingPolicy(ScalingPolicy.fixed(1))
.build();
final boolean streamCreation = streamManager.
createStream(scope, streamName, streamConfig);
創(chuàng)建stream的時(shí)候需要指定scope,名稱和配置項(xiàng)。
其中,流配置項(xiàng)包括流的伸縮策略(Scaling Policy)和降層策略(Retention Policy)。Pravega支持三種伸縮策略,將會(huì)在下一篇《Pravega動(dòng)態(tài)彈性伸縮特性》中具體介紹。降層策略已經(jīng)在上一篇中介紹過。
❸ 使用ClientFactory創(chuàng)建一個(gè)Writer,并向Stream中寫數(shù)據(jù)。
try (ClientFactory clientFactory = ClientFactory.withScope(scope, controllerURI);
EventStreamWriter writer = clientFactory.createEventWriter(streamName, new JavaSerializer(), EventWriterConfig.builder().build())) { final CompletableFuture writeFuture = writer.writeEvent(routingKey, message); }
ClientFactory是用于創(chuàng)建Readers,Writers和其它類型的客戶端對(duì)象的工具,它是在Scope的上下文中創(chuàng)建的。ClientFactory以及由它創(chuàng)建的對(duì)象會(huì)消耗Pravega的資源,所以在示例中用try-with-resources來創(chuàng)建這些對(duì)象,以保證程序結(jié)束時(shí)這些對(duì)象會(huì)被正確的關(guān)閉。如果你使用其他的方式創(chuàng)建對(duì)象,請(qǐng)確保在使用結(jié)束后正確的調(diào)用這些對(duì)象的close方法。
在創(chuàng)建Writer的時(shí)候還需要指定一個(gè)序列化器,它負(fù)責(zé)把Java對(duì)象轉(zhuǎn)化為字節(jié)碼。事件在Pravega中是以字節(jié)碼的形式存儲(chǔ)的,Pravega并不需要知道事件的具體類型,這使得Pravega可以存儲(chǔ)任意類型的對(duì)象,由客戶端負(fù)責(zé)提供序列化/反序列化的方法。
用writeEvent方法將事件寫入流,需要指定一個(gè)路由鍵(Routing key)。
6使用Reader從流中數(shù)據(jù)
示例HelloWorldReader舉例說明了如何使用EventStreamReader從Stream中讀取事件,其關(guān)鍵部分也是在run()方法中。
❶ 使用ReaderGroupManager創(chuàng)建一個(gè)ReaderGroup。
final ReaderGroupConfig readerGroupConfig = ReaderGroupConfig.builder()
.stream(Stream.of(scope, streamName))
.build();
try (ReaderGroupManager readerGroupManager =
ReaderGroupManager.withScope(scope, controllerURI)) {
readerGroupManager.createReaderGroup(readerGroup, readerGroupConfig);
}
ReaderGroupManager類似于ClientFactory,也是在scope的上下文中創(chuàng)建的。
創(chuàng)建ReaderGroup需要指定名稱和配置項(xiàng),其中配置項(xiàng)規(guī)定了該ReaderGroup從哪些Stream中讀數(shù)據(jù),以及所要讀取的Stream的起止位置。Pravega具有Position的概念,它表示Reader當(dāng)前所在的Stream中的位置。應(yīng)用保留Reader***成功讀取的位置,Position的信息可以用于Checkpoint恢復(fù)機(jī)制,如果讀失敗了就從這個(gè)保存的檢查點(diǎn)重新開始讀。
❷ 創(chuàng)建一個(gè)Reader并從流中讀數(shù)據(jù)。
try (ClientFactory clientFactory = ClientFactory.withScope(scope, controllerURI); EventStreamReaderreader = clientFactory.createReader("reader", readerGroup, new JavaSerializer (), ReaderConfig.builder().build())) { EventRead event = null; do { event = reader.readNextEvent(READER_TIMEOUT_MS); } while (event.getEvent() != null); }
Reader也是由ClientFactory創(chuàng)建的。一個(gè)新建的Reader會(huì)被加入到相應(yīng)的ReadGroup中,系統(tǒng)根據(jù)當(dāng)前ReaderGroup的工作負(fù)載自動(dòng)分配相應(yīng)的段給新創(chuàng)建的Reader。Reader可以通過readNextEvent讀取事件。
由于Pravega的自動(dòng)伸縮功能,Segment的數(shù)量會(huì)隨著負(fù)載的變化而變化,當(dāng)ReaderGroup管理的Segment總數(shù)發(fā)生變化時(shí),會(huì)觸發(fā)段通知(SegmentNotification),ReaderGroup可以監(jiān)聽該事件并適時(shí)地調(diào)整Reader的數(shù)量。 如果當(dāng)前的Segment比較多,為了保證讀的并發(fā)性,建議增加Reader;反之,如果當(dāng)前的Segment比較少,建議減少Reader。由于Reader和Segment是一對(duì)多的關(guān)系,Reader的數(shù)量大于Segment的數(shù)量是沒有意義的。
本章總結(jié):
本期內(nèi)容我們重點(diǎn)介紹了Pravega的云原生特性、核心組件、安裝部署實(shí)踐以及Reader/Writer的基本應(yīng)用實(shí)踐。
截至目前,我們已經(jīng)花了4個(gè)篇幅(***期、第二期、第三期)詳細(xì)了Pravega,相信你對(duì)它已經(jīng)有了全面的了解,在下一期的內(nèi)容里,我們將對(duì)Pravega的僅一次語(yǔ)義及事務(wù)支持進(jìn)行介紹。歡迎大家持續(xù)關(guān)注,如何你有疑問,可在下方留言或知乎號(hào)上(見下方二維碼)找到我們。下一期見~

掃碼關(guān)注知乎號(hào)
你和戴爾易安信專家只有一條網(wǎng)線的距離~
往期回顧
5G時(shí)代下,大數(shù)據(jù)存儲(chǔ)面臨的三大挑戰(zhàn)
探尋流數(shù)據(jù)存儲(chǔ)Pravega的優(yōu)勢(shì)與特點(diǎn)
假如紐約出租車數(shù)據(jù)交給Pravega分析



























