文档章节

t-io入门篇(二)

卡尔码农
 卡尔码农
发布于 2017/05/08 09:15
字数 1442
阅读 2542
收藏 11

一、项目下载导入

  1. t-io的git地址是 https://git.oschina.net/tywo45/t-io 直接克隆一个到本地。
git clone https://git.oschina.net/tywo45/t-io.git

    2. 由于使用eclipse直接导入maven项目非常慢,我先使用本地maven命令生成t-io的eclipse项目,进入到项目目录中的 t-io\src\parent 目录执行直到所有项目都生成eclipse文件成功:

mvn eclipse:eclipse -DdownloadSources=true -X

   3. 使用eclipse导入maven项目即可。

二、helloworld server 

  • HelloServerStarter.java demo

      hello world的代码非常简介,其功能主要就是:

      启动一个服务端,服务端可以接收客户端发送的消息,并且向客户端回发一条消息。我们先来看看使用这个框架启动一个服务端有多么简单。

..
//创建消息handler,解/编码消息体
public static ServerAioHandler<Object, HelloPacket, Object> aioHandler = new HelloServerAioHandler();
//创建连接公用上下文
public static ServerGroupContext<Object, HelloPacket, Object> serverGroupContext = new ServerGroupContext<>(aioHandler, aioListener);
//创建aioserver对象
public static AioServer<Object, HelloPacket, Object> aioServer = new AioServer<>(serverGroupContext);

..

// 启动server
aioServer.start(serverIp, serverPort);

      没有错,就只需要初始化几个这样的参数即可启动socket server服务了 。

以下是server启动框架内部代码分析(自己不用管,我是学习一下作者的源代码):


AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(serverGroupContext.getGroupExecutor());
		serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);

..

AcceptCompletionHandler<SessionContext, P, R> acceptCompletionHandler = serverGroupContext.getAcceptCompletionHandler();
//接收到消息之后把消息交给acceptCompletionHandler来处理
serverSocketChannel.accept(this, acceptCompletionHandler);

     AcceptCompletionHandler.java中接收到消息之后,会把消息传递给ReadCompletionHandler<SessionContext, P, R> readCompletionHandler,readCompletionHandler会对消息进行解析:

public void completed(Integer result, ByteBuffer byteBuffer) {
		if (result > 0) {
			if (channelContext.isTraceClient()) {
				Map<String, Object> map = new HashMap<>();
				map.put("p_r_buf_len", result);
				channelContext.traceClient(ClientAction.RECEIVED_BUF, null, map);
			}
            //注意这里,DecodeRunnable会调用 aioHandler的decode方法
            // run方法中 channelContext.getGroupContext().getAioHandler().decode(byteBuffer, channelContext);
			DecodeRunnable<SessionContext, P, R> decodeRunnable = channelContext.getDecodeRunnable();
			readByteBuffer.flip();
			decodeRunnable.setNewByteBuffer(readByteBuffer);
			decodeRunnable.run();
		} else if (result == 0) {
			log.error("{}读到的数据长度为0", channelContext);
		} else if (result < 0) {
			Aio.close(channelContext, null, "读数据时返回" + result);
		}

		if (AioUtils.checkBeforeIO(channelContext)) {
			AsynchronousSocketChannel asynchronousSocketChannel = channelContext.getAsynchronousSocketChannel();
			readByteBuffer.position(0);
			readByteBuffer.limit(readByteBuffer.capacity());
			asynchronousSocketChannel.read(readByteBuffer, readByteBuffer, this);
		}

	}

DecodeRunnable 中会先执行自己的decode方法,然后调用handler的 handler 方法,也就是调用了HelloServerAioHandler的handler方法

public Object handler(HelloPacket packet, ChannelContext<Object, HelloPacket, Object> channelContext) throws Exception
	{
		byte[] body = packet.getBody();
		if (body != null)
		{
			String str = new String(body, HelloPacket.CHARSET);
			System.out.println("收到消息:" + str);

			HelloPacket resppacket = new HelloPacket();
			resppacket.setBody(("收到了你的消息,你的消息是:" + str).getBytes(HelloPacket.CHARSET));
			Aio.send(channelContext, resppacket);
		}
		return null;
	}
  • 代码细节

     因为之前没接触过这方面开发,看起来很吃力,有些吃力比如asynchronousSocketChannel.read 这种语句,不是很清楚工作原理,看似是类似filter里面的fillter.doChain这种链式工作流程。

但是看代码有些细节值得我注意:

1.ObjWithLock 对象附带读写锁封装,其实可以不封装,但是为了代码整洁方便做出了这个小的改进。

2.SystemTimer.currentTimeMillis()。哇,不得不说作者对性能要求极高。连jdk自带的System.currentTimeMills()自己也做了一点小优化,可能他的代码里面获取当前时间比较多了,为了提高性能,他自己写了一个单线程task,保证每10ms只会有一个线程去调用native方法,定时更新这个时间,各个线程每次不会调用native方法去区系统时间,而是直接从内存获取这个时间即可。如果是我自己写框架的话想想自己也不会考虑这么细,一般就直接System.currentTimeMills()了。

3.我有一点不太明白的是,作者经常出现即使这个类没有手动定义的父类时,在该类定义构造方法的时候都会写一个super()方法,也就是object的构造方法,但是实际上没什么作用吧,还是说编辑器自带生成,或者说是一个编码好习惯,防止未来出现什么疏漏?

 

==========================分割线=======================================

三、helloworld client

  • HelloClientStarter.java

     客户端代码看起来相当简洁 

..
// 客户端处理handler,发送消息之前会走super.encode 
public static ClientAioHandler<Object, HelloPacket, Object> aioClientHandler = new HelloClientAioHandler();
..
//断链后自动连接的,不想自动连接请设为null
private static ReconnConf<Object, HelloPacket, Object> reconnConf = new ReconnConf<Object, HelloPacket, Object>(5000L);

//一组连接共用的上下文对象
public static ClientGroupContext<Object, HelloPacket, Object> clientGroupContext = new ClientGroupContext<>(aioClientHandler, aioListener, reconnConf);
..
//创建client对象
aioClient = new AioClient<>(clientGroupContext);
//尝试连接服务端
clientChannelContext = aioClient.connect(serverNode);
..
//最后发送
Aio.send(clientChannelContext, packet);

  OK,上面就是客户端连接我上面的服务端的代码,看起来是不是非常的简洁?就连向服务端发送的方法都已经写成了静态方法了。

以下是客户端框架内部代码分析:

  • aioClient.connect方法解析

     helloworld采用的是默认同步连接服务端方式,其中采用了 CountDownLatch(闭锁)

//闭锁创建
CountDownLatch countDownLatch = new CountDownLatch(1);
attachment.setCountDownLatch(countDownLatch);
//连接服务端
asynchronousSocketChannel.connect(inetSocketAddress, attachment, clientGroupContext.getConnectionCompletionHandler());
countDownLatch.await(_timeout, TimeUnit.SECONDS);

   在ConnectionCompletionHandler中逻辑处理完毕在finally中调用的

attachment.getCountDownLatch().countDown()

  同样在Aio.send方法里面也同样使用了闭锁,同样SendRunnable中会调用handler的encode方法将packet进行编码后发送。

  • 心得:

1.闭锁和栅栏之前也有阅读过一些资料,但是一直没有应用到一些应用中来,譬如之前自己做的一个爬虫项目,同时使用多线程抓取多个网站多网页数据,我是通过设置信号量来判断线程是否已经抓取完毕然后执行数据清洗去重工作。其实使用CountDownLatch和CyclicBarrier来实现更简单

2.接口使用静态公用方法代码更加简洁。老版本的diamond在使用的时候要各种初始化,创建对象、spring ioc配置等等非常麻烦,后来也是有大神改了一版本,全部接口使用了静态方法去调用,代码简化了很多很多,不是乱糟糟的样子,让代码可读性增强。

© 著作权归作者所有

卡尔码农
粉丝 91
博文 15
码字总数 19284
作品 0
常德
其他
私信 提问
加载中

评论(5)

舒克呼叫贝塔
舒克呼叫贝塔
看到countDownLatch,特别去了解下,很强大实用的一个辅助类
精灵007
精灵007
写得很不错哈
卡尔码农
卡尔码农 博主

引用来自“小木学堂”的评论

最适合用在什么地方 至少举两个例子

当然,当我轻车熟路之后打算做一个棋牌类游戏后端数据交互服务器,负责客户端与客户端之间数据交互以及游戏逻辑算法校验。
卡尔码农
卡尔码农 博主

引用来自“小木学堂”的评论

最适合用在什么地方 至少举两个例子

回复@小木学堂 : 兄弟!这是一个网络编程高性能的框架,现在看作者官方描述有聊天、游戏等等都可以。我后面打算用这个做一些弹幕方面的东西肯定可以实现的、还有比如简单的configserver(服务器配置列表)
山东-小木
山东-小木
最适合用在什么地方 至少举两个例子
【Python Web学习路线】干货整理,不谈虚的,让你短时间高效学好它!

无论是大数据、人工智能还是机器学习,Python都是最热门的首选语言。 学好Python,可以从事Python Web工程师、Python数据分析、人工智能专家等岗位。本期专题,分享的主题是“如何成为一枚优...

Eddie_yang
2018/12/24
816
0
少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

本文引用了“架构师社区”公众号的《史上讲的最好的Java NIO与IO的区别与应用》一文部分内容,感谢原作者的技术分享。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典...

JackJiang2011
06/25
0
0
前端资源网站总汇

Web 前端入门与最佳实践 Web前端从入门菜鸟到实践老司机所需要的资料与指南合集 前端开发者手册 「前端知识框架」 -- 较全的前端知识(一) 「前端知识框架」 -- 较全的前端知识(二) 前端书...

treenewtreenew
2016/12/05
62
0
react学习资源汇总

react-tutorial a tutorial react collection and sort,let you easily get started and organized 主要是搜集整理生态从入门到深入的教程、工具和自己做的demo,以便日后查阅 :blush: 设计思...

逆蝶_Snow
2016/12/01
433
0
1. 彤哥说netty系列之开篇(有个问卷调查)

你好,我是彤哥,本篇是netty系列的第一篇。 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识。 简介 本文主要讲述netty系列的整体规划,并调查一下大家喜欢的学习方式。 知识点 nett...

彤哥读源码
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

Java 8 Stream Api 中的 skip 和 limit 操作

1. 前言 Java 8 Stream API 中的skip()和limit()方法具有类似的作用。它们都是对流进行裁剪的中间方法。今天我们来探讨一下这两个方法。 2. skip() skip(lang n) 是一个跳过前 n 个元素的...

码农小胖哥
35分钟前
1
0
看中华小当家第二集片段记录

最近正好在学习做饭,看看这片子,燃烧燃烧做饭的热情 哈哈,以下是摘的一些句子和个人理解 My name is Liu Mao Xing . More commonly known as Mao. 个人理解:我的名字叫 刘昴星, (commo...

T型人才追梦者
40分钟前
3
0
Python基础-6

1、注释 # 单行注释'''多行注释'''"""多行注释"""# 中文编码声明注释 ,在文件开头加上下面这行注释# encoding=utf-8或者# -*- encoding:utf-8 -*- (-*- 只是为了美观,没有实际......

zfskkk
40分钟前
5
0
我的这半年 / 2018 七夕著

浑然不觉,竟已至七夕, 今年已经过了一半 , 突然想给自己记录点什么 , 回顾下我的2018, 我的这上半年. 2018的第一件记事得从一月份下旬的那场大雪说起 , 一觉醒来 , 窗外整个银装素裹 , 在此之...

gxLong
44分钟前
3
0
NSS支持的数据库

NSS,简写自Name Service Switch。我把它译为“名字服务中心”。 在*nix操作系统中,NSS是C语言库(Library C或者glibc)的一部分,用以寻找名字。比如说,我们运行ls -lh查看一个目录中的文...

大别阿郎
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部