架构设计:系统间通信(7)

接上文《架构设计:系统间通信(6)——IO通信模型和Netty 上篇》

5、再次审视为什么使用Netty

上篇文章我们讨论了Netty的基本原理,重要概念,并使用java代码描述了Netty的基本使用。当然Netty的技术涵盖点远远不是那一篇基础代码就可以全部概括的,但是至少可以给读者一个切入点。让大家去思考一个我们一直在讨论的问题:为什么有了JAVA NIO框架后我们还需要有Netty这样的框架对底层再次进行封装?

5-1、IO模型的封装5-1-1、再次总结IO模型

在前文中我们已经提到了,几种典型的IO模型(参见系统间通信3、4、5这三篇文章中的介绍,这里再进行一次总结):

5-1-2、对IO模型的再次封装

以上这些IO工作模型,在JAVA中都能够找到对应的支持:传统的JAVA Socket套接字支持阻塞/非阻塞模式下的同步IO(有的技术资料里面也称为OIO或者BIO);JAVA NIO框架在不同操作系统下支持不同种类的多路复用IO技术(windows下的select模型、Linux下的poll/epoll模型);JAVA AIO框架支持异步IO(windows下的IOCP和Linux使用epoll的模拟AIO)

实际上Netty是对JAVA BIO 、JAVA NIO框架的再次封装。让我们不再纠结于选用哪种底层实现。您可以理解成Netty/MINA 框架是一个面向上层业务实现进行封装的“业务层”框架。而JAVA Socket框架、JAVA NIO框架、JAVA AIO框架更偏向于对下层技术实现的封装,是面向“技术层”的框架。

5-2、数据信息格式的封装

“技术层”框架本身只对IO模型技术实现进行了封装,并不关心IO模型中流淌的数据格式;“业务层”框架对数据格式也进行了处理,让我们可以抽出精力关注业务本身。

下面是一个使用Netty的Http编码/解码处理器,设计的一个简单的WEB服务器:

package testNetty;import javaimport javaimport javaimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport ioimport orgimport orgimport orgpublic class TestHTTPNetty {static {BasicConfigurator.configure();}public static void main(String[] args) throws Exception {//这就是主要的服务启动器ServerBootstrap serverBootstrap = new ServerBootstrap();//=======================下面我们设置线程池(代码已经详细讲解过,就不再赘述了)EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);ThreadFactory threadFactory = new DefaultThreadFactory(“work thread pool”);int processorsNumber = Runtime.getRuntime().availableProcessors();EventLoopGroup workLoogGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory, SelectorProvider.provider());serverBootstrap.group(bossLoopGroup , workLoogGroup);//========================下面我们设置我们服务的通道类型(代码已经详细讲解过,就不再赘述了)serverBootstrap.channel(NioServerSocketChannel.class);//========================设置处理器serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {/* (non-Javadoc)* @see io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.Channel)*/@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {//我们在socket channel pipeline中加入http的编码和解码器ch.pipeline().addLast(new HttpResponseEncoder());ch.pipeline().addLast(new HttpRequestDecoder());ch.pipeline().addLast(new HTTPServerHandler());}});serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);serverBootstrap.bind(new InetSocketAddress(“0.0.0.0”, 83));}}/** * @author yinwenjie */@Sharableclass HTTPServerHandler extends ChannelInboundHandlerAdapter {/*** 日志*/private static Log LOGGER = LogFactory.getLog(HTTPServerHandler.class);/*** 由于一次httpcontent可能没有传输完全部的请求信息。所以这里要做一个连续的记录* 然后在channelReadComplete方法中(执行了这个方法说明这次所有的http内容都传输完了)进行处理*/private static AttributeKey<StringBuffer> CONNTENT = AttributeKey.valueOf(“content”);/* (non-Javadoc)* @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {/** 在测试中,,我们首先取出客户端传来的参数、URL信息,并且返回给一个确认信息。* 要使用HTTP服务,我们首先要了解Netty中http的格式,如下:* ———————————————-* | http request | http content | http content |* ———————————————-** 所以通过HttpRequestDecoder channel handler解码后的msg可能是两种类型:* HttpRquest:里面包含了请求head、请求的url等信息* HttpContent:请求的主体内容* */if(msg instanceof HttpRequest) {HttpRequest request = (HttpRequest)msg;HttpMethod method = request.getMethod();String methodName = method.name();String url = request.getUri();HTTPServerHandler.LOGGER.info(“methodName = ” + methodName + ” && url = ” + url);}//如果条件成立,则在这个代码段实现http请求内容的累加if(msg instanceof HttpContent) {StringBuffer content = ctx.attr(HTTPServerHandler.CONNTENT).get();if(content == null) {content = new StringBuffer();ctx.attr(HTTPServerHandler.CONNTENT).set(content);}HttpContent httpContent = (HttpContent)msg;ByteBuf contentBuf = httpContent.content();String preContent = contentBuf_8);content.append(preContent);}}/* (non-Javadoc)* @see io.netty.channel.ChannelInboundHandlerAdapter#channelReadComplete(io.netty.channel.ChannelHandlerContext)*/@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {HTTPServerHandler.LOGGER.info(“super.channelReadComplete(ChannelHandlerContext ctx)”);/** 一旦本次http请求传输完成,则可以进行业务处理了。* 并且返回响应* */StringBuffer content = ctx.attr(HTTPServerHandler.CONNTENT).get();HTTPServerHandler.LOGGER.info(“http客户端传来的信息为:” + content);//开始返回信息了String returnValue = “return response”;FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);HttpHeaders httpHeaders = response.headers();//这些就是http response 的head信息咯,参见http规范。另外您还可以设置自己的head属性httpHeaders.add(“param”, “value”);response.headers().set(HttpHeaders.Names.CONTENT_TYPE, “text/plain”);//一定要设置长度,否则http客户端会一直等待(因为返回的信息长度客户端不知道)response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, returnValue.length());ByteBuf responseContent = response.content();responseContent.writeBytes(returnValue.getBytes(“UTF-8”));//开始返回ctx.writeAndFlush(response);} }

由于上篇文章中已经介绍了Netty的基本使用方法,所以以上的代码将其他不必要的注释、方法都去掉了,只做了实现web服务器的最简代码。但是这段代码本省是可以运行的。下面是运行效果:

5-3、解决了“技术层”框架中的技术问题坚韧是成功的一大要素,只要在门上敲得够久够大声,终会把人唤醒的。

架构设计:系统间通信(7)

相关文章:

你感兴趣的文章:

标签云: