Netty架构设计-事件驱动模型-详细版

超详细Netty入门,看这篇就够了! 思维导图 前言 本文主要讲述Netty框架的一些特性以及重要组件,希望看完之后能对Netty框架有一个比较直观的感受,希望能帮助读者快速入门Netty,减少一些弯路。 一、Netty概述 官方的介绍: Netty isan asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & .. 阅读详情

Netty 架构设计 - 事件驱动模型详解(初学者版)

用最简单的方式理解 Netty 的核心设计思想

前言

很多初学者看 Netty 的文章时,会被各种专业术语搞晕:事件驱动、责任链、ChannelPipeline…这些到底是什么?

本文用生活化的例子,帮你彻底理解 Netty 的设计原理。看完后你会发现:Netty 的设计其实很简单,就是把复杂的网络编程变成了"搭积木"。


一、传统网络编程的痛点

问题场景

假设你要开发一个聊天服务器,需要处理:

  1. 接收客户端连接
  2. 读取客户端消息
  3. 解析消息内容
  4. 处理业务逻辑
  5. 返回响应
  6. 记录日志
  7. 处理异常

传统写法:所有逻辑混在一起

// 传统 BIO 写法(伪代码)
while (true) {
    Socket socket = serverSocket.accept(); // 接收连接
    
    try {
        // 读取数据
        byte[] data = socket.read();
        
        // 解析数据
        String message = new String(data);
        
        // 处理业务
        String response = handleBusiness(message);
        
        // 返回响应
        socket.write(response.getBytes());
        
        // 记录日志
        log.info("处理完成");
        
    } catch (Exception e) {
        // 处理异常
        log.error("出错了", e);
    }
}

问题:

  • 代码混乱,难以维护
  • 功能耦合,难以扩展
  • 一个线程处理一个连接,性能差

二、Netty 的解决方案:事件驱动 + 责任链

核心思想

Netty 把网络编程变成了**“事件响应"和"流水线处理”**:

  1. 事件驱动:不是你主动问"有数据吗?“,而是"有数据了会通知你”
  2. 责任链模式:把处理逻辑拆成多个小模块,像流水线一样依次处理

生活化类比

传统方式:你是个全能员工

客户来了 → 你接待
客户说话 → 你听
客户需求 → 你处理
客户付款 → 你收钱
客户离开 → 你送客

一个人干所有事,累死了!

Netty 方式:你是个流水线工厂

客户来了 → 前台接待(Handler 1)
客户说话 → 翻译员翻译(Handler 2)
客户需求 → 业务员处理(Handler 3)
客户付款 → 财务收钱(Handler 4)
客户离开 → 保安送客(Handler 5)

每个人只做自己的事,专业高效!


三、事件驱动模型详解

什么是事件?

在 Netty 中,事件就是网络操作的各个阶段。

常见事件类型

入站事件(Inbound Events)- 数据进来

就像客户来你店里:

事件生活场景Netty 中的含义
channelRegistered客户进门登记Channel 注册到 EventLoop
channelActive客户坐下准备点餐连接建立成功
channelRead客户开始说话收到数据
channelReadComplete客户说完了数据读取完毕
channelInactive客户起身要走连接断开
exceptionCaught客户闹事发生异常
出站事件(Outbound Events)- 数据出去

就像你主动做事:

事件生活场景Netty 中的含义
bind开门营业绑定端口
connect主动拜访客户发起连接
write给客户说话写数据到缓冲区
flush确保客户听到把缓冲区数据发出去
disconnect送客断开连接
close关门关闭 Channel

事件驱动的工作流程

传统方式(主动轮询):
你:有客户吗?(查询)
门:没有
你:有客户吗?(查询)
门:没有
你:有客户吗?(查询)
门:有!
你:好的,我去接待

事件驱动方式(被动通知):
你:我在这等着,有客户叫我
--- 你可以做其他事 ---
门:有客户来了!(事件通知)
你:好的,我去接待

代码对比

传统方式:

// 不停地问
while (true) {
    if (有数据()) {
        处理数据();
    }
    Thread.sleep(100); // 浪费 CPU
}

事件驱动方式:

// 注册事件处理器,等通知
channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 有数据时自动调用
        处理数据(msg);
    }
});

四、核心组件详解

1. Channel(通道)

是什么:Channel 就是一个连接,代表客户端和服务器之间的通信管道。

生活类比:电话线

  • 你和朋友打电话,电话线就是 Channel
  • 可以通过它发送消息(write)
  • 可以通过它接收消息(read)
  • 可以挂断(close)

常用方法:

Channel channel = ...;

// 写数据(不会立即发送)
channel.write("Hello");

// 刷新(真正发送)
channel.flush();

// 写并刷新(常用)
channel.writeAndFlush("Hello");

// 关闭连接
channel.close();

// 获取远程地址
SocketAddress address = channel.remoteAddress();

代码示例:

public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 连接建立时
        Channel channel = ctx.channel();
        System.out.println("新连接:" + channel.remoteAddress());
        
        // 发送欢迎消息
        channel.writeAndFlush("欢迎光临!");
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 收到消息时
        System.out.println("收到消息:" + msg);
        
        // 回复消息
        ctx.channel().writeAndFlush("收到:" + msg);
    }
}

2. ChannelHandler(事件处理器)

是什么:ChannelHandler 是处理事件的工作人员。

生活类比:餐厅的各个岗位

  • 前台(接待 Handler):负责接待客户
  • 服务员(点餐 Handler):负责记录订单
  • 厨师(烹饪 Handler):负责做菜
  • 收银员(结账 Handler):负责收钱

两大类型:

ChannelInboundHandler(入站处理器)

处理进来的数据和事件:

public class MyInboundHandler extends ChannelInboundHandlerAdapter {
    
    // 连接建立
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("连接建立了");
    }
    
    // 收到数据
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("收到数据:" + msg);
        
        // 传递给下一个处理器
        ctx.fireChannelRead(msg);
    }
    
    // 数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        System.out.println("数据读完了");
        ctx.flush(); // 刷新缓冲区
    }
    
    // 连接断开
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("连接断开了");
    }
    
    // 发生异常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("出错了:" + cause.getMessage());
        ctx.close(); // 关闭连接
    }
}
ChannelOutboundHandler(出站处理器)

处理出去的数据和操作:

public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
    
    // 写数据
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("准备发送:" + msg);
        
        // 可以修改数据
        String newMsg = "【已加密】" + msg;
        
        // 传递给下一个处理器
        ctx.write(newMsg, promise);
    }
    
    // 连接操作
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, 
                       SocketAddress localAddress, ChannelPromise promise) {
        System.out.println("准备连接:" + remoteAddress);
        ctx.connect(remoteAddress, localAddress, promise);
    }
    
    // 关闭操作
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
        System.out.println("准备关闭连接");
        ctx.close(promise);
    }
}

3. ChannelPipeline(处理器链)

是什么:ChannelPipeline 是一条流水线,里面按顺序排列着多个 Handler。

生活类比:餐厅的服务流程

客户进门 → 前台接待 → 服务员点餐 → 厨师做菜 → 服务员上菜 → 收银结账 → 保安送客
         ↓          ↓          ↓         ↓         ↓         ↓
      Handler1   Handler2   Handler3  Handler4  Handler5  Handler6

工作原理:

入站事件(数据进来):从头到尾
┌─────────────────────────────────────────┐
│  Pipeline                                │
│  ┌────┐  ┌────┐  ┌────┐  ┌────┐        │
│  │ H1 │→ │ H2 │→ │ H3 │→ │ H4 │        │
│  └────┘  └────┘  └────┘  └────┘        │
└─────────────────────────────────────────┘
   数据 →  解码  →  业务  →  响应

出站事件(数据出去):从尾到头
┌─────────────────────────────────────────┐
│  Pipeline                                │
│  ┌────┐  ┌────┐  ┌────┐  ┌────┐        │
│  │ H1 │← │ H2 │← │ H3 │← │ H4 │        │
│  └────┘  └────┘  └────┘  └────┘        │
└─────────────────────────────────────────┘
   发送 ←  编码  ←  业务  ←  数据

代码示例:

// 配置 Pipeline
ChannelPipeline pipeline = channel.pipeline();

// 添加入站处理器(按顺序执行)
pipeline.addLast("decoder", new StringDecoder());      // 1. 解码
pipeline.addLast("logger", new LoggingHandler());      // 2. 日志
pipeline.addLast("business", new BusinessHandler());   // 3. 业务

// 添加出站处理器(逆序执行)
pipeline.addLast("encoder", new StringEncoder());      // 编码

完整示例:

public class PipelineDemo {
    public static void main(String[] args) {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
                ChannelPipeline pipeline = ch.pipeline();
                
                // 入站处理器链
                pipeline.addLast("1-接待", new Handler1());
                pipeline.addLast("2-翻译", new Handler2());
                pipeline.addLast("3-业务", new Handler3());
                
                // 出站处理器链
                pipeline.addLast("4-编码", new Handler4());
            }
        });
    }
    
    // Handler 1:接待
    static class Handler1 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.println("Handler1: 客户来了,接待中...");
            ctx.fireChannelRead(msg); // 传给下一个
        }
    }
    
    // Handler 2:翻译
    static class Handler2 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.println("Handler2: 翻译客户的话...");
            String translated = "【已翻译】" + msg;
            ctx.fireChannelRead(translated); // 传给下一个
        }
    }
    
    // Handler 3:业务处理
    static class Handler3 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.println("Handler3: 处理业务 - " + msg);
            
            // 处理完后,发送响应(触发出站)
            ctx.writeAndFlush("处理完成");
        }
    }
    
    // Handler 4:编码(出站)
    static class Handler4 extends ChannelOutboundHandlerAdapter {
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
            System.out.println("Handler4: 编码响应 - " + msg);
            ctx.write("【已编码】" + msg, promise);
        }
    }
}

执行流程:

收到数据 "Hello"
  ↓
Handler1: 客户来了,接待中...
  ↓
Handler2: 翻译客户的话...(变成 "【已翻译】Hello")
  ↓
Handler3: 处理业务 - 【已翻译】Hello
  ↓ (调用 writeAndFlush,触发出站)
Handler4: 编码响应 - 处理完成(变成 "【已编码】处理完成")
  ↓
发送 "【已编码】处理完成"

4. ChannelHandlerContext(上下文)

是什么:ChannelHandlerContext 是 Handler 的工作台,提供了操作 Channel 和 Pipeline 的能力。

生活类比:服务员的工作台

  • 可以拿到客户信息(Channel)
  • 可以通知下一个同事(fireChannelRead)
  • 可以给客户发消息(write)
  • 可以查看整个流程(Pipeline)

常用方法:

public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 1. 获取 Channel
        Channel channel = ctx.channel();
        
        // 2. 获取 Pipeline
        ChannelPipeline pipeline = ctx.pipeline();
        
        // 3. 传递给下一个入站处理器
        ctx.fireChannelRead(msg);
        
        // 4. 写数据(从当前位置开始,经过后续的出站处理器)
        ctx.write("Hello");
        
        // 5. 写数据(从 Pipeline 尾部开始,经过所有出站处理器)
        ctx.channel().write("Hello");
        
        // 6. 关闭连接
        ctx.close();
    }
}

ctx.write() vs channel.write() 的区别:

Pipeline: [H1] → [H2] → [H3] → [H4]
                  ↑
                当前位置

ctx.write("Hello")
  → 从 H2 开始往前找出站处理器:H1
  
channel.write("Hello")  
  → 从 H4 开始往前找出站处理器:H4 → H3 → H2 → H1

示例:

public class ContextDemo extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("收到消息:" + msg);
        
        // 方式1:通过 ctx 写(推荐,性能更好)
        ctx.writeAndFlush("响应1");
        
        // 方式2:通过 channel 写
        ctx.channel().writeAndFlush("响应2");
        
        // 传递给下一个处理器
        ctx.fireChannelRead(msg);
    }
}

5. Future 和 Promise

是什么:Future 是异步操作的结果占位符,Promise 是可写的 Future。

生活类比:取餐号

  • 你点餐后拿到取餐号(Future)
  • 号码上写着"88号"(将来的结果)
  • 你可以拿着号去做其他事
  • 做好了会叫号通知你

ChannelFuture 示例:

// 发起连接(异步操作)
ChannelFuture future = bootstrap.connect("localhost", 8080);

// 方式1:添加监听器(推荐)
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture f) {
        if (f.isSuccess()) {
            System.out.println("连接成功");
            f.channel().writeAndFlush("Hello");
        } else {
            System.out.println("连接失败:" + f.cause());
        }
    }
});

// 方式2:阻塞等待(不推荐)
future.sync(); // 等待连接完成
if (future.isSuccess()) {
    System.out.println("连接成功");
}

链式操作:

bootstrap.connect("localhost", 8080)
    .addListener(connectFuture -> {
        if (connectFuture.isSuccess()) {
            System.out.println("1. 连接成功");
            
            // 发送数据
            connectFuture.channel().writeAndFlush("Hello")
                .addListener(writeFuture -> {
                    if (writeFuture.isSuccess()) {
                        System.out.println("2. 发送成功");
                        
                        // 关闭连接
                        writeFuture.channel().close()
                            .addListener(closeFuture -> {
                                System.out.println("3. 关闭成功");
                            });
                    }
                });
        }
    });

五、责任链模式详解

什么是责任链模式?

定义:把请求沿着一条链传递,链上的每个节点都可以处理请求。

生活场景:请假审批流程

员工请假 → 组长审批 → 经理审批 → 总监审批 → 批准

每个人只负责自己的部分:

  • 组长:1天以内的假,我批
  • 经理:3天以内的假,我批
  • 总监:7天以内的假,我批
  • 更多天:需要老板批

Netty 中的责任链

Pipeline 就是责任链:

pipeline.addLast("解码器", new StringDecoder());
pipeline.addLast("日志", new LoggingHandler());
pipeline.addLast("业务", new BusinessHandler());
pipeline.addLast("编码器", new StringEncoder());

数据流转:

收到字节数据
  ↓
解码器:把字节转成字符串
  ↓
日志:记录日志
  ↓
业务:处理业务逻辑
  ↓
编码器:把字符串转成字节
  ↓
发送字节数据

责任链的优点

  1. 解耦:每个 Handler 只关心自己的事
  2. 灵活:可以随意增删 Handler
  3. 复用:Handler 可以在不同的 Pipeline 中复用

示例:

// 场景1:简单服务器
pipeline.addLast(new StringDecoder());
pipeline.addLast(new BusinessHandler());
pipeline.addLast(new StringEncoder());

// 场景2:加上日志
pipeline.addLast(new StringDecoder());
pipeline.addLast(new LoggingHandler());  // 新增
pipeline.addLast(new BusinessHandler());
pipeline.addLast(new StringEncoder());

// 场景3:加上权限验证
pipeline.addLast(new StringDecoder());
pipeline.addLast(new AuthHandler());     // 新增
pipeline.addLast(new LoggingHandler());
pipeline.addLast(new BusinessHandler());
pipeline.addLast(new StringEncoder());

实战示例:HTTP 服务器

public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        
        // 1. HTTP 解码器
        pipeline.addLast("httpDecoder", new HttpRequestDecoder());
        
        // 2. HTTP 聚合器(把多个消息聚合成一个完整的 HTTP 请求)
        pipeline.addLast("httpAggregator", new HttpObjectAggregator(65536));
        
        // 3. HTTP 编码器
        pipeline.addLast("httpEncoder", new HttpResponseEncoder());
        
        // 4. 业务处理器
        pipeline.addLast("httpHandler", new HttpServerHandler());
    }
}

// 业务处理器
class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
        // 处理 HTTP 请求
        String uri = request.uri();
        System.out.println("收到请求:" + uri);
        
        // 构造响应
        String content = "Hello, Netty!";
        FullHttpResponse response = new DefaultFullHttpResponse(
            HttpVersion.HTTP_1_1,
            HttpResponseStatus.OK,
            Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)
        );
        
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.length());
        
        // 发送响应
        ctx.writeAndFlush(response);
    }
}

处理流程:

客户端发送 HTTP 请求
  ↓
HttpRequestDecoder:解析 HTTP 协议
  ↓
HttpObjectAggregator:聚合成完整请求
  ↓
HttpServerHandler:处理业务逻辑
  ↓
HttpResponseEncoder:编码成 HTTP 响应
  ↓
发送给客户端

六、完整实战示例

场景:聊天服务器

需求:

  1. 客户端连接时,发送欢迎消息
  2. 收到客户端消息,广播给所有人
  3. 客户端断开时,通知其他人
  4. 记录所有操作日志

服务端代码

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.concurrent.ConcurrentHashMap;

public class ChatServer {
    
    // 保存所有连接的客户端
    private static final ConcurrentHashMap<String, Channel> clients = new ConcurrentHashMap<>();
    
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            
                            // 责任链:按顺序添加处理器
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            pipeline.addLast("logger", new LogHandler());
                            pipeline.addLast("chat", new ChatHandler());
                        }
                    });
            
            System.out.println("聊天服务器启动在 7397 端口");
            ChannelFuture future = bootstrap.bind(7397).sync();
            future.channel().closeFuture().sync();
            
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    // Handler 1:日志处理器
    static class LogHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            System.out.println("[日志] 新连接:" + ctx.channel().remoteAddress());
            ctx.fireChannelActive(); // 传递给下一个处理器
        }
        
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.println("[日志] 收到消息:" + msg);
            ctx.fireChannelRead(msg); // 传递给下一个处理器
        }
        
        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            System.out.println("[日志] 连接断开:" + ctx.channel().remoteAddress());
            ctx.fireChannelInactive(); // 传递给下一个处理器
        }
    }
    
    // Handler 2:聊天业务处理器
    static class ChatHandler extends ChannelInboundHandlerAdapter {
        
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            Channel channel = ctx.channel();
            String clientId = channel.id().asShortText();
            
            // 保存客户端
            clients.put(clientId, channel);
            
            // 发送欢迎消息
            channel.writeAndFlush("欢迎来到聊天室!你的ID是:" + clientId + "\n");
            
            // 通知其他人
            broadcast(clientId + " 加入了聊天室\n", channel);
            
            System.out.println("当前在线人数:" + clients.size());
        }
        
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            String message = (String) msg;
            String clientId = ctx.channel().id().asShortText();
            
            // 广播消息
            String broadcastMsg = clientId + " 说:" + message + "\n";
            broadcast(broadcastMsg, null);
        }
        
        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            String clientId = ctx.channel().id().asShortText();
            
            // 移除客户端
            clients.remove(clientId);
            
            // 通知其他人
            broadcast(clientId + " 离开了聊天室\n", null);
            
            System.out.println("当前在线人数:" + clients.size());
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            System.out.println("发生异常:" + cause.getMessage());
            ctx.close();
        }
        
        // 广播消息给所有人(除了排除的 Channel)
        private void broadcast(String message, Channel exclude) {
            clients.values().forEach(channel -> {
                if (channel != exclude) {
                    channel.writeAndFlush(message);
                }
            });
        }
    }
}

客户端代码

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;

public class ChatClient {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            pipeline.addLast("client", new ClientHandler());
                        }
                    });
            
            System.out.println("连接服务器...");
            ChannelFuture future = bootstrap.connect("localhost", 7397);
            
            // 添加连接监听器
            future.addListener((ChannelFutureListener) f -> {
                if (f.isSuccess()) {
                    System.out.println("连接成功!");
                    System.out.println("请输入消息(输入 quit 退出):");
                    
                    // 启动输入线程
                    Channel channel = f.channel();
                    new Thread(() -> {
                        Scanner scanner = new Scanner(System.in);
                        while (scanner.hasNextLine()) {
                            String line = scanner.nextLine();
                            if ("quit".equalsIgnoreCase(line)) {
                                channel.close();
                                break;
                            }
                            channel.writeAndFlush(line + "\n");
                        }
                    }).start();
                } else {
                    System.out.println("连接失败:" + f.cause().getMessage());
                }
            });
            
            future.channel().closeFuture().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }
    
    // 客户端处理器
    static class ClientHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.print(msg); // 打印服务器消息
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            System.out.println("发生异常:" + cause.getMessage());
            ctx.close();
        }
    }
}

运行效果

服务端输出:

聊天服务器启动在 7397 端口
[日志] 新连接:/127.0.0.1:54321
当前在线人数:1
[日志] 新连接:/127.0.0.1:54322
当前在线人数:2
[日志] 收到消息:大家好
[日志] 收到消息:你好
[日志] 连接断开:/127.0.0.1:54321
当前在线人数:1

客户端1输出:

连接服务器...
连接成功!
请输入消息(输入 quit 退出):
欢迎来到聊天室!你的ID是:abc123
def456 加入了聊天室
大家好
def456 说:你好
quit

客户端2输出:

连接服务器...
连接成功!
请输入消息(输入 quit 退出):
欢迎来到聊天室!你的ID是:def456
abc123 说:大家好
你好
abc123 离开了聊天室

代码解析

事件驱动体现:

// 不需要轮询,事件发生时自动调用
@Override
public void channelActive(ChannelHandlerContext ctx) {
    // 连接建立时自动调用
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // 收到数据时自动调用
}

责任链体现:

pipeline.addLast("decoder", new StringDecoder());  // 1. 解码
pipeline.addLast("encoder", new StringEncoder());  // 2. 编码
pipeline.addLast("logger", new LogHandler());      // 3. 日志
pipeline.addLast("chat", new ChatHandler());       // 4. 业务

// 数据流转:
// 收到数据 → decoder → logger → chat
// 发送数据 → chat → encoder → 网络

异步非阻塞体现:

// 连接操作立即返回,不阻塞
ChannelFuture future = bootstrap.connect("localhost", 7397);

// 添加监听器,连接完成后回调
future.addListener((ChannelFutureListener) f -> {
    if (f.isSuccess()) {
        System.out.println("连接成功!");
    }
});

七、核心设计思想总结

1. 事件驱动(Event-Driven)

传统方式:主动轮询

while (true) {
    if (有数据()) {
        处理();
    }
}

Netty 方式:被动通知

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // 有数据时自动调用
}

优势:

  • 不浪费 CPU(不需要轮询)
  • 响应及时(事件发生立即处理)
  • 代码清晰(每个事件对应一个方法)

2. 责任链模式(Chain of Responsibility)

核心思想:把复杂的处理逻辑拆分成多个小模块,像流水线一样依次处理。

类比:

传统方式:一个人干所有活
你 → 接待、翻译、处理、编码、发送(累死)

Netty 方式:流水线分工
接待员 → 翻译员 → 业务员 → 编码员 → 发送员(高效)

优势:

  • 解耦:每个 Handler 只关心自己的事
  • 灵活:可以随意增删 Handler
  • 复用:Handler 可以在不同场景复用

3. 异步非阻塞(Asynchronous Non-blocking)

阻塞方式:

connect();  // 等待连接完成(阻塞)
write();    // 等待写入完成(阻塞)

Netty 方式:

ChannelFuture future = connect();  // 立即返回
future.addListener(f -> {
    // 连接完成后回调
});

优势:

  • 不阻塞线程
  • 提高并发能力
  • 充分利用系统资源

4. 组件化设计

Netty 把网络编程的各个部分都抽象成了组件:

组件作用类比
Channel连接通道电话线
EventLoop事件循环服务员
ChannelHandler事件处理器各个岗位的员工
ChannelPipeline处理器链流水线
ByteBuf数据容器购物车
Future异步结果取餐号

优势:

  • 职责清晰
  • 易于理解
  • 方便扩展

八、学习路线建议

第一阶段:理解核心概念

  1. 事件驱动:理解"被动通知"的思想
  2. Channel:理解连接的抽象
  3. Handler:理解事件处理器
  4. Pipeline:理解责任链

第二阶段:动手实践

  1. 写一个简单的 Echo 服务器(回显服务器)
  2. 写一个聊天服务器
  3. 写一个 HTTP 服务器
  4. 写一个文件传输服务器

第三阶段:深入源码

  1. 研究 EventLoop 的实现
  2. 研究 Pipeline 的实现
  3. 研究 ByteBuf 的实现
  4. 研究编解码器的实现

第四阶段:性能优化

  1. 理解零拷贝
  2. 理解内存池
  3. 理解对象池
  4. 理解线程模型

九、常见问题解答

Q1:为什么要用事件驱动?

A:传统的轮询方式会浪费 CPU,而事件驱动是"有事才干活",效率更高。

// 轮询方式(浪费 CPU)
while (true) {
    if (有数据()) {  // 一直问
        处理();
    }
    Thread.sleep(100);
}

// 事件驱动(高效)
@Override
public void channelRead(...) {
    // 有数据时才调用
}

Q2:为什么要用责任链?

A:把复杂的逻辑拆分成多个小模块,每个模块只做一件事,代码更清晰,更容易维护。

// 不用责任链(混乱)
void handle(data) {
    解码(data);
    验证(data);
    处理(data);
    编码(data);
    发送(data);
}

// 用责任链(清晰)
pipeline.addLast(new 解码器());
pipeline.addLast(new 验证器());
pipeline.addLast(new 处理器());
pipeline.addLast(new 编码器());

Q3:Handler 什么时候用 Inbound,什么时候用 Outbound?

A:

  • Inbound:处理进来的数据(读、连接建立、异常等)
  • Outbound:处理出去的操作(写、连接、关闭等)
// 处理收到的数据 → Inbound
class MyInboundHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(...) { }
}

// 处理发送的数据 → Outbound
class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(...) { }
}

Q4:ctx.write() 和 channel.write() 有什么区别?

A:

  • ctx.write():从当前 Handler 开始往前找出站处理器
  • channel.write():从 Pipeline 尾部开始找出站处理器
Pipeline: [H1] → [H2] → [H3] → [H4]
                  ↑
                当前位置

ctx.write("Hello")      → 从 H2 往前:H1
channel.write("Hello")  → 从 H4 往前:H4 → H3 → H2 → H1

建议:一般用 ctx.write(),性能更好。

Q5:为什么要用 Future?

A:因为 Netty 是异步的,操作立即返回,但结果还没出来。Future 就是"结果的占位符",可以设置回调,结果出来后自动通知你。

// 同步方式(阻塞)
connect();  // 等待连接完成
write();    // 等待写入完成

// 异步方式(不阻塞)
ChannelFuture future = connect();  // 立即返回
future.addListener(f -> {
    // 连接完成后回调
    write();
});

Q6:一个 Channel 可以有多个 Handler 吗?

A:可以!而且这正是 Netty 的设计精髓。一个 Pipeline 可以有多个 Handler,形成责任链。

pipeline.addLast("解码", new StringDecoder());
pipeline.addLast("日志", new LoggingHandler());
pipeline.addLast("验证", new AuthHandler());
pipeline.addLast("业务", new BusinessHandler());
pipeline.addLast("编码", new StringEncoder());

Q7:Handler 可以复用吗?

A:

  • 无状态 Handler:可以复用(加 @Sharable 注解)
  • 有状态 Handler:不能复用(每个 Channel 一个实例)
// 无状态,可以复用
@Sharable
class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(...) {
        // 不保存任何状态
    }
}

// 有状态,不能复用
class MyHandler extends ChannelInboundHandlerAdapter {
    private int count = 0;  // 有状态
    
    @Override
    public void channelRead(...) {
        count++;  // 会被多个 Channel 共享,出问题
    }
}

十、总结

Netty 的核心设计思想

  1. 事件驱动:不是你问"好了吗",而是"好了"会通知你
  2. 责任链:把复杂的处理拆成多个小模块,像流水线一样
  3. 异步非阻塞:操作立即返回,不等待结果
  4. 组件化:每个部分都是独立的组件,职责清晰

核心组件关系图

┌─────────────────────────────────────────────────┐
│                   Channel                        │
│  (连接通道,代表一个客户端连接)                    │
│                                                  │
│  ┌────────────────────────────────────────┐    │
│  │         ChannelPipeline                 │    │
│  │  (处理器链,责任链模式)                   │    │
│  │                                          │    │
│  │  ┌────────┐  ┌────────┐  ┌────────┐   │    │
│  │  │Handler1│→ │Handler2│→ │Handler3│   │    │
│  │  │(解码)  │  │(日志)  │  │(业务)  │   │    │
│  │  └────────┘  └────────┘  └────────┘   │    │
│  │                                          │    │
│  └────────────────────────────────────────┘    │
│                                                  │
│  ChannelFuture (异步操作结果)                    │
└─────────────────────────────────────────────────┘

记忆口诀

  • Channel:一个连接
  • Handler:一个员工
  • Pipeline:一条流水线
  • Event:一个通知
  • Future:一个取餐号
【Netty系列】Netty高性能架构设计以及光速入门 关于这个问题,我贴一段官网的阐述:大概意思是:Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。具体来说,Netty就是对于Java NIO 的封装。 阅读详情

相关推荐

深入解析 Netty 的线程模型

Netty 的线程模型通过巧妙地结合和优化传统的线程处理方式,提供了一种高效、灵活且可扩展的解决方案,适用于各种高并发的网络应用场景。理解和选择合适的线程模型对于构建高性能的网络应用至关重要。我是马丁,一名专注于网络编程技术的开发者,经常在 优快云 平台分享技术心得。希望本文能对您有所帮助,欢迎大家三连加关注,一起交流探讨更多技术话题!

持续分享有趣的技术干货 1477

Netty架构设计-事件驱动模型-简洁版

Netty采用事件驱动模型实现高性能网络通信,核心架构包括: 事件驱动:通过回调机制处理网络事件,避免CPU轮询浪费 通道(Channel):抽象网络连接,提供统一操作接口 处理器链(Pipeline):采用责任链模式,数据依次经过多个处理器(Handler)处理 异步Future:通过结果占位符实现非阻塞操作 组件化设计:各模块职责单一,通过组合实现复杂功能 优势在于: 高性能:事件驱动+异步非阻塞 高扩展:责任链模式易于功能扩展 低耦合:组件化设计提升可维护性 Netty将复杂网络编程简化为"

java_rutu的博客 305

事件驱动的 Netty

前言 Netty 官网对自己的描述是一个异步的事件驱动的网络框架。异步如何表现和异步背后的线程模型在上文已经分析过了。 事件驱动是 Netty 中另外一个非常重要的概念。Netty 框架中描述的事件是一种抽象概念,其将框架运行过程中会发生的许多“动作”以事件的概念定义。并且在事件发生时,触发对应的回调方法。对于开发者而言,只需要实现自己感兴趣的事件的回调方法。因此,对于事件的回调方法的实现,构成了...

kuangzhanshatian的专栏 251

深入探索Netty的事件驱动模型与实现原理

事件驱动模型是一种通过回调机制处理异步事件的设计模式。其主要特点是将事件的检测和处理分离开来,通过事件循环不断监听事件源,一旦检测到事件发生,就调用相应的回调函数处理事件。这种模型在网络编程中尤为适用,因为网络I/O操作通常是非阻塞和异步的。Netty通过其事件驱动模型高效地处理网络I/O操作,适用于高并发和高吞吐量的网络应用。通过理解和应用Netty的事件驱动模型,可以开发出性能优异的网络应用。本文通过详细讲解Netty的事件驱动模型和实际代码示例,帮助你更好地掌握Netty的核心机制。

微笑听雨 6093

Netty权威指南 第二版 源码深度解析

Netty是一个高性能的异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它基于Java NIO(New I/O)提供的API构建,能够处理大量并发连接,同时保持低延迟和高吞吐量。Netty的架构设计非常灵活,使得它不仅可以用于简单的客户端-服务器通信,而且还能用作复杂的应用程序的一部分,如游戏服务器、实时交易系统等。本章将为读者提供Netty的入门级概述,为理解后续章节中更深层次的概念打下基础。我们将探讨Netty的主要特点以及它是如何在行业内部得到广泛应用的。

weixin_35414484的博客 1127

AirSim安装和地图加载

详细教程如何安装AirSim并加载地图

fegxg的博客 3034

【Netty 什么是事件驱动模型?】

首先来简单的看一下netty的简介: Netty 是一个 Java 开源通讯框架,是一个异步的,基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络 IO 程序。 这里主要和大家分享的就是事件驱动模型。 首先我们看下百度百科是如何描述“事件驱动模型”的:鼠标的一个点击,移动,键盘的按键按下等等操作,都是对应操作系统的一个事件,然后应用程序接受你的操作进行处理。 em...好像没有什么不对的地方,但是基本上我们看完后也还是不懂什么是事件驱动模型。只能说百度百科凝结的都是精华(敷衍~)。 1

W664160450的博客 2200

il2cpp分析-gobal-metadata.dat解密

打开ida,把libil2cpp拖到ida中按快捷键Shift+F12等待字符串分析完后,搜索global-metadata.dat。ida中双击sub_57EE7C进入,VS code中也跳转到LoadMetadataFile函数。VSCode打开il2cpp源代码,全局搜索global-metadata.dat。工具:010Editor,IDA 7.5,Jadx,VS Code。判断加密逻辑为根据文件的长度循环读取字节码做亦或加密。双击搜索出来的结果,切记要选il2cpp目录下的。

Android_machong的博客 8025

Netty 如何实现事件驱动

事件驱动是一种编程范式,系统通过监听和响应事件(Event)来执行操作,而不是通过轮询或阻塞等待。事件通常由外部刺激触发(如网络 I/O、定时器、用户任务),由事件循环(Event Loop)捕获并分发给相应的处理逻辑。异步非阻塞:避免线程阻塞,提升资源利用率。高并发:单线程处理多个事件,适合网络应用。模块化:事件处理逻辑解耦,易于扩展。用户事件通过 ctx.fireUserEventTriggered(event) 触发自定义事件。定时任务。

eclipseC的博客 1788

基于dify平台批量分析excel格式信息

如何以表格形式批量输入一些信息,然后让大模型以对话应用形式逐条进行推理分析?请逐行分析以下数据,给出推理结果:{{input}}这里提供一个分步解决方案,结合。

knightissocool的博客 8012

【智能优化算法】2024年SCI鳗鱼-石斑鱼优化算法(EGO),含MathType公式、word伪代码、visio流程图、matlab代码等。

该文中包括5个内容:用MathType编辑的公式、伪代码、visio的流程图,matlab代码,PDF论文,拿来直接用,可以帮助科研者省下超多时间。 鳗鱼-石斑鱼优化算法(Eel and grouper optimizer ,EGO)是一种新型的元启发式算法(智能优化算法),灵感来源于海洋生态系统中鳗鱼和石斑鱼的共生作用和觅食策略。该成果由Ali Mohammadzadeh和Seyedali Mirjalili两位作者于2024年6月发表在SCI著名期刊《Cluster Computing》上。

2301_79359347的博客 1044

Netty框架之异步事件驱动模型

Netty使用了异步的事件驱动模型,来触发网络I/O的各种操作,其在socket层上面封装一层异步事件驱动模型,使得业务代码不需要关心网络底层,就可以编写异步的无网络I/O阻塞的代码。 Netty的异步事件驱动模型主要涉及到下面几个核心的概念:   Channel:表示一个与socket关联的通道 ChannelPipeline:管道,一个Channel拥有一个ChannelPipel

wuhuan_wp的专栏 3056

SIGIR 2026:信息检索前沿技术与投稿指南

信息检索(Information Retrieval, IR)是计算机科学中处理大规模数据检索的核心技术,其基本原理是通过算法模型从海量数据中高效提取相关信息。随着深度学习的发展,神经信息检索(Neural IR)和多模态检索成为研究热点,如稠密检索模型(DPR)和跨模态预训练(CLIP)等技术的应用显著提升了检索精度。这些技术不仅在学术领域具有重要价值,还在电商搜索、医疗检索等实际场景中展现出巨大潜力。SIGIR作为信息检索领域的顶级会议,汇聚了全球顶尖学者和工业界专家,是展示最新研究成果的重要平台。本文

weixin_29189363的博客 291

【Netty】Netty 架构设计(二)

上一篇文章,我们对 Netty做了一个基本的概述,知道什么是Netty以及Netty的简单应用。【Netty】Netty 概述(一)本篇文章我们就来说说Netty的架构设计,解密高并发之道。学习一个框架之前,我们首先要弄懂它的设计原理,然后再进行深层次的分析。接下来我们从三个方面来分析 Netty 的架构设计。正是由于 Netty 的分层架构设计非常合理,基于 Netty 的各种应用服务器和协议栈开发才能够如雨后春笋般得到快速发展。

DreamSun的博客 1806

RT-DERT安卓部署教程

这个文档中的代码案例,实现了拍照和选图识别,两个功能,可以看效果图。UI做了美化:显示类别机和数量。代码下载地址:通过网盘分享的文件:android_rtdert.zip链接: https://pan.baidu.com/s/1k6e-d_lov1OPgUmit5i_cA?pwd=hpfi 提取码: hpfi–来自百度网盘超级会员v8的分享。

AIOT魔法师 196

使用Matlab simulink的PLC coder功能进行算法搭建与处理

使用的案例如下: Anti-Windup PID Control Demonstration with Feedforward Control 将其中的PID块生成SCL语言再导入TIA 使用的平台如下: Matlab 2019b TIA portal 15.1 应用:可以在PLC中搭载一些并不方便使用的,或者比较难使用的算法 步骤如下: 首先打开案例,案例地址我放在最下面,点击Run运行仿真,必...

苦走一口田 1万+

Netty的事件驱动模型

事件循环会不断地轮询注册在其上的 Channel,当发现 Channel 中有新的事件发生时,会将该事件派发给对应的 ChannelHandler 处理。Channel:Channel 表示一个打开的连接,可以进行读写操作。Netty 是一个基于 NIO 的客户端服务器框架,使用事件驱动的模型来实现高效的网络通信。在 Netty 中,所有的 I/O 操作都是异步的,而且都是通过事件来驱动的。通过以上的组件协作,Netty 的事件驱动模型可以高效地处理大量的连接,并且可以支持各种协议的数据交换。

lonely_baby的博客 426

RT-DETR改进策略【卷积层】| CVPR-2020 Strip Pooling 空间池化模块 处理不规则形状的对象 含二次创新

一、本文介绍 本文记录的是利用Strip Pooling模块优化RT-DETR的目标检测网络模型。Strip Pooling结合了长而窄的卷积核形状在一个空间维度上的长程关系捕捉能力和在另一个空间维度上的局部细节捕捉能力,有效地处理复杂的场景信息。这一机制通过采用1×N1×N1×N或N×1N×1N×1的池化核形状来适应不同的图像特征,提高模型对目标形状和分布的适应性。在场景解析网络中,Strip Pooling可以被用于提升对具有长程带状结构或离散分布目标的解析能力,特别是在复杂场景或不同对象布局条件下效果

Limiiiing的博客 580

超详细Netty入门

本文主要讲述Netty框架的一些特性以及重要组件,希望看完之后能对Netty框架有一个比较直观的感受,希望能帮助读者快速入门Netty,减少一些弯路。官方的介绍:Netty isNetty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

ff0123_的博客 1044

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

[Netty实现异步、事件驱动的模型 ]

简直破防了 今天

汤圆的博客 678

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Netty详细介绍

Netty 是一个基于 Java 的异步事件驱动的网络应用程序框架,用于快速开发高性能、高可靠性的网络服务器和客户端。它是由旗下的一位开发者创建的,并以开源的形式发布在上。自从诞生以来,Netty 在网络通信领域取得了广泛的应用,尤其是在高并发和低延迟的场景中表现出色。

Flying_Fish_roe的博客 1589

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【Netty】一.Netty架构设计与Reactor线程模型深度解析

本文深入剖析了Netty的Reactor线程模型架构。首先回顾了从BIO到NIO的网络编程演进历程,指出JDK NIO存在的三大缺陷。然后详细解析了三种Reactor模型及其适用场景,重点阐述了Netty采用的主从Reactor多线程模型。文章通过源码分析展示了Netty中EventLoopGroup与EventLoop的工作机制,包括Channel绑定策略、事件循环流程和ioRatio调优参数。最后对比了Netty如何解决JDK NIO问题,并给出线程数配置的实战建议。全文揭示了Netty高性能背后的设计

hrh的博客 967

Netty模型架构

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。 Netty做了什么? 1.Netty实现了对Java NIO的封装,提供了更方便使用的接口; 2.Netty利用责任链模式实现了ChannelPipeline这一概念,基于ChannelPipeline,可以优雅的实现网络消息的处理(可插拔,解耦); 3.Netty的Reactor线程模型,利...

禾露悠悠摇的博客 1228

Netty(五) Reactor模型与事件驱动

前几章我们了解了nio以及epoll,在我们了解netty之前,我们还需要了解什么是Reactor编程模型,netty则是这个模型的实现. 1.事件驱动 在很久以前我们使用JWT的时候,我首先需要把点击或者滚动事件注册在按钮上,然后当我鼠标点击或者滚动的时候会触发事件,会告诉监听者,这个时候我的监听者就会进行一系列的逻辑操作. 2.观察者设计模式 观察者设计模式其实就是事件驱动,两者其实本质上可以认为是一致的. 简单来说就是我有一个Subject主题...

qq_24768941的博客 1195

Netty入门 -- 什么是Netty?

通俗易懂,一文让你搞懂Netty~

wanghuichen的博客 2331
上一篇: Netty架构设计-事件驱动模型-简洁版
下一篇: Netty解码器深度解析-详细版
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值