Netty-ChannelPipeline

我们经常往pipeline中添加ChannelHandler来进行前后顺序控制处理实际业务。是不是类似Servlet和Filter过滤器,利用职责链模式的思想,通过一个一个的Handler进行业务传递处理。

1:图解ChannelPipeline、ChannelHandler流程

 2:这篇文章主要对ChannelPipeline进行总结学习,首先看下总结思维导图

3:功能说明

(1):ChannelPipeline和ChannelHandler的事件处理模型

     如图:

  1. 一个消息被socketChannel read()方法读取ByteBuf出发ChannelRead事件,由I/O线程NioEventLoop调用ChannelPipeline的fireChannelRead方法,将消息传输到ChannelPipeline中;
  2. 消息一次被ChannelHandler处理,这个过程中任何ChannelHandler都可以中断当前的流程,结束消息的传递;
  3. 处理完请求后,进行响应,调用ChannelHandlerContext的write方法发送消息,然后消息倒着在ChannelHandler中进行依次处理,直到最后哦socket进行write。

       Netty中事件分为inbound事件(通常由I/O线程触发,例如TCP链路建立事件、链路关闭事件、读事件、异常通知时间等,对应上图的左半部分)和 outbound事件(通常由用户主动发起的网络I/O操作,例如用户发起的连接操作、绑定操作、消息发送等操作,对应上图的右半部分)

 (2)inbound事件方法

inbound事件方法
事件方法说明
ChannelHandlerContext fireChannelRegistered();Channel注册事件
ChannelHandlerContext fireChannelActive();Tcp链路建立成功,Channel激活事件
ChannelHandlerContext fireChannelRead(Object msg);读事件
ChannelHandlerContext fireChannelReadComplete();读操作完成通知事件
ChannelHandlerContext fireExceptionCaught(Throwable cause);异常通知事件
ChannelHandlerContext fireUserEventTriggered(Object event);用户自定义事件
ChannelHandlerContext fireChannelWritabilityChanged();Channel的可写状态变化通知事件
ChannelHandlerContext fireChannelInactive();Tcp连接关闭,链路不可用通知事件

 

(3):outbound事件方法

outbound事件方法
事件方法说明
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);绑定本地地址事件
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);连接服务端事件
ChannelFuture write(Object msg, ChannelPromise promise);发送事件
ChannelHandlerContext flush();刷新事件
ChannelHandlerContext read();读事件
ChannelFuture disconnect(ChannelPromise promise);断开连接事件
ChannelFuture close(ChannelPromise promise);关闭当前Channel事件

(4)自定义拦截器

也就是我们经常写的各种业务处理handler,例如:处理编解码的,处理半包问题的,处理加解密的,处理分隔符的……  通常我们只需要继承ChannelHandlerAdapter类覆盖自己关心的方法即可。网上的例子非常多,大家去看下,这里简单举个:

/**
 * 拦截active时间,进行日志打印
 */
public class MyInboundHandler extends ChannelHandlerAdapter {
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("tcp connection");
        ctx.fireChannelActive();
    }
 
}

(5)构建pipeline

构建pipeline:其实不需要我们进行创建。Netty中使用ServerBootstrap或者bootstrap启动服务端或者客户端时,会为每个Channel链接创建一个独立的pipeline,我们只需要将自定义的Handler加入到pipeline中即可。(前边我们都是通过addLast()进行添加保证一定的顺序)。

主要特性:ChannelPipeline支持动态的添加或者删除ChannelHandler。一些业务场景会非常适用的。ChannelPipeline是线程安全,即可以运行多个业务线程并发操作ChannelPipeline,不存在多线程并发问题。但是ChannelHandler却不是线程安全,需要我们自己进行编码控制。

4:源码阶段

(1):类关系图

非常简单的,一个接口,一个默认实现类

(2):对ChannelHandler的管理

ChannelPipeline是ChannelHandler的管理容器,负责ChannelHandler的增删改查。类似Map的容器实现,看下新增方法吧,其它的也可以这样跟踪理解一下:

/**
 * 1,添加方法
 **/
@Override
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
        return addBefore((ChannelHandlerInvoker) null, baseName, name, handler);
}
 
/**
 * 1.1,调用添加方法
 **/
    @Override
    public ChannelPipeline addBefore(
            ChannelHandlerInvoker invoker, String baseName, String name, ChannelHandler handler) {
        //注意synchronized关键字,来保证并发安全,保证同步块内所有操作的原子性。
        synchronized (this) {
            //第一步   
            AbstractChannelHandlerContext ctx = getContextOrDie(baseName);
            //第二步 
            name = filterName(name, handler);
            //第三步 
            addBefore0(name, ctx, new DefaultChannelHandlerContext(this, invoker, name, handler));
        }
        return this;
    }
 
    //第一步,根据baseName获取它对应的DefaultChannelHandlerContext.
    private AbstractChannelHandlerContext getContextOrDie(String name) {
        AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(name);
        if (ctx == null) {
            throw new NoSuchElementException(name);
        } else {
            return ctx;
        }
    }
 
    @Override
    public ChannelHandlerContext context(String name) {
        if (name == null) {
            throw new NullPointerException("name");
        }
 
        synchronized (this) {
            return name2ctx.get(name);
        }
    }
 
    //第二步:对新增的handler名进行重复性校验
    private String filterName(String name, ChannelHandler handler) {
        if (name == null) {
            return generateName(handler);
        }
 
        if (!name2ctx.containsKey(name)) {
            return name;
        }
 
        throw new IllegalArgumentException("Duplicate handler name: " + name);
    }
 
    //第三部:进行添加
    private void addBefore0(
            final String name, AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
        //校验
        checkMultiplicity(newCtx);
        //相当于向一个链表中添加了项应该好理解
        newCtx.prev = ctx.prev;
        newCtx.next = ctx;
        ctx.prev.next = newCtx;
        ctx.prev = newCtx;
 
        name2ctx.put(name, newCtx);
 
        callHandlerAdded(newCtx);
    }
    //进行ctx校验:!h.isSharable() && h.added
    private static void checkMultiplicity(ChannelHandlerContext ctx) {
        ChannelHandler handler = ctx.handler();
        if (handler instanceof ChannelHandlerAdapter) {
            ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
            if (!h.isSharable() && h.added) {
                throw new ChannelPipelineException(
                        h.getClass().getName() +
                        " is not a @Sharable handler, so can't be added or removed multiple times.");
            }
            h.added = true;
        }
    }

(3):inbound事件

上边已经介绍了功能,这边以fireChannelActive为例,看下:

    /**
     * 调用head.fireChannelActive()后,判断当前channel是否自动读取,如果是,调用read方法
     **/
    @Override
    public ChannelPipeline fireChannelActive() {
        head.fireChannelActive();
 
        if (channel.config().isAutoRead()) {
            channel.read();
        }
 
        return this;
    }

(4):outbound事件

上边介绍了其功能,我们来用connect举例看下:

    //Pipeline本身不直接进行I/O操作的,前边看Channel和Unsafe的时候,都是有它俩进行真正IO操作的。通过方法追踪,最终是可以验证到unsafe身上的。
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
        return tail.connect(remoteAddress, localAddress);
    }
 
    @Override
    public void connect(
                ChannelHandlerContext ctx,
                SocketAddress remoteAddress, SocketAddress localAddress,
                ChannelPromise promise) throws Exception {
        unsafe.connect(remoteAddress, localAddress, promise);
    }

 ChannelPipeline其实还是比较简单的,主要做了Handler的管理,还有就是各种事件的触发响应。有没有感觉就像“管事的”,管着工作怎么流转,到什么地方需要处理什么工序,有什么事情通知给他,他根据不同事情,找不同的人进行处理。嗯,大概就是这样。 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Netty中使用WebSocket SSL,需要进行以下步骤: 1. 创建SSLContext对象 ``` KeyStore keyStore = KeyStore.getInstance("JKS"); InputStream stream = new FileInputStream("keystore.jks"); keyStore.load(stream, "password".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, "password".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); ``` 其中,keystore.jks是包含SSL证书的密钥库文件,password是密钥库密码。 2. 配置SslContext ``` SslContext sslCtx = SslContextBuilder.forServer(keyManagerFactory) .trustManager(trustManagerFactory) .build(); ``` 3. 配置WebSocketServerHandler ``` public class WebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> { private final WebSocketServerHandshaker handshaker; public WebSocketServerHandler(WebSocketServerHandshaker handshaker) { this.handshaker = handshaker; } @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { // 处理WebSocket消息 } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 握手成功后,建立WebSocket连接 handshaker.handshake(ctx.channel(), new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 处理异常 } } ``` 4. 配置WebSocketServerInitializer ``` public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> { private final SslContext sslCtx; public WebSocketServerInitializer(SslContext sslCtx) { this.sslCtx = sslCtx; } @Override public void initChannel(SocketChannel ch) throws Exception { // 配置ChannelPipeline ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerProtocolHandler("/websocket")); pipeline.addLast(new WebSocketServerHandler(handshaker)); pipeline.addLast(sslCtx.newHandler(ch.alloc())); } } ``` 其中,WebSocketServerProtocolHandler是Netty提供的WebSocket协议处理器,用于处理WebSocket握手和帧。 5. 启动WebSocket服务器 ``` EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new WebSocketServerInitializer(sslCtx)) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } ``` 其中,sslCtx是之前创建的SSLContext对象。 以上就是在Netty中配置WebSocket SSL的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值