【开源项目学习】源码剖析,学习t-io代码

版权声明:尊重博主原创文章,转载请注明出处 https://blog.csdn.net/weixin_39020940/article/details/81843316

【前言】

这篇文字不全是讲t-io代码框架,而是博主怎么根据代码系统学习梳理的过程

t-io和之前的ukefu不太一样,ukefu是产品,所有有控制层/业务支撑层/数据层,t-io是网络框架

tio源码链接:https://gitee.com/tywo45/t-io

代码分析过程用的代码地图工具参考链接:

https://blog.csdn.net/weixin_39020940/article/details/80633835

【正文】

首先,我们知道,t-io对外呈现的是接口,依赖的是java的aio能力(不了解aio的自己补一课

对于产品,我的建议是从控制层入手,对于框架,我建议从接口Demo入手,先看看Demo和框架之间的串联

package org.tio.examples.helloworld.server;

import java.io.IOException;

import org.tio.examples.helloworld.common.Const;
import org.tio.server.TioServer;
import org.tio.server.ServerGroupContext;
import org.tio.server.intf.ServerAioHandler;
import org.tio.server.intf.ServerAioListener;

/**
 *
 * @author tanyaowu
 * 2017年4月4日 下午12:22:58
 */
public class HelloServerStarter {
	//handler, 包括编码、解码、消息处理
	public static ServerAioHandler aioHandler = new HelloServerAioHandler();

	//事件监听器,可以为null,但建议自己实现该接口,可以参考showcase了解些接口
	public static ServerAioListener aioListener = null;

	//一组连接共用的上下文对象
	public static ServerGroupContext serverGroupContext = new ServerGroupContext("hello-tio-server", aioHandler, aioListener);

	//tioServer对象
	public static TioServer tioServer = new TioServer(serverGroupContext);

	//有时候需要绑定ip,不需要则null
	public static String serverIp = null;

	//监听的端口
	public static int serverPort = Const.PORT;

	/**
	 * 启动程序入口
	 */
	public static void main(String[] args) throws IOException {
		serverGroupContext.setHeartbeatTimeout(org.tio.examples.helloworld.common.Const.TIMEOUT);

		tioServer.start(serverIp, serverPort);
	}
}

Demo代码结构大致如下:

从这个结构和Demo代码可以看出

1、使用t-io做网络开发确实是异常简单,只用几行代码就可以实现服务端

2、ServerAioListener\ServerAioHandler是框架给应用来实现的接口,用于框架对应用的回调

3、TioServer、ServerGroupContext是框架的提供的(在这里我们可能还不知道它具体的作用,但可以确认的是,这两个负责起来了应用和框架的串联)

一、首先自上而下的看,找到框架是怎么把应用进行了串联

如果框架要对应用进行回调控制,就需要持有ServerAioHandler实例,这里使用starTool导出t-io的代码地图,在Member Variable列过滤ServerAioHandler,结果如下图

后面几个Starter结尾的肯定和上面的demo一样是应用,而ServerGroupContext就是刚才demo使用的上下文这样,这里我们看一下ServerGroupContext后面的方法,确认一下提供了ServerAioHandler的set/get方法

而ServerAioHandler的get方法的调用点是ServerGroupContext的AioHandler的get方法(调用点需要看代码得出,毕竟这里以及锁定代码了),而ServerGroupContext的AioHandler的get方法的调用点是DecodeRunnable

				Integer packetNeededLength = channelContext.getPacketNeededLength();
				if (packetNeededLength != null) {
					log.info("{}, 解码所需长度:{}", channelContext, packetNeededLength);
					if (readableLength >= packetNeededLength) {
						packet = groupContext.getAioHandler().decode(byteBuffer, limit, initPosition, readableLength, channelContext);
					}
				} else {
					packet = groupContext.getAioHandler().decode(byteBuffer, limit, initPosition, readableLength, channelContext);
				}

这里,我们找到了ServerAioHandler的一个方法的调用点(框架->应用),但是ServerAioHandler方法不止这一个,为了避免过于深入代码,我们发散一下,继续使用代码地图,过滤后缀是Runnable,结果如下:

这样,对应用handler/Listener的几个回调点我们找到了(CloseRunable会回调AioListener,当然不一定是全部回调点),而且我们也找到了几个框架的自有线程

(这里注意下,使用代码地图时,同时也要观察类的成员类,这样才能有系统的理解)

到此,我们基于当前的结论做一下总结

1、框架的xxRunable会回调应用的handler和listener

2、GroupContext是连接应用和框架的上下文,框架会从GroupContext获取应用的handler和listener

3、框架底层很多是面向事务的,上层再衍生出Server和Client,如ServerAioHandler/ClientAioHandler,ServerGroupContext/ClientGroupContext

以及三个新任务:

1、ChannelContext是做什么的

2、xxRunable的运作机制和生命周期

3、TioServer的start做了哪些事情

二、ChannelContext\Runable\TioServer解读

首先从代码地图中搜索包含几个Runnable的

范围缩小后,我们可以撸代码了,稍作排查基本可以确认(只看有没有new这些xxRunnable的点就行了)

1、Tio控制CloseRunnable的生命周期,确切的来说是在Close的时候

2、ChannelContext控制DecodeRunnable,HandlerRunnable,SendRunnable,直接贴代码了

	public void setGroupContext(GroupContext groupContext) {
		this.groupContext = groupContext;

		if (groupContext != null) {
			decodeRunnable = new DecodeRunnable(this);
			//			closeRunnable = new CloseRunnable(this, null, null, groupContext.getCloseExecutor());

			//			handlerRunnableHighPrior = new HandlerRunnable(this, groupContext.getHandlerExecutorHighPrior());
			handlerRunnable = new HandlerRunnable(this, groupContext.getTioExecutor());

			//			sendRunnableHighPrior = new SendRunnable(this, groupContext.getSendExecutorHighPrior());
			sendRunnable = new SendRunnable(this, groupContext.getTioExecutor());

			groupContext.connections.add(this);
		}
	}

3、ReadCompletionHandler只有调用

4、SslListener,作者懒不想看了

Tio和ChannelContext都不是Demo里面显示创建的对象,而且从代码地图上,这时候我们看一眼Tio代码,很容易就可以看到原因,这是一个API类,所有的方法都是静态的,正如作者注释所说,The Class Tio. t-io用户关心的API几乎全在这

ChannelContext的生命周期管理者其实很好找了,之前如果看过一下GroupContext代码,用代码地图过滤也行,就可以找到,是由GroupContext创建管理的,如下:

	public final SetWithLock<ChannelContext> connections = new SetWithLock<ChannelContext>(new HashSet<ChannelContext>());
	public final SetWithLock<ChannelContext> connecteds = new SetWithLock<ChannelContext>(new HashSet<ChannelContext>());

	public final SetWithLock<ChannelContext> closeds = new SetWithLock<ChannelContext>(new HashSet<ChannelContext>());

不难看出,GroupContext管理了三组ChannelContext,分别是各种状态下的连接,结构如下

接下来我们看一下,TioServer在调用start中间,做了什么,基本应该就可以把框架相关流程串联起来了

	public void start(String serverIp, int serverPort) throws IOException {
		this.serverNode = new Node(serverIp, serverPort);
		//		ExecutorService groupExecutor = serverGroupContext.getGroupExecutor();

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

		serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
		serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);

		InetSocketAddress listenAddress = null;

		if (StringUtils.isBlank(serverIp)) {
			listenAddress = new InetSocketAddress(serverPort);
		} else {
			listenAddress = new InetSocketAddress(serverIp, serverPort);
		}

		serverSocketChannel.bind(listenAddress, 0);

		AcceptCompletionHandler acceptCompletionHandler = serverGroupContext.getAcceptCompletionHandler();
		serverSocketChannel.accept(this, acceptCompletionHandler);

		log.warn("{} started, listen on {}", serverGroupContext.getName(), this.serverNode);
	}

这里TioServer使用的Asynchronous开头的相关能力就是jdk.aio提供的了,代码核心流程如下

1、TioServer使用ServerGroupContext创建的线程池提供给aio的channel用于建连

2、绑定端口监听

3、设置报文接受完应用的回调函数,AcceptCompletionHandler::completed

4、AcceptCompletionHandler::completed里创建ServerChannelContext用于保存本次连接相关信息并刷新到ServerGroupContext下,并回调预留给APP的回调接口

至此,我们就完成一轮代码摸底,相信认真看完的已经对代码框架有了初步的理解

三、回过头梳理一下服务端侧APP和Tio共同完成的服务端业务主要流程

       作者在梳理这个流程的时候,看到了很多在代码地图里面上功能类被上下文context使用,类似于黑名单、鉴权、统计、跟踪,服务于这个呼叫流程。

四、横向纵向扩展理解

现在我们已经看了一个关键业务流程,并且知道了一些代码风格,我们可以根据这个风格做横向扩展

1、xxxHandler在框架内外的职责基本一致,就是对xx的处理

2、xxListener

3、xxRunable

4、xxContext

【总结】

1、代码地图需要增加对静态API类的识别(已经加入作者工期)

2、代码都是关联的,我们总能通过关联找到代码之间的联系

3、通过一个流程的横向纵向扩展学习,可以帮助我们了解整个系统

猜你喜欢

转载自blog.csdn.net/weixin_39020940/article/details/81843316