Run a task only once in (akka) cluster
在stackOverflow网站上看到这一提问,下文是部分摘抄问题简述:
Java cluster, run task only once
We have a java process, which listen's to a directory X on the file system using apache commons vfs. Whenever a new file is exported to this directory, our process kicks in. We first rename the file to filename.processing and parse the file name, get some information from the file and insert into tables, before sending this file to a Document management system. This is a single-threaded application per cluster. Now consider this running in a cluster environment, we have 5 server's. So 5 different VM's are trying to get access of the same file. The whole implementation was on the basis that only one process can rename the file to .processing at a given time, as OS will not allow multiple processes modifying the file at the same time. Once a cluster get's holds and renames file to .processing, other cluster's will ignore files which are of format .processing.
上述问题简而言之,就是假设软件里有一个监听某个文件变化以及命名服务,这在单机部署时一点问题都没有,但是当多机集群时,如果不加额外逻辑限制,那么每个集群节点都会去执行多个监听以及命名服务,对于文件系统,这是非常危险的。
还有比如,软件里存在一个定时发送email的服务,单机部署,那么接收方最多收到一份email。当多机集群部署时,如果不去额外限制,接收方应当接收到多个email。
问题是这个额外的逻辑限制应当怎么样去设计呢?
解决方案当然不止一种。
Via a shared database
第一种你可以利用数据库,建立job表。
找出需要执行的job,然后去执行它,大致逻辑类似如此。
SELECT *
FROM jobs
WHERE state = 'NotRun'
ORDER BY run_time ASC
UPDATE jobs
SET state = 'Running'
WHERE job_id = :id
AND state = 'NotRun'
Via a cluster singleton
借助集群单例即可。
我们来看看akka-cluster中对cluster singleton的描述。
For some use cases it is convenient and sometimes also mandatory to ensure that you have exactly one actor of a certain type running somewhere in the cluster.
我们知道akka的架构是树状层级结构。就是说每一个节点都对应着一个path。当akka.actor.provider为LocalProvider是,ActorPath是从/user/somePath开始。当集群时,也就是akka.actor.provider为ClusterProvider或者RemoteProvider时,ActorPath是由akka.tcp://systemName@hots:port/user/somePath组成。
代码解析:
system.actorOf(
ClusterSingletonManager.props(
singletonProps = Props(classOf[Consumer], queue, testActor),
terminationMessage = End,
settings = ClusterSingletonManagerSettings(system).withRole("worker")),
name = "consumer")
val proxy = system.actorOf(
ClusterSingletonProxy.props(
singletonManagerPath = "/user/consumer",
settings = ClusterSingletonProxySettings(system).withRole("worker")),
name = "consumerProxy")
上述代码分为两步骤。第一步是创建一个集群单例对象,第二步创建单例对象的代理对象。
那么为何需要这样设计呢?代理对象是逻辑地址,第一步创建的是单例的实际地址。
以下代码利用单例实现只执行一次的语义。
class OnlyExecuteOnce[A](exe: () => A) extends Actor {
override def preStart(): Unit =
exe()
override def receive: Receive = {
case _ => ()
}
}
object OnlyExecuteOnce {
// /user/onlyExecuteOnce1234
// /user/onlyExecuteOnce1234Proxy
def apply[A](id: String)(exe: () => A)(implicit system: ActorSystem) = {
system.actorOf(
ClusterSingletonManager.props(Props(classOf[OnlyExecuteOnce[A]], exe), PoisonPill, ClusterSingletonManagerSettings(system)),
name = s"onlyExecuteOnce${id}")
system.actorOf(ClusterSingletonProxy.props(s"/user/onlyExecuteOnce${id}", ClusterSingletonProxySettings(system)),
name = s"onlyExecuteOnce${id}Proxy")
}
}
参数id用于构建单例对象的path。
Run a task only once in (akka) cluster的更多相关文章
- akka cluster sharding source code 学习 (1/5) 替身模式
为了使一个项目支持集群,自己学习使用了 akka cluster 并在项目中实施了,从此,生活就变得有些痛苦.再配上 apache 做反向代理和负载均衡,debug 起来不要太酸爽.直到现在,我还对 ...
- Task.Run Vs Task.Factory.StartNew
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
- Task.Run Vs Task.Factory.StartNew z
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
- .Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法
前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...
- Akka Cluster简介与基本环境搭建
akka集群是高容错.去中心化.不存在单点故障以及不存在单点瓶颈的集群.它使用gossip协议通信以及具备故障自动检测功能. Gossip收敛 集群中每一个节点被其他节点监督(默认的最大数量为 ...
- Task.Run与Task.Factory.StartNew的区别
Task是可能有延迟的工作单元,目的是生成一个结果值,或产生想要的效果.任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者. 在.Net 4中,Task.Factory.Star ...
- C# Task.Run 和 Task.Factory.StartNew 区别
Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制.可以认 ...
- Task.Run Vs Task.Factory.StartNew 【收藏】
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
- akka cluster 初体验
cluster 配置 akka { actor { provider = "akka.cluster.ClusterActorRefProvider" } remote { log ...
随机推荐
- Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享
Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享 ============================= 蕃薯耀 2017年11月27日 http: ...
- android shape 大全 (转)
1. 各属性的配置语法 在项目 res/drawable 文件夹中创建一个以 shape 为根节点的 XML 文件,基本语法如下: <?xml version="1.0" e ...
- 实现JavaScript forEach
function forEach(list, callback){ ; n <list.length; n++){ callback.call(list[n], n); } } ,,,,,,,] ...
- React 页面间传值的个人总结
react 组件之间传值的方案有很多,下面是我个人经验的总结 props 来传递值 传值方式: 通过props 获取值 通过props 提供的func去修改值 优点: 不需要任何第三方的组件,纯rea ...
- PHP 静态缓存
今天来说说PHP页面的静态缓存. 根据个人理解,由于客户端重复的请求某个页面,导致该页面短时间内被重复请求相同的数据,导致给服务端一定的压力,同时用户访问速度也会变慢.此时如果把这个页面缓存起来,客户 ...
- jQuer __Ajax DOM
链接:在线jQueryhttp://www.bootcdn.cn 一.each(遍历) $("ul li").each(function(index,value){ ale ...
- flask-login使用笔记
看外国文献的中文翻译 翻译的程度有的让人会疯,翻译最好的状态是异译 直译会显的很生硬 看起来确实难过:所以在看的时候,建议都看外国文献吧,或者自己用谷歌翻译,感觉比一些翻译的博客准多了: 在使用fl ...
- labview生成可执行文件
labview生成可执行文件可以分为两种情况. 第一种,是电脑中有labview软件开发环境的情况 第二种,是电脑中没有安装labview软件开发环境 下面是一个简单的labview代码: 程序解释: ...
- Scala入门系列(十三):类型参数
引言 Scala中类型参数是什么呢?其实就类似于Java中的泛型.定义一种类型参数,比如在集合.类.函数中定义类型参数,然后就可以保证使用到该类型参数的地方就只能是这种类型,从而实现程序更好的健壮性. ...
- C++中指向类的指针
事情缘起是因为上班途中刷到了有个微博,那人说答对这个问题的请发简历. 看了就是关于指向C++类的指针的知识,原代码类似下面这样: class NullPointCall { public: void ...