分布式应用框架Akka快速入门
转自:http://blog.csdn.net/jmppok/article/details/17264495
本文结合网上一些资料,对他们进行整理,摘选和翻译而成,对Akka进行简要的说明。引用资料在最后列出。
1.什么是Akka
Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Java 和 Scala 的 Actor 模型应用。
Akka is a toolkit and runtime for building highly concurrent, distributed, and fault tolerant event-driven applications on the JVM.
Build powerful concurrent & distributed applications more easily.
翻译成中文就是:Akka是一个开发库和运行环境,可以用于构建高并发、分布式、可容错、事件驱动的基于JVM的应用。使构建高并发的分布式应用更加容易。
Akka可以以两种不同的方式来使用
- 以库的形式:在web应用中使用,放到 WEB-INF/lib 中或者作为一个普通的Jar包放进classpath。
- 以微内核的形式:你可以将应用放进一个独立的内核。
2.Akka的五大特性
Akka在设计时采用了异步通讯和分布式架构,并对上层进行抽象,如Actors、Futures ,STM等。
2)可靠性(Resilient by Design)
系统具备自愈能力,在本地/远程都有监护。
3)高性能(High Performance)
在单机中每秒可发送50000000个消息。内存占用小,1GB内存中可保存2500000个actors。
4)弹性,无中心(Elastic — Decentralized)
5)可扩展(Extensible)
可以使用Akka 扩展包进行扩展。
3.什么场景下特别适合使用Akka?
我们看到Akka被成功运用在众多行业的众多大企业,从投资业到商业银行、从零售业到社会媒体、仿真、游戏和赌博、汽车和交通系统、数据分析等等等等。任何需要高吞吐率和低延迟的系统都是使用Akka的候选。
Actor使你能够进行服务失败管理(监管者),负载管理(缓和策略、超时和隔离),水平和垂直方向上的可扩展性(增加cpu核数和/或增加更多的机器)管理。
下面的链接中有一些Akka用户关于他们如何使用Akka的描述:http://stackoverflow.com/questions/4493001/good-use-case-for-akka
所有以上这些都在这个Apache2许可的开源软件中。
以下是Akka被部署到生产环境中的领域
事务处理 (在线游戏,金融/银行业,贸易,统计,赌博,社会媒体,电信):垂直扩展,水平扩展,容错/高可用性
服务后端 (任何行业,任何应用):提供REST, SOAP, Cometd, WebSockets 等服务 作为消息总线/集成层 垂直扩展,水平扩展,容错/高可用性
并发/并行 (任何应用):运行正确,方便使用,只需要将jar包添加到现有的JVM项目中(使用Scala,java, Groovy或jruby)
仿真:主/从,计算网格,MaReduce等等.
批处理 (任何行业):Camel集成来连接批处理数据源 Actor来分治地批处理工作负载
通信Hub (电信, Web媒体, 手机媒体):垂直扩展,水平扩展,容错/高可用性
游戏与赌博 (MOM, 在线游戏, 赌博):垂直扩展,水平扩展,容错/高可用性
商业智能/数据挖掘/通用数据处理:垂直扩展,水平扩展,容错/高可用性
复杂事件流处理:垂直扩展,水平扩展,容错/高可用性
4. Scala语言
Scala是一种多范式的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
百度百科,Scala语言介绍:
百度文库,Scala编程入门:
http://wenku.baidu.com/view/27cbf218964bcf84b9d57b83.html
5.Actors模型
Actor,可以看作是一个个独立的实体,他们之间是毫无关联的。但是,他们可以通过消息来通信。
一个Actor收到其他Actor的信息后,它可以根据需要作出各种相应。消息的类型可以是任意的,消息的内容也可以是任意的。这点有点像webservice了。只提供接口服务,你不必了解我是如何实现的。
一个Actor如何处理多个Actor的请求呢?它先建立一个消息队列,每次收到消息后,就放入队列,而它每次也从队列中取出消息体来处理。通常我们都使得这个过程是循环的。让Actor可以时刻处理发送来的消息。
6.示例(http://www.th7.cn/Program/java/2012/03/29/67015.shtml)
应用场景:服务端要处理大量的客户端的请求,并且处理请求耗费较长的时间。这时就需要使用并发处理。多线程是一种方法,这里使用Akka框架处理并发。(以下代码在Groovy1.7.5、akka-actors-1.2下运行成功)
这里有三个角色:Client、Master、Worker
Client傻乎乎地发同步请求给Master,一直等到结果返回客户端才离开。
Master接收客户端发来的请求,然后将请求交给Worker处理,处理完成之后将结果返回给Client。
Worker负责具体的业务处理,它耗费的事件比较长。
所以这里的关键在于Master,如果Master线性地“接收请求――调用Worker处理得到返回结果――将结果返回”,这样的系统必将歇菜。
使用Akka可以方便地将它变成并行地。
先看看Client,模拟同时多个客户端给Master发请求
import akka.actor.ActorRef
import static akka.actor.Actors.remote
class HelloClient implements Runnable {
int seq
String serviceName
HelloClient(int seq, String serviceName) {
this.seq = seq
this.serviceName = serviceName
}
void run() {
ActorRef actor = remote().actorFor(serviceName, "10.68.15.113", 9999);
String str = "Hello--" + seq
println "请求-----${str}"
Object res = actor.sendRequestReply(str)
println "返回-----${res}"
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new HelloClient(i, "hello-service"))
thread.start() //同时启动5个客户端请求Master
}
}
}
真正干活的Worker:
import akka.actor.UntypedActor
class HelloWorker extends UntypedActor { //Worker是一个Actor,需要实现onReceive方法
@Override
void onReceive(Object o) {
println "Worker 收到消息----" + o
if (o instanceof String) {
String result = doWork(o) //调用真实的处理方法
getContext().replyUnsafe(result)//将结果返回给Master
}
}
//Worker处理其实很简单,仅仅将参数字符串改造一下而已。只不过使其sleep了20秒,让它变得“耗时较长”
String doWork(String str) {
Thread.sleep(1000 * 20)
return "result----" + str + " 。"
}
}
负责并发调度的Master:
import akka.actor.ActorRef
import akka.actor.Actors
import akka.actor.UntypedActor
import akka.actor.UntypedActorFactory
import akka.dispatch.Future
import akka.dispatch.Futures
import java.util.concurrent.Callable
class HelloMaster extends UntypedActor {
@Override
void onReceive(Object o) {
println "Master接收到Work消息:" + o
def clientChannel = getContext().channel() //客户端链接Channel
//启动worker actor
ActorRef worker = Actors.actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new HelloWorker();
}
}).start();
//这里实现真正的并发
Future f1 = Futures.future(new Callable() {
Object call() {
def result = worker.sendRequestReply(o) //将消息发给worker actor,让Worker处理业务,同时得到返回结果
worker.stop()
println "Worker Return----" + result
clientChannel.sendOneWay(result) //将结果返回给客户端
return result
}
})
println "Future call over"
}
public static void main(String[] args) { //启动Master进程,绑定IP、端口和服务
Actors.remote().start("10.68.15.113", 9999).register(
"hello-service",
Actors.actorOf(HelloMaster.class));
}
}
看看客户端的调用日志
请求-----Hello--4
请求-----Hello--1
请求-----Hello--3
请求-----Hello--0
请求-----Hello--2
[GENERIC] [11-10-6 下午9:49] [RemoteClientConnected(akka.remote.netty.NettyRemoteSupport@195b6aad,/10.68.15.113:9999)]
[GENERIC] [11-10-6 下午9:49] [RemoteClientStarted(akka.remote.netty.NettyRemoteSupport@195b6aad,/10.68.15.113:9999)]
返回-----result----Hello--0 。
返回-----result----Hello--1 。
返回-----result----Hello--2 。
返回-----result----Hello--4 。
返回-----result----Hello--3 。
服务端的日志:
[GENERIC] [11-10-6 下午9:49] [RemoteServerClientConnected(akka.remote.netty.NettyRemoteSupport@5a4fdf11,Some(/10.68.15.113:53462))]
Master接收到Work消息:Hello--1
Future call over
Master接收到Work消息:Hello--2
Future call over
Worker 收到消息----Hello--1
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-13] [HelloWorker] started
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-13] [HelloWorker] started
Worker 收到消息----Hello--2
Master接收到Work消息:Hello--0
Future call over
Master接收到Work消息:Hello--3
Worker 收到消息----Hello--0
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-7] [HelloWorker] started
Future call over
Master接收到Work消息:Hello--4
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-7] [HelloWorker] started
Worker 收到消息----Hello--3
Future call over
Worker 收到消息----Hello--4
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-7] [HelloWorker] started
Worker 将消息Hello--1处理完成
Worker 将消息Hello--2处理完成
Worker Return----result----Hello--2 。
Worker Return----result----Hello--1 。
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-13] [HelloWorker] stopping
Worker 将消息Hello--0处理完成
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-3] [HelloWorker] stopping
Worker Return----result----Hello--0 。
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-23] [HelloWorker] stopping
Worker 将消息Hello--4处理完成
Worker 将消息Hello--3处理完成
Worker Return----result----Hello--4 。
Worker Return----result----Hello--3 。
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-11] [HelloWorker] stopping
[DEBUG] [11-10-6 下午9:49] [akka:event-driven:dispatcher:global-10] [HelloWorker] stopping
可以从服务端日志看到,Master接收到Work消息后onReceive就结束了(函数最后打印Future call over),一连接收了5个消息,然后Worker才收到消息并处理。最后消息处理完成好后f1的call才收到Worker Return的消息。
这里使用Future实现并发。
如果不使用Future:
def result = worker.sendRequestReply(o) //将消息发给worker actor
println "Worker Return----" + result
getContext().replyUnsafe(result) // 将worker返回的消息回复给客户端
这就成了同步处理(第一个消息处理完后才接收并处理第二个消息)。
如果在Future后调用了f1.await()或f1.get(),也成同步的了,因为await将等待worker返回后再继续往下执行。
Future f1 = Futures.future(new Callable() {
Object call() {
def result = worker.sendRequestReply(o) //将消息发给worker actor
worker.stop()
println "Worker Return----" + result
clientChannel.sendOneWay(result)
return result
}
})
println "Future call over" + f1.get()
服务器日志如下:
[GENERIC] [11-10-6 下午10:06] [RemoteServerStarted(akka.remote.netty.NettyRemoteSupport@7e566633)]
[DEBUG] [11-10-6 下午10:06] [main] [HelloMaster] started
[GENERIC] [11-10-6 下午10:07] [RemoteServerClientConnected(akka.remote.netty.NettyRemoteSupport@7e566633,Some(/10.68.15.113:53571))]
Master接收到Work消息:Hello--0
[DEBUG] [11-10-6 下午10:07] [akka:event-driven:dispatcher:global-3] [HelloWorker] started
Worker 收到消息----Hello--0
Worker 将消息Hello--0处理完成
[DEBUG] [11-10-6 下午10:07] [akka:event-driven:dispatcher:global-5] [HelloWorker] stopping
Worker Return----result----Hello--0 。
Future call overresult----Hello--0 。
Master接收到Work消息:Hello--2
Worker 收到消息----Hello--2
[DEBUG] [11-10-6 下午10:07] [akka:event-driven:dispatcher:global-3] [HelloWorker] started
Worker 将消息Hello--2处理完成
Worker Return----result----Hello--2 。
Future call overresult----Hello--2 。
Master接收到Work消息:Hello--3
[DEBUG] [11-10-6 下午10:07] [akka:event-driven:dispatcher:global-10] [HelloWorker] stopping
[DEBUG] [11-10-6 下午10:07] [akka:event-driven:dispatcher:global-3] [HelloWorker] started
Worker 收到消息----Hello--3
Worker 将消息Hello--3处理完成
Worker Return----result----Hello--3 。
Future call overresult----Hello--3 。
Master接收到Work消息:Hello--4
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-14] [HelloWorker] stopping
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-3] [HelloWorker] started
Worker 收到消息----Hello--4
Worker 将消息Hello--4处理完成
Worker Return----result----Hello--4 。
Future call overresult----Hello--4 。
Master接收到Work消息:Hello--1
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-18] [HelloWorker] stopping
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-3] [HelloWorker] started
Worker 收到消息----Hello--1
Worker 将消息Hello--1处理完成
Worker Return----result----Hello--1 。
Future call overresult----Hello--1 。
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-21] [HelloWorker] stopping
Master接收到Work消息:Hello--6
[DEBUG] [11-10-6 下午10:08] [akka:event-driven:dispatcher:global-24] [HelloWorker] started
Worker 收到消息----Hello--6
Worker 将消息Hello--6处理完成
Worker Return----result----Hello--6 。
Future call overresult----Hello--6 。
Master接收到Work消息:Hello--5
[DEBUG] [11-10-6 下午10:09] [akka:event-driven:dispatcher:global-26] [HelloWorker] stopping
Worker 收到消息----Hello--5
[DEBUG] [11-10-6 下午10:09] [akka:event-driven:dispatcher:global-24] [HelloWorker] started
需要注意的是,Akka默认使用环境变量%AKKA_HOME%/config/akka.conf配置,默认配置是client的read-timeout = 10(客户端连接10秒后将自动断开,这时服务端再给客户端发消息就发布了了。报RemoteServerWriteFailed异常),可以将值设为0,将一直连着不断开。
actor的timeout默认为5秒,也太短了,延长(不能设为0,0为总是超时).
7.参考文档
2)akka简介 http://blog.chinaunix.net/uid-25885064-id-3400549.html
4)actors模型 http://janeky.iteye.com/blog/1504125
5)示例 http://www.th7.cn/Program/java/2012/03/29/67015.shtml
6)akka资料 http://www.jdon.com/tags/10531
分布式应用框架Akka快速入门的更多相关文章
- Nodejs ORM框架Sequelize快速入门
Nodejs ORM框架Sequelize快速入门 什么是ORM? 简单的讲就是对SQL查询语句的封装,让我们可以用OOP的方式操作数据库,优雅的生成安全.可维护的SQL代码.直观上,是一种Model ...
- Mybatis框架 的快速入门
MyBatis 简介 什么是 MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果 ...
- [jbdj]SpringMVC框架(1)快速入门
1)springmvc快速入门(传统版) 步一:创建springmvc_demo一个web应用 步二:导入springioc,springweb , springmvc相关的jar包 步三:在/WEB ...
- ssm框架整合快速入门
写在前面: 公司一直都是使用ssh框架(Struts2,Spring,Hibernate)来开发,但是现在外面的公司大多数都是使用的ssm框架,所以也有必要多学习一下外面的新技术.这里就快速搭建一个小 ...
- Hibernate入门第一讲——Hibernate框架的快速入门
Hibernate框架的概述 什么是框架? 框架指的是软件的半成品,已经完成了部分功能. JavaEE开发的三层架构 了解框架的基本概念之后,我们就来看看Hibernate框架处于JavaEE开发的经 ...
- Shiro安全框架【快速入门】就这一篇!
Shiro 简介 照例又去官网扒了扒介绍: Apache Shiro™ is a powerful and easy-to-use Java security framework that perfo ...
- Python 什么是flask框架?快速入门
一:Python flask框架 前言 1.Python 面向对象的高级编程语言,以其语法简单.免费开源.免编译扩展性高,同时也可以嵌入到C/C++程序和丰富的第三方库,Python运用到大数据分析. ...
- MyBatis 框架之快速入门程序
一.使用 IDEA 快速创建 Maven 项目 关于如何快速创建 Maven 项目,这个可以参考下面这篇文章: Maven 项目管理工具基础入门系列(一) 二.快速配置 MyBatis 依赖 jar ...
- 网络应用框架Netty快速入门
一 初遇Netty Netty是什么? Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.可扩展协议的服务器和客户端 ...
随机推荐
- Bolt 动画
引擎内置的 种动画 --PosChangeAnimation 平移 local ani = XLGetObject("Xunlei.UIEngine.AnimationFactory&quo ...
- *Linux之rpm命令
在Linux操作系统中,有一个系统软件包,它的功能类似于Windows里面的“添加/删除程序”,但是功能又比"添加/删除程序"强很多,它就是Red Hat Package Mana ...
- linux中proc文件系统 -- ldd3读书笔记
1./proc 文件系统概述 /proc 文件系统是由软件创建,被内核用来向外界报告信息的一个文件系统./proc 下面的每一个文件都和一个内核函数相关联,当文件的被读取时,与之对应的内核函数用于产生 ...
- Android L 使用ART能提高多少性能?
点击打开链接 刚刚结束的 Google I/O 大会上,Android 下一代操作系统「L」带来不少惊喜.新系统运行更快.更省电. 然而开发者对这个新系统也有颇多疑问,比如新的运行模式 ART 对开发 ...
- POJ2135 最小费用最大流模板题
练练最小费用最大流 此外此题也是一经典图论题 题意:找出两条从s到t的不同的路径,距离最短. 要注意:这里是无向边,要变成两条有向边 #include <cstdio> #include ...
- Test语言编译器V0.8
感觉这个挺好耍的,书上的代码有错误,而且功能有限. 一.词法分析 特点: (1)可对中文进行识别:(2)暂不支持负数,可以在读入‘-'时进行简单标记后就能对简单负数进行识别了. #include &l ...
- Android ArrayAdapter 详解
本文主要讲解ArrayAdapter的创建方法,我把ArrayAdapter分为三种:简单的.样式丰富的但内容简单的.内容丰富的. 默认的,ArrayAdapter期望接受的样式文件里只含有一个tex ...
- 设置Android默认锁定屏幕旋转
/********************************************************************************** * 设置Android默认锁定屏 ...
- datatables使用总结篇
<!doctype html> <html> <head> <meta charset="gbk"/> <meta name= ...
- 使用ffmpeg向crtmpserver发布rtsp流
ffmpeg的调用命令如下: ffmpeg -re -i xxx.mp4 -vcodec copy -acodec copy -f rtsp rtsp://127.0.0.1/live/mystre ...