转载自http://blog.csdn.net/kobejayandy/article/details/11493717

先啰嗦两句,如果你还不知道Netty是做什么的能做什么。那可以先简单的搜索了解一下。我只能说Netty是一个NIO的框架,可以用于开发分布式的Java程序。具体能做什么,各位可以尽量发挥想象。技术,是服务于人而不是局限住人的。

如果你已经万事具备,那么我们先从一段代码开始。程序员们习惯的上手第一步,自然是"Hello world",不过Netty官网的例子却偏偏抛弃了"Hello world"。那我们就自己写一个最简单的"Hello world"的例子,作为上手。

  1. /**
  2. * Netty 服务端代码
  3. *
  4. * @author lihzh
  5. * @alia OneCoder
  6. * @blog http://www.coderli.com
  7. */
  8. public class HelloServer {
  9. public static void main(String args[]) {
  10. // Server服务启动器
  11. ServerBootstrap bootstrap = new ServerBootstrap(
  12. new NioServerSocketChannelFactory(
  13. Executors.newCachedThreadPool(),
  14. Executors.newCachedThreadPool()));
  15. // 设置一个处理客户端消息和各种消息事件的类(Handler)
  16. bootstrap
  17. .setPipelineFactory(new ChannelPipelineFactory() {
  18. @Override
  19. public ChannelPipeline getPipeline()
  20. throws Exception {
  21. return Channels
  22. .pipeline(new HelloServerHandler());
  23. }
  24. });
  25. // 开放8000端口供客户端访问。
  26. bootstrap.bind(new InetSocketAddress(8000));
  27. }
  28. private static class HelloServerHandler extends
  29. SimpleChannelHandler {
  30. /**
  31. * 当有客户端绑定到服务端的时候触发,打印"Hello world, I'm server."
  32. *
  33. * @alia OneCoder
  34. * @author lihzh
  35. */
  36. @Override
  37. public void channelConnected(
  38. ChannelHandlerContext ctx,
  39. ChannelStateEvent e) {
  40. System.out.println("Hello world, I'm server.");
  41. }
  42. }
  43. }
  1. /**
  2. * Netty 客户端代码
  3. *
  4. * @author lihzh
  5. * @alia OneCoder
  6. * @blog http://www.coderli.com
  7. */
  8. public class HelloClient {
  9. public static void main(String args[]) {
  10. // Client服务启动器
  11. ClientBootstrap bootstrap = new ClientBootstrap(
  12. new NioClientSocketChannelFactory(
  13. Executors.newCachedThreadPool(),
  14. Executors.newCachedThreadPool()));
  15. // 设置一个处理服务端消息和各种消息事件的类(Handler)
  16. bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
  17. @Override
  18. public ChannelPipeline getPipeline() throws Exception {
  19. return Channels.pipeline(new HelloClientHandler());
  20. }
  21. });
  22. // 连接到本地的8000端口的服务端
  23. bootstrap.connect(new InetSocketAddress(
  24. "127.0.0.1", 8000));
  25. }
  26. private static class HelloClientHandler extends SimpleChannelHandler {
  27. /**
  28. * 当绑定到服务端的时候触发,打印"Hello world, I'm client."
  29. *
  30. * @alia OneCoder
  31. * @author lihzh
  32. */
  33. @Override
  34. public void channelConnected(ChannelHandlerContext ctx,
  35. ChannelStateEvent e) {
  36. System.out.println("Hello world, I'm client.");
  37. }
  38. }
  39. }

既然是分布式的,自然要分多个服务。Netty中,需要区分Server和Client服务。所有的Client都是绑定在Server上的,他们之间是不能通过Netty直接通信的。(自己采用的其他手段,不包括在内。)。白话一下这个通信过程,Server端开放端口,供Client连接,Client发起请求,连接到Server指定的端口,完成绑定。随后便可自由通信。其实就是普通Socket连接通信的过程。

Netty框架是基于事件机制的,简单说,就是发生什么事,就找相关处理方法。就跟着火了找119,抢劫了找110一个道理。所以,这里,我们处理的是当客户端和服务端完成连接以后的这个事件。什么时候完成的连接,Netty知道,他告诉我了,我就负责处理。这就是框架的作用。Netty,提供的事件还有很多,以后会慢慢的接触和介绍。

你应该已经可以上手了:)

"Hello World"的代码固然简单,不过其中的几个重要概念(类)和 Netty的工作原理还是需要简单明确一下,至少知道其是负责什。方便自己以后更灵活的使用和扩展。

 
声明,笔者一介码农,不会那么多专业的词汇和缩写,只能以最简单苍白的话来形容个人的感受和体会。如果您觉得这太不专业,笔者首先只能抱歉。然后,笔者曾转过《Netty代码分析》,您可参考。
  • ChannelEvent
先说这个ChannelEvent,因为Netty是基于事件驱动的,就是我们上文提到的,发生什么事,就通知"有关部门"。所以,不难理解,我们自己的业务代码中,一定有跟这些事件相关的处理。在样例代码,我们处理的事件,就是channelConnected。以后,我们还会处理更多的事件。
 
  • ChannelPipeline
Pipeline,翻译成中文的意思是:管道,传输途径。也就是说,在这里他是控制ChannelEvent事件分发和传递的。事件在管道中流转,第一站到哪,第二站到哪,到哪是终点,就是用这个ChannelPipeline 处理的。比如:开发事件。先给A设计,然后给B开发。一个流转图,希望能给你更直观的感觉。
  • ChannelHandler
刚说Pipeline负责把事件分发到相应的站点,那个这个站点在Netty里,就是指ChannelHandler。事件到了ChannelHandler这里,就要被具体的进行处理了,我们的样例代码里,实现的就是这样一个处理事件的“站点”,也就是说,你自己的业务逻辑一般都是从这里开始的。

  • Channel
有了个部门的协调处理,我们还需要一个从整体把握形势的,所谓“大局观”的部门,channel。
channel,能够告诉你当前通道的状态,是连同还是关闭。获取通道相关的配置信息。得到Pipeline等。是一些全局的信息。Channel自然是由ChannelFactory产生的。Channel的实现类型,决定了你这个通道是同步的还是异步的(nio)。例如,我们样例里用的是NioServerSocketChannel。

这些基本的概念,你懂了吧。

说了这么多废话,才提到对象的传输,不知道您是不是已经不耐烦了。一个系统内部的消息传递,没有对象传递是不太现实的。下面就来说说,怎么传递对象。

如果,您看过前面的介绍,如果您善于专注本质,勤于思考。您应该也会想到,我们说过,Netty的消息传递都是基于流,通过ChannelBuffer传递的,那么自然,Object也需要转换成ChannelBuffer来传递。好在Netty本身已经给我们写好了这样的转换工具。 ObjectEncoder和ObjectDecoder。

工具怎么用?再一次说说所谓的本质,我们之前也说过,Netty给我们处理自己业务的空间是在灵活的可子定义的Handler上的,也就是说,如果我们自己去做这个转换工作,那么也应该在Handler里去做。而Netty,提供给我们的ObjectEncoder和Decoder也恰恰是一组 Handler。于是,修改Server和Client的启动代码:

 
 
服务端
01
02
03
04
05
06
07
08
09
10
// 设置一个处理客户端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(newChannelPipelineFactory() {
    @Override
    publicChannelPipeline getPipeline()throwsException {
        returnChannels.pipeline(
        newObjectDecoder(ClassResolvers.cacheDisabled(this
                .getClass().getClassLoader())),
        newObjectServerHandler());
    }
});
客户端
1
2
3
4
5
6
7
8
// 设置一个处理服务端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(newChannelPipelineFactory() {
    @Override
    publicChannelPipeline getPipeline()throwsException {
        returnChannels.pipeline(newObjectEncoder(),
                newObjectClientHandler());
    }
});

要传递对象,自然要有一个被传递模型,一个简单的Pojo,当然,实现序列化接口是必须的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * @author lihzh
 * @alia OneCoder
 */
public class Command implementsSerializable {
 
    privatestaticfinal long serialVersionUID = 7590999461767050471L;
 
    privateString actionName;
 
    publicString getActionName() {
        returnactionName;
    }
 
    publicvoidsetActionName(String actionName) {
        this.actionName = actionName;
    }
}

服务端和客户端里,我们自定义的Handler实现如下:

 
ObjectServerHandler .java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 对象传递服务端代码
 *
 * @author lihzh
 * @alia OneCoder
 */
public class ObjectServerHandler extendsSimpleChannelHandler {
 
    /**
     * 当接受到消息的时候触发
     */
    @Override
    publicvoidmessageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throwsException {
        Command command = (Command) e.getMessage();
        // 打印看看是不是我们刚才传过来的那个
        System.out.println(command.getActionName());
    }
}
ObjectClientHandler .java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * 对象传递,客户端代码
 *
 * @author lihzh
 * @alia OneCoder
 */
public class ObjectClientHandler extendsSimpleChannelHandler {
 
    /**
     * 当绑定到服务端的时候触发,给服务端发消息。
     *
     * @author lihzh
     * @alia OneCoder
     */
    @Override
    publicvoidchannelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
        // 向服务端发送Object信息
        sendObject(e.getChannel());
    }
 
    /**
     * 发送Object
     *
     * @param channel
     * @author lihzh
     * @alia OneCoder
     */
    privatevoidsendObject(Channel channel) {
        Command command =newCommand();
        command.setActionName("Hello action.");
        channel.write(command);
    }
 
}

启动后,服务端正常打印结果:Hello action.

简单梳理一下思路:

通过Netty传递,都需要基于流,以ChannelBuffer的形式传递。所以,Object -> ChannelBuffer.
Netty提供了转换工具,需要我们配置到Handler。
样例从客户端 -> 服务端,单向发消息,所以在客户端配置了编码,服务端解码。如果双向收发,则需要全部配置Encoder和Decoder。
这里需要注意,注册到Server的Handler是有顺序的,如果你颠倒一下注册顺序:

1
2
3
4
5
6
7
8
9
bootstrap.setPipelineFactory(newChannelPipelineFactory() {
    @Override
    publicChannelPipeline getPipeline()throwsException {
        returnChannels.pipeline(newObjectServerHandler(),
                newObjectDecoder(ClassResolvers.cacheDisabled(this
                        .getClass().getClassLoader()))
                );
    }
});

结果就是,会先进入我们自己的业务,再进行解码。这自然是不行的,会强转失败。至此,你应该会用Netty传递对象了吧。

有一段事件没有更新文章了,各种原因都有吧。搬家的琐事,搬家后的安逸呵呵。不过,OneCoder明白,绝不能放松。对于Netty的学习,也该稍微深入一点了。

所以,这次OneCoder花了几天时间,仔细梳理了一下Netty的源码,总结了一下ServerBootStrap的启动和任务处理流程,基本涵盖了Netty的关键架构。
 
OneCoder总结了一张流程图:
 
 
该图是OneCoder通过阅读Netty源码,逐渐记录下来的。基本可以说明Netty服务的启动流程。这里在具体讲解一下。
 
首先说明,我们这次顺利的流程是基于NioSocketServer的。也就是基于Java NIO Selector的实现方式。在第六讲《Java NIO框架Netty教程(六)-Java NIO Selector模式》中,我们已经知道使用Selector的几个关键点,所以这里我们也重点关注一下,这些点在Netty中是如何使用的。
 
很多看过Netty源码的人都说Netty源码写的很漂亮。可漂亮在哪呢?Netty通过一个ChannelFactory决定了你当前使用的协议类型 (Nio/oio等),比如,OneCoder这里使用的是NioServerSocket,那么需要声明的Factory即为 NioServerSocketChannelFactory,声明了这个Factory,就决定了你使用的Channel,pipeline以及 pipeline中,具体处理业务的sink的类型。这种使用方式十分简洁的,学习曲线很低,切换实现十分容易。
 
Netty采用的是基于事件的管道式架构设计,事件(Event)在管道(Pipeline)中流转,交由(通过pipelinesink)相应的处理器(Handler)。这些关键元素类型的匹配都是由开始声明的ChannelFactory决定的。
 
Netty框架内部的业务也遵循这个流程,Server端绑定端口的binder其实也是一个Handler,在构造完Binder后,便要声明一个 Pipeline管道,并赋给新建一个Channel。Netty在newChannel的过程中,相应调用了Java中的 ServerSocketChannel.open方法,打开一个channel。然后触发fireChannelOpen事件。这个事件的接受是可以复写的。Binder自身接收了这个事件。在事件的处理中,继续向下完成具体的端口的绑定。对应Selector中的 socketChannel.socket().bind()。然后触发fireChannelBound事件。默认情况下,该事件无人接受,继续向下开始构造Boss线程池。我们知道在Netty中Boss线程池是用来接受和分发请求的核心线程池。所以在channel绑定后,必然要启动Boss线城池,随时准备接受client发来的请求。在Boss构造函数中,第一次注册了selector感兴趣的事件类型,SelectionKey.OP_ACCEPT。至此,在第六讲中介绍的使用Selector的几个关键步骤都体现在Netty中了。在Boss中回启动一个死循环来查询是否有感兴趣的事件发生,对于第一次的客户端的注册事件,Boss会将Channel注册给worker保存。
 
这里补充一下,也是图中忽略的部分,就是关于worker线程池的初始化时机问题。worker池的构造,在最开始构造ChannelFactory的时候就已经准备好了。在NioServerSocketChannelFactory的构造函数里,会new一个NioWorkerPool。在 NioWorkerPool的基类AbstractNioWorkerPool的构造函数中,会调用OpenSelector方法,其中也打开了一个 selector,并且启动了worker线程池。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void openSelector() {
        try {
            selector = Selector.open();
        catch (Throwable t) {
            throw new ChannelException("Failed to create a selector.", t);
        }
 
        // Start the worker thread with the new Selector.
        boolean success = false;
        try {
            DeadLockProofWorker.start(executor, new ThreadRenamingRunnable(this"New I/O  worker #" + id));
            success = true;
        finally {
            if (!success) {
                // Release the Selector if the execution fails.
                try {
                    selector.close();
                catch (Throwable t) {
                    logger.warn("Failed to close a selector.", t);
                }
                selector = null;
                // The method will return to the caller at this point.
            }
        }
        assert selector != null && selector.isOpen();
    }

至此,会分线程启动AbstractNioWorker中run逻辑。同样是循环处理任务队列。

1
2
3
4
processRegisterTaskQueue();
processEventQueue();
processWriteTaskQueue();
processSelectedKeys(selector.selectedKeys());
 
这样,设计使事件的接收和处理模块完全解耦。
由此可见,如果你想从nio切换到oio,只需要构造不同的ChannelFacotry即可。果然简洁优雅。

[转载] Netty教程的更多相关文章

  1. [转载] Netty

    转载自http://lippeng.iteye.com/blog/1907279 Netty是什么? 本质:JBoss做的一个Jar包 目的:快速开发高性能.高可靠性的网络服务器和客户端程序 优点:提 ...

  2. [转载] Netty源码分析

    转载自http://blog.csdn.net/kobejayandy/article/details/11836813 Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高 ...

  3. (转载)Ant教程

    ant教程(一) 写在所有之前 为了减少阅读的厌烦,我会使用尽量少的文字,尽量明白的表达我的意思,尽我所能吧.作为一个学习者,在我的文章中会存在各种问题,希望热心人指正.目录大概是这样 ant教程 ( ...

  4. Java NIO框架Netty教程(一) – Hello Netty

    先啰嗦两句,如果你还不知道Netty是做什么的能做什么.那可以先简单的搜索了解一下.我只能说Netty是一个NIO的框架,可以用于开发分布式的Java程序.具体能做什么,各位可以尽量发挥想象.技术,是 ...

  5. [转载] FreeMarker教程

    转载自http://www.blogjava.net/freeman1984/archive/2010/11/04/337239.html FreeMarker是一个模板引擎,一个基于模板生成文本输出 ...

  6. Netty教程

    Netty是一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. Netty是一个NIO客户端.服务端框架.允许快速简单 ...

  7. [转载]VIM 教程:Learn Vim Progressively

    文章来源:http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/   Learn Vim Progressively   TL ...

  8. [转载]CSS教程:实例讲解定位Position

    http://www.missyuan.com/thread-395406-1-1.html 1. position:static 所有元素的默认定位都是:position:static,这意味着元素 ...

  9. 转载-smarty教程(基本语法)

    转自:http://hi.baidu.com/qxxgvpdtzhbckpr/item/681049160d7be60db98a1aec 1.smarty的配置      首先,使用smarty第一件 ...

随机推荐

  1. SQLserver学习(四)——T-SQL编程之事务、索引和视图

    今天来分享下T-SQL高级编程中的事务.索引.视图,可以和之前的SQL server系列文章结合起来. 一.事务 事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个 ...

  2. javascript字典数据结构Dictionary实现

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...

  3. JS全选与不选、反选

    思路: 1.获取元素. 2.用for循环历遍数组,把checkbox的checked设置为true即实现全选,把checkbox的checked设置为false即实现不选. 3.通过if判断,如果ch ...

  4. windows下LINUX模拟终端Cypwin以及Vim的配置使用

    Cypwin的安装 从官网下载相应版本后,直接安装. 官网地址:Cypwin 安装过程中可以选择相应的Packages,我们需要安装的Vim就需要在这一步中选择相应的包. Cypwin的使用 纯命令行 ...

  5. oracle11G r2 静默安装单实例(待优化版)

    测试环境:centos 6.9 X64 mini 版 oracle版本:11G r2 Oracle软件包:db_112040_Linux-x86-64_1of7.zip;db_112040_Linux ...

  6. ELK系列~log4-nxlog-Fluentd-elasticsearch写json数据需要注意的几点

    经验与实践 前两篇文章里我们介绍了nxlog的日志收集和转发<ELK系列~Nxlog日志收集加转发(解决log4日志换行导致json转换失败问题)>,今天我们主要总结一下,在与log4和f ...

  7. LINUX 笔记-ubuntu 配置 jdk 环境

    在 /etc/profile 文件尾添加 JAVA_HOME=/opt/jdk1.8.0JRE_HOME=${JAVA_HOME}/jre   PATH=$PATH:$HOME/bin:$JAVA_H ...

  8. Java中的的画正三角方法

    在循环的语句的练习中,画正三角是一个很经典的例子,但是如果方法找的不对的话,即使最终画出来了,那么得到的代码也是非常的复杂,应用性不高. 下面有两种方法来画正三角,第一种是一种比较麻烦的办法,是通过归 ...

  9. Java就业企业面试问题-ssh框架

    SSH框架阶段SSH的优缺点,使用场景?   Hibernate优点:   (1) 对象/关系数据库映射(ORM) 它使用时只需要操纵对象,使开发更对象化,抛弃了数据库中心的思想,完全的面向对象思想 ...

  10. hive1.2.2部署

    1.解压hvie.tar,进入conf目录下,cp hive-default.xml.template hive-site.xml; 2.将hive下的新版本jline的JAR包拷贝到hadoop下: ...