alpakka-kafka(9)-kafka在分布式运算中的应用
kafka具备的分布式、高吞吐、高可用特性,以及所提供的各种消息消费模式可以保证在一个多节点集群环境里消息被消费的安全性:即防止每条消息遗漏处理或重复消费。特别是exactly-once消费策略:可以保证每条消息肯定只被消费一次。换句话说就是在分布式运算环境里kafka的消息消费是能保证唯一性的。
但是,保证了消息读取唯一性,消息的处理过程如果也放到分布式运算环境里仍然会面对数据完整性(data integrity)问题。例如:消息处理过程是更新银行账户中金额、消息内容是更新某个账户的指令,那么,对多条针对同一个银行账户的消息进行并行处理时肯定会引发数据完整性问题。这就是本文重点讨论的问题。
我们来看看下面的代码:
kfkSource
.async.mapAsync(parallelism=8) { msg => updateAccount(msg.value() }
.toMat(Sink.fold(0) { (accu, e) => if (e) accu + 1 else accu })(Keep.right)
.run()
在上面的例子里,从kafka队列里逐一读取的消息可能有多个被并行处理(最多有8个并行线程parallelism=8), 如果这8条消息里包含相同的账户号码,肯定会产生数据完整性问题。那么如果:
> kfkSource
.async.mapAsync(parallelism=1) { msg => updateAccount(msg.value() }
.toMat(Sink.fold(0) { (accu, e) => if (e) accu + 1 else accu })(Keep.right)
.run()
用(parallelism=1),这样每条消息用单一线程处理,牺牲一些效率,能解决问题吗?答案是:在这台服务器上貌似可以。但我们的目的是在一个多节点集群环境里进行数据处理。这也应该是我们使用kafka的初衷嘛。在分布式环境里上面的这段代码等于是在多个节点上同时运行,同样会产生像多线程并行运算所产生的问题。
显然:问题的核心是重复的消息内容,在上面的例子里是多条消息里相同的银行账号。如果相同的账号在同一个线程里进行处理就可以避免以上问题了。akka actor信箱里的指令是按序逐个执行的,所以我们如果能保证把相同内容的消息发给同一个actor就可以解决问题了。为了实现有目的的向actor发送消息,可以使用集群分片(cluster-sharding)。在akka-cluster里,每一个分片都就等于一个命名的actor。还有一个问题是如果涉及大量的唯一账号,或者商品号,比如超百万的唯一编号又该怎么办呢?刚才讲过:我们只要保证每一种消息发给同一个分片,多种消息是可以发个同一个分片的。所以,对于大量编号我们可以通过hash算法来简化编号精度,如下:
def hashItemCode(code: String): String = {
val arrCode = code.toCharArray
var occur : Array[Int] = Array.fill(8)(0)
arrCode.foreach {
case x if (x >= '0' && x <= '2') =>
occur(0) = occur(0) + 1
case x if (x >= '3' && x <= '5') =>
occur(1) = occur(1) + 1
case x if (x >= '6' && x <= '8') =>
occur(2) = occur(2) + 1
case x if (x == '9' || x == '-' || x == '_' || x == ':') =>
occur(3) = occur(3) + 1
case x if ((x >= 'a' && x <= 'g') || (x >= 'A' && x <= 'G')) =>
occur(4) = occur(4) + 1
case x if ((x >= 'h' && x <= 'n') || (x >= 'H' && x <= 'N')) =>
occur(5) = occur(5) + 1
case x if ((x >= 'o' && x <= 't') || (x >= 'O' && x <= 'T')) =>
occur(6) = occur(6) + 1
case x if ((x >= 'u' && x <= 'z') || (x >= 'U' && x <= 'Z')) =>
occur(7) = occur(7) + 1
case _ =>
occur(7) = occur(7) + 1
}
occur.mkString
}
这个hashItemCode返回一个字串,代表原编码code中各种字母发生的频率,把这个字串作为sharding的entityId。
那么从kafaka读取一条消息后按hashItemCode结果指定发送给某个分片,下面是一个实际例子:
def toStockWorker(jsonDoc: String) = {
val bizDoc = fromJson[BizDoc](jsonDoc)
val plu = bizDoc.pluCode
val entityId = DocModels.hashItemCode(plu)
log.step(s"CurStk-toStockWorker: sending CalcStock to ${entityId} with message: $jsonDoc")
val entityRef = sharding.entityRefFor(StockCalculator.EntityKey, entityId)
entityRef ! StockCalculator.CalcStock(jsonDoc)
}
下面我提供一个exactly-once源代码作为参考;
(1 to numReaders).toList.map {_ =>
RestartSource
.onFailuresWithBackoff(restartSource) { () => mergedSource }
// .viaMat(KillSwitches.single)(Keep.right)
.async.mapAsync(1) { msg => //only one message uniq checked
for { //and flow down stream
newtxn <- curStk.isUniqStkTxns(msg.value())
_ <- FastFuture.successful {
log.step(s"ExactlyOnceReaderGroup-futStkTxnExists is ${!newtxn}: ${msg.value()}")
}
} yield (newtxn,msg)
}
.async.mapAsyncUnordered(8) { rmsg => //passed down msg
for { //can be parrallelly processed
cmt <- if (rmsg._1) stkTxns.stkTxnsWithRetry(rmsg._2.value(), rmsg._2.partition(), rmsg._2.offset()).toFuture().map(_ => "Completed")
else FastFuture.successful {"stktxn exists!"}
pmsg <- FastFuture.successful {
log.step(s"ExactlyOnceReaderGroup-stkTxnsWithRetry: committed transaction-$cmt")
rmsg
}
} yield pmsg
}
.async.mapAsyncUnordered(8) { rmsg =>
for {
_ <- if(rmsg._1) FastFuture.successful {curStk.toStockWorker(rmsg._2.value())}
else FastFuture.successful(false)
pmsg <- FastFuture.successful {
log.step(s"ExactlyOnceReaderGroup-updateStk...")
rmsg
}
} yield pmsg
}
.async.mapAsyncUnordered(8) { rmsg =>
for {
_ <- if (rmsg._1) FastFuture.successful {
pcmTxns.toPcmAggWorker(rmsg._2.value())
}
else FastFuture.successful(false)
pmsg <- FastFuture.successful {
log.step(s"ExactlyOnceReaderGroup-AccumulatePcm...")
}
} yield "Completed"
}
.toMat(Sink.seq)(Keep.left)
.run()
}
alpakka-kafka(9)-kafka在分布式运算中的应用的更多相关文章
- 使用kafka消息队列解决分布式事务(可靠消息最终一致性方案-本地消息服务)
微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务 本文转自:http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一 ...
- Kafka — 高吞吐量的分布式发布订阅消息系统【转】
1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有哪两个条件 ...
- kafka高吞吐量的分布式发布订阅的消息队列系统
一:kafka介绍kafka(官网地址:http://kafka.apache.org)是一种高吞吐量的分布式发布订阅的消息队列系统,具有高性能和高吞吐率. 1.1 术语介绍BrokerKafka集群 ...
- zookeeper,kafka,redis等分布式框架的主从同步策略
1 zookeeper选主机制 1.1 LeaderElection选举算法 选举线程由当前Server发起选举的线程担任,他主要的功能对投票结果进行统计,并选出推荐的Server.选举线程首先向所有 ...
- Spark Streaming + Kafka整合(Kafka broker版本0.8.2.1+)
这篇博客是基于Spark Streaming整合Kafka-0.8.2.1官方文档. 本文主要讲解了Spark Streaming如何从Kafka接收数据.Spark Streaming从Kafka接 ...
- Kafka(1)--kafka基础知识
Kafka 的简介: Kafka 是一款分布式消息发布和订阅系统,具有高性能.高吞吐量的特点而被广泛应用与大数据传输场景.它是由 LinkedIn 公司开发,使用 Scala 语言编写,之后成为 Ap ...
- Kafka记录-Kafka简介与单机部署测试
1.Kafka简介 kafka-分布式发布-订阅消息系统,开发语言-Scala,协议-仿AMQP,不支持事务,支持集群,支持负载均衡,支持zk动态扩容 2.Kafka的架构组件 1.话题(Topic) ...
- 【Kafka】Kafka数据可靠性深度解读
转帖:http://www.infoq.com/cn/articles/depth-interpretation-of-kafka-data-reliability Kafka起初是由LinkedIn ...
- Zookeeper在分布式架构中的应用
Zookeeper 是一个高性能.高可靠的分布式协调系统,是 Google Chubby 的一个开源实现.Zookeeper 能够为分布式应用提供一致性服务,提供的功能包括:配置维护.域名服务.分布式 ...
随机推荐
- LeetCode 1482. 制作 m 束花所需的最少天数
LeetCode 1482. 制作 m 束花所需的最少天数 题目 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k 朵花 ...
- 【操作系统】Linux bash常用函数路径配置
临时方法:export PATH=/usr/bin:/usr/sbin:/bin:/sbin长期方法:1. vi /etc/profile2. 在最后插入并保存: export PA ...
- 数据可视化 gojs 简单使用介绍
目录 1. gojs 简介 2. gojs 应用场景 3. 为什么选用 gojs: 4. gojs 上手指南 5. 小技巧(非常实用哦) 6. 实践:实现节点分组关系可视化交互图 最后 本文是关于如何 ...
- Java初学者作业——声明变量对个人信息进行输入和输出
返回本章节 返回作业目录 需求说明: 声明变量存储个人信息(姓名.年龄.性别.地址以及余额),通过键盘输入个人信息并存储在相应的变量中, 最后将个人信息输出. 实现思路: 声明存储姓名.年龄.性别.地 ...
- docker学习:docker安装
Centos7 安装docker 下载安装 yum install docker-ce 启动docker systemctl start docker 创建并编写镜像加速文件 vim /etc/doc ...
- Elasticsearch安装X-Pack插件
Elasticsearch安装X-Pack插件, 基于已经安装好的6.2.2版本的Elasticsearch, 安装6.2.2版本的X-Pack插件. 1.下载x-pack的zip包到本地 https ...
- JZOJ5409. Fantasy && Luogu2048 [NOI2010]超级钢琴
题目大意 给出一个序列和\(L, R\), 求前k大长度在\([L,R]\)之间的连续子序列的和的和. 解题思路 朴素的想法是对于一个左端点\(p\), 它的右区间取值范围是一个连续的区间即\([p+ ...
- Android系统编程入门系列之硬件交互——通信硬件USB
在硬件交互的首篇对设备硬件的分类中,互联通信系列硬件主要用来与其他设备进行数据交互.从本文开始,将重点介绍该系列相关硬件. 互联通信系列硬件 根据硬件的可通信距离,由近及远分为USB.NFC.蓝牙.W ...
- JMeter跨线程,怎么定义全局变量,跨线程使用变量?
JMeter跨线程时,怎么定义全局变量,跨线程使用此变量? 通过函数助手,获取到设置变量的语法脚本 2.通过Bean shell Sampler取样器,定义全局变量 3.定义好全局变量,可以调用,调用 ...
- django rest framework 自定义验证器
一.基于钩子函数: 官网上的例子: 官方提示:如果字段声明在序列化类上时,就具有参数required=Fasle的作用,当函数名中没有包括字段名时,那么这个验证函数就不起作用 二.基于类的验证器: 使 ...