一、IO模型(五种IO模型)
二、数据协议(http/protobuf/Thrift)
三、线程模型(主从线程组模型)
EventLoop好比一个线程,1个EventLoop可以服务多个Channel,一个Channel只有一个EventLoop,也就是说一个线程负责多个连接通道,可以创建多个EventLoop来优化资源的利用,也就是EventLoopGroup。源码分析默认线程池数量=cpu核心数*2
EventLoopGroup负责生成EventLoop到创建新的Channel,里面包含多个EventLoop。
EventLoop维护一个Reactor模型(I/O模型),这个I/O模型可以负责多个连接,即:Channel通道,因此I/O模型的好坏也影响系统的效率。
Selector学习
单线程模型:
一、如果此时是主从线程组,那就去掉从线程组,然后在group中去掉从线程组的对象。让serverBootstrap.group(boosgroup)只包含一个线程组。
EventLoopGroup bossGroup = new NioEventLoopGroup();
//EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
//serverBootstrap.group(bossGroup, workGroup)
serverBootstrap.group(bossGroup)
多线程模型:
一、多线程模型就是主线程组只有一个线程,从线程组有多个线程。主线程就好比主线程中的一个接待员,然后从线程组就好比服务人员。
//配置服务端线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
主从线程模型
设置channel通道类型NioServerSocketChannel,OioServerSocketChannel
用于对每个通道里面的数据进行处理。
作用于每个新建立的Channel,设置TCP连接中的一些参数,主要设置两个参数:
一、设置ChannelOption.SO_BACKLOG:用于存放已完成三次握手的请求的等待队列的最大长度;
syn queue:半连接队列,tcp_max_syn_backlog
accept queue:全连接队列,net.core.somexconn
系统默认somexconn参数足够大,如果backlog比somexconn大,系统就会使用somexconn,否则会施一公backlog
二、设置ChannelOption.TCP_NODELAY:为了解决Nagle的算法问题,默认是false,要求高实时性,有数据立马发送,就将该选项设置为true,关闭Nagle算法。要求减少发送次数,就要使用Nagle算法,就要设置为false,这样数据就会积累一定的数据量才会发送。
三、Nagle算法:就是为了让数据积累一定的量再发送,减少发送数据次数。
总结:
一个Channel包含一个ChannelPipeline,所有ChannelHandler都会顺序加入到ChannelPipeline中,创建Channel时会自动创建一个ChannelPipeline,每个Channel都有一个管理它的pipeLine,这种关联是永久性的。
handlerAdded():当ChannelHandler添加到ChannelPipeline时,就会回调handlerAdded
handlerRemoved():当ChannelHandler从ChannelPipeline移除时,就会回调handlerRemoved()
exceptionCaught():当抛出异常时,会调用此方法。
入站:
ChannelInboundHandler():用于处理数据和Channel状态类型改变。
适配器:ChannelInboundHandlerAdapter(适配器模式)
常用的类:SimpleChannelInboundHandler,这个会写的时候主动帮我们释放了内存。做了一些简单的包装。
出站:
ChannelOutboundHandler():处理输出数据
适配器:ChannelOutboundHandlerAdapter
好比工厂流水线,可以添加ChannelHandler,可以删除。可以指定跳过某个ChannelHandler。就是一种高级的拦截器。
假如有一个接口有很多未曾实现的方法,如果我们用一个类去继承它,我们会发现这个类不得不去实现它的所有方法。然而有些方法我们可能永远也不会用到,这样就会让代码变得更加冗余复杂。
为了减少这种代码的冗余,我们创建了一个类对该方法进行了实现,但是并没有做什么处理。当我们开发时我们只需要继承这个实现的方法就可以了,那么我们此时就可以再实现我们想要的方法了。而不会报错。
一、通过Channel写回去
Channel channel = ctx.channel();
channel.write(Unpooled.copiedBuffer("abc",CharsetUtil.UTF_8));
二、通过pipeline写回
ChannelPipeline pipeline = ctx.pipeline();
pipeline.writeAndFlush(Unpooled.copiedBuffer("abc",CharsetUtil.UTF_8));
三、通过ChannelHandlerContext写回
ctx.writeAndFlush(data);
一、用Channel写回的时候,会经过所有的ChannelHandler的过滤。
二、用ChannelHandlerContext写回的时候,会经过之后的ChannelHandler。跳过一些ChannelHandler.
三、用ChannelPipeline和Channel一样会经过管道里的所有ChannelHandler.
一、InboundHandler顺序执行,OutboundHandler逆序执行。(在pipeline中)
二、InboundHandler之间数据传递,通过ctx.fireChannelRead(msg)
三、InboundHandler通过ctx.write(msg),传递到outboundHandler
四、ctx.write(msg)传递消息,Inbound需要放在结尾,在outbound之后,不然outboundHandler不会执行,但是,使用Channel.write(msg),或者pipline.write(msg),情况会不一样,都会执行。
五、outbound和Inbound谁先执行,针对客户端和服务端而言,客户端先发请求后接收数据,先out,后in,服务端相反。
客户端是先输出,再输入服务器,服务端是先输入数据,然后再写回硬件,在服务器中,输出out必须写在int输入的前面,并且输出是逆序执行的。
jdk1.5之前是没有fulture的,首先要先从线程说起,开启一个线程需要实现Runnable或者继承Thread类,但是这两者有个问题,就是没有任何返回值,是void方法类型。因为线程是异步的,主进程交给线程处理任务时,很难获取线程返回的结果,但是有些实际要求确实需要一个返回值。因此Fulture就出现了。fulture的作用就是检测线程是否执行的状态,包含未执行完,和执行完。并且可以将线程计算的结果返回给主进程。fulture可以来接收多线程的执行结果。
Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时 间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。
//绑定端口,同步等待成功,通过channelFulture监听端口里面传过来的值。serverBootstrap,里面含有线程组,监听端口后,进行同步的等待。一旦有值,就会被channelfulture监听到
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
//等待服务端监听端口关闭,调用channel通道,然后同步监听closeFulture,一旦出现关闭就同步的进行关闭。
channelFuture.channel().closeFuture().sync();