talent-aio源码阅读小记(一)
近来在oschina上看到一个很火的java 即时通讯项目talent-aio,恰巧想了解一下这方面的东西,就阅读了一下项目的源码,这里对自己阅读源码后的一些心得体会做一下备忘,也希望能够对其他项目中需要用到即时通讯功能的人有所帮助。
1 talent-aio是什么
talent-aio是基于java aio(JSR 203 )实现的即时通讯框架。对比与NIO,JSR 203 习惯上称为 NIO.2,主要包括新的异步io机制。在talent-aio中,server与client的实现主要使用了AsynchronousSocketChannel
以及AsynchronousSocketChannel
。作为一个简单的热身,我们先来一个小例子说明如何使用aio搭建一个简单的server。
server端
首先,创建一个channel group,之后server端accept等操作的回调就会在这个Channel group所拥有的线程池中执行。
final AsynchronousChannelGroup group
= AsynchronousChannelGroup.withFixedThreadPool(5, Executors.defaultThreadFactory());
然后将其与一个AsynchronousSocketChannel
与上面的AsynchronousChannelGroup关联起来:
final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);
将这个listener bind到指定端口上:
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 8888);
listener.bind(hostAddress);
之后就可以通过回调accept客户端发来的连接,而后读取客户端发来的数据了,记得在调用处理方法后后继续调用listen的accept方法来接收新的客户端请求。
final String att1 = "First connection";
listener.accept(att1, new CompletionHandler() {
@Override
public void completed(AsynchronousSocketChannel ch, Object att) {
System.out.println("Completed: " + att);
String msg = handleConnection(ch);
att = "next completed";
listener.accept(att, this);
}
@Override
public void failed(Throwable e, Object att) {
System.out.println(att + " - handler failed");
e.printStackTrace();
currentThread.interrupt();
}
});
其中handleConnection的代码为,在其中调用read,在read回调中打印出客户端发来的数据
private String handleConnection(AsynchronousSocketChannel ch) {
ByteBuffer buffer = ByteBuffer.allocate(32);
ch.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result > 0) {
attachment.flip();
String msg = new String(attachment.array()).trim();
System.out.println("Message from client: " + msg);
attachment.clear();
if (msg.equals("close")) {
if (!group.isTerminated()) {
System.out.println("Terminating the group...");
try {
group.shutdownNow();
group.awaitTermination(10, TimeUnit.SECONDS);
} catch (IOException | InterruptedException e) {
System.out.println("Exception during group termination");
e.printStackTrace();
}
}
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println(" - handler failed");
exc.printStackTrace();
currentThread.interupt();
}
});
}
客户端
客户端使用不带AsynchronousChannelGroup
参数的open,则使用系统默认的AsynchronousChannelGroup
。客户端没有使用回调的方式,而是使用future,限制通过get阻塞到连接建立完成,而后向服务器发送close,并轮询write返回的future查看数据是否发送完毕,最后关闭连接。
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 3883);
Future future = client.connect(hostAddress);
future.get(); // returns null
System.out.println("Client is started");
System.out.println("Sending message to server: ");
byte [] bytes = new String("close").getBytes();
ByteBuffer buffer = ByteBuffer.wrap(bytes);
Future result = client.write(buffer);
while (! result.isDone()) {
System.out.println("... ");
}
System.out.println(new String(buffer.array()).trim());
buffer.clear();
client.close();
可以看出,在不使用Future的情况下,aio的主要处理逻辑在accept
,read
,write
,close
等回调函数中。
2 talent-aio server端的处理流程:
通过上面的例子,相信大家已经对aio有了一个大概的了解,下面我们就来看看talent-aio server端是怎么处理客户端连接的。对于客户端,read、write、close等的处理方式与server端相似。
2.1 server处理流程
首先,accept客户端连接:
然后,读取并处理客户端数据:
2.2 处理流程相关的重要的接口与类说明
了解了大概的流程后,就需要深入细节来了解talent-aio的工作方式了。talent-aio的数据解码、包处理、数据发送、连接关闭分别在DecodeRunnable
、HandlerRunnable
、SendRunnable
以及CloseRunnable
几类Task中完成。这些Task都继承了抽象类AbstractQueueRunnable
,该抽象类继承了AbstractSynRunnable
,而AbstractSynRunnable
实现了接口SynRunnableIntf
:
public interface SynRunnableIntf extends Runnable
{
public ReadWriteLock runningLock();
public boolean isNeededExecute();
public boolean isCanceled();
public void setCanceled(boolean isCanceled);
/**
* Run task.
*/
public void runTask();
}
AbstractSynRunnable
在该接口基础上添加了方法:
/**
* @return the executor
*/
public Executor getExecutor()
{
return executor;
}
/**
* @param executor the executor to set
*/
public void setExecutor(Executor executor)
{
this.executor = executor;
}
并且主要实现了Runnable
的run
方法:
@Override
public final void run()
{
if (isCanceled()) //任务已经被取消
{
return;
}
ReadWriteLock runningLock = runningLock();
Lock writeLock = runningLock.writeLock();
boolean trylock = writeLock.tryLock();
if (!trylock)
{
return;
}
try
{
runTask();
} catch (Exception e)
{
log.error(e.toString(), e);
} finally
{
writeLock.unlock();
if (isNeededExecute())
{
getExecutor().execute(this);
}
}
}
在run
中,会尝试获取runningLock,如果获取失败,说明该runneable已经在执行了,可以立即退出。否则就运行runTask
,最终根据是否需要继续执行决定要不要再次将该runnable提交到执行线程池中(比如处理完一个packet,发现该连接还有待处理的packet则需要继续处理)。
AbstractQueueRunnable
还实现了QueueRunnableIntf<T>
:
public interface QueueRunnableIntf<T>
{
/**
* 获取数据队列.
*
* @return 保存着要处理的数据的队列
*/
ConcurrentLinkedQueue<T> getMsgQueue();
}
并且实现了方法isNeededExecute
,通过检查数据队列中是否还有待处理数据来判断是否要继续提交该runnable到executor:
public boolean isNeededExecute()
{
return getMsgQueue().size() > 0;
}
这样,只要向msgQueue中添加一个任务,就可以在runTask方法中获取该任务,并且进行相应的处理了。对于解码,发送,处理,关闭的详细分析请关注本系列的第二篇文章。
转:http://www.jianshu.com/p/522446599d39
talent-aio源码阅读小记(一)的更多相关文章
- talent-aio源码阅读小记(二)
我们上一篇提到了talent-aio的四类Task:DecodeRunnable.HandlerRunnable.SendRunnable.CloseRunnable,并且分析了这些task的基类Ab ...
- avalon源码阅读(1)
来源 写angularJS源码阅读系列的时候,写的太垃圾了. 一个月后看,真心不忍直视,以后有机会的话得重写. 这次写avalonJS,希望能在代码架构层面多些一点,少上源码.多写思路. avalon ...
- koa源码阅读[2]-koa-router
koa源码阅读[2]-koa-router 第三篇,有关koa生态中比较重要的一个中间件:koa-router 第一篇:koa源码阅读-0第二篇:koa源码阅读-1-koa与koa-compose k ...
- koa源码阅读[0]
koa源码阅读[0] Node.js也是写了两三年的时间了,刚开始学习Node的时候,hello world就是创建一个HttpServer,后来在工作中也是经历过Express.Koa1.x.Koa ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
- 【原】AFNetworking源码阅读(六)
[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...
- 【原】AFNetworking源码阅读(五)
[原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...
随机推荐
- php支付走过的坑(支付宝篇 注册 秘钥 环境等等配置)
支付这东西,说容易也容易,说难也难 代码这玩意还比较好说 但是 如果没有demo 直接去看官方文档 十有八九一脸懵逼 今天就整理一下 支付这块走过的坑 涉及 微信h5支付 支付宝h5支付 (api文档 ...
- SQL SERVER 错误代码 0x534
解决办法就是修改一下登陆名: ALTER LOGIN [G-PC\zqwang] WITH NAME=[新的机器名\zqwang]; 然后查询一下 Service Broker 队列, 里面已经有 ...
- .net 控制器调用外部链接传参方法
public class RequestHelper { /// <summary> /// 发起post请求 /// </summary> /// <typeparam ...
- NFS笔记(一)NFS服务器工作原理及详细配置
一.NFS工作原理 1.什么是NFS服务器 NFS就是Network File System的缩写,它最大的功能就是可以通过网络,让不同的机器.不同的操作系统可以共享彼此的文件. NFS服务器可以让P ...
- Python元组、列表、字典、集合
1. 元组 元组由不同元素组成,每个元素可以存储不同类型的数据,元组是有序的,元组创建后不能再做任何修改. 元组的创建: tuple = ('a','b','c','d') 如果创建的元组只有1个元素 ...
- 批处理实现虚拟WIFI
类似于360免费WiFi那个软件,可以PC发射WiFi(需要有无线网卡). 源码如下: @Echo off title Windows7 虚拟Wifi----By yllinux mode con c ...
- DOM(十四):代理检测和事件处理(跨浏览器)
一.检测 用于用户代理检测,检测范围包括浏览器引擎.平台.Windows.移动设备和游戏系统等 /* *用户代理检测脚本,检测范围包括浏览器引擎.平台.Windows.移动设备和游戏系统 */ var ...
- APP专项测试使用到的工具
最近在读<大话APP测试>,我也就是把需要使用的测试点做一个总结,目前是使用的工具进行的整理,后期慢慢把工具使用案例贴出来
- Uva 11078 简单dp
题目链接:http://uva.onlinejudge.org/external/110/11078.pdf a[i] - a[j] 的最大值. 这个题目马毅问了我,O(n^2)超时,记忆化一下当前最 ...
- veritas.com常用资源汇总
NetBackup 8.1.2文档(合集) https://www.veritas.com/support/en_US/article.100044086 NetBackup产品组停止支持生命周期 ...