1. Netty模型

Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型有多个Reactor。

简版图:

说明:

  1. BossGroup线程维护Selector,只关注Accept
  2. 当接收到Accept事件,获取到对应的SocketChannel,封装成NIOSocketChannel并注册到Worker线程(事件循环),并进行维护
  3. 当Worker线程监听到selector中通道发生自己感兴趣的事件后,就进行处理(就由handler),注意,handler已经加入到channel

Netty模型进阶版图:

详细版:

模型说明:

  1. Netty抽象出2组线程池:

    • BossGroup:专门接受客户端的连接
    • WorkerGroup:专门负责网络的读写
  2. BossGroup和WorkerGroup类型都是NioEventLoopGroup
  3. NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
  4. NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
  5. NioEventLoop可以有多个线程,即可以含有多个NioEventLoop
  6. 每个Boss NioEventLoop循环执行的步骤有3步:
    • 轮询accept事件
    • 处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个worker
    • 处理任务队列的任务,即runAllTasks
  7. 每个Worker NIOEventLoop循环执行的步骤:
    • 轮询read,write事件
    • 处理I/O事件,即read,write事件,在对应NioSocketChannel处理
    • 处理任务队列的任务,即runAllTasks
  8. 每个Worker NIOEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应channel,管道中维护了很多的处理器

2. Netty实现简单服务端与客户端交互

NettyServer.java

package com.tang.netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { public static void main(String[] args) {
// 创建 BossGroup 和 WorkerGroup
// 1.创建 2 个线程组 boosGroup 和 workerGroup
// 2.bossGroup只是处理连接请求,真正与客户端的业务处理,会交给workerGroup
// 3.两个都是无限循环
// 4.bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数,默认为实际 CPU 核数*2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap(); // 使用链式编程来进行设置
bootstrap.group(bossGroup, workerGroup) // 设置2个线程组
.channel(NioServerSocketChannel.class) // 使用 NioServerSocketChannel 作为服务器通道的实现
.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接数
.childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道测试对象(匿名对象)
// 给pipeline 设置处理器
// 使用的自己实现的 NettyServerHandler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
}); // 给我们 workerGroup 的 EventLoop 对应的管道设置处理器 System.out.println("....Server is ready..."); try {
// 绑定一个端口并且同步,生成了一个 ChannelFuture 对象
// 启动服务器
ChannelFuture cf = bootstrap.bind(6668).sync(); // 对关闭通道进行监听
cf.channel().closeFuture().sync(); } catch (InterruptedException e){
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
} }
}

NettyServerHandler.java

package com.tang.netty.simple;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.util.CharsetUtil; import java.nio.ByteBuffer; /**
* 我们自定义一个Handler 需要继承netty 规定好的某个 HandlerAdapter(规范)
* 这个 ChannelInboundHandlerAdapter 是 Pipeline 里的 ChannelHandler 的一个实现
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter { // 读取数据(这里我们可以读取客户端发送的消息) /**
1.ChannelHandlerContext ctx:上下文对象,含有管道 pipeline,通道 channel,地址
2.Object msg:就是客户端发送的数据,默认Object
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
System.out.println("服务器读取线程 " + Thread.currentThread().getName());
System.out.println("server ctx = " + ctx);
System.out.println("查看 channel 与 pipeline 的关系");
Channel channel = ctx.channel();
ChannelPipeline pipeline = ctx.pipeline(); // 本质是一个双向链表,出战入站 // 将 msg 转为 ByteBuf
// ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送的消息是:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + channel.remoteAddress()); } // 数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{ // writeAndFlush 是 write + flush
// 将数据写入到缓冲,并刷新
// 一般讲,我们对这个发送的数据进行编码
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,客户端~ 收到", CharsetUtil.UTF_8));
} // 处理异常,一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}

NettyClient.java

package com.tang.netty.simple;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; public class NettyClient { public static void main(String[] args) {
// 客户端需要一个事件循环组
EventLoopGroup eventExecutors = new NioEventLoopGroup(); try{
// 创建客户端启动对象
// 注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
Bootstrap bootstrap = new Bootstrap(); // 设置相关参数
bootstrap.group(eventExecutors) // 设置线程组
.channel(NioSocketChannel.class) // 设置客户端Channel的实现类
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler()); // 加入自己的处理器
}
}); System.out.println("客户端 ok..."); // 启动客户端去连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); // 给关闭通道进行监听
channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e){
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
} }
}

NettyClientHandler.java

package com.tang.netty.simple;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil; public class NettyClientHandler extends ChannelInboundHandlerAdapter { // 当 Channel 就绪时,就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client " + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello, server, this is client", CharsetUtil.UTF_8)); } // 当 Channel 有读取事件时,会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

------------------------------------

此 Netty 笔记为学习尚硅谷韩老师讲的 Netty 整理完成,原视频讲解十分详细,建议对 Netty 框架感兴趣的同学们可以看一遍原视频:

https://www.bilibili.com/video/BV1DJ411m7NR

Netty(三)Netty模型的更多相关文章

  1. Netty IO线程模型学习总结

    Netty框架的 主要线程是IO线程.线程模型的好坏直接决定了系统的吞吐量.并发性和安全性. Netty的线程模型遵循了Reactor的基础线程模型.以下我们先一起看下该模型 Reactor线程模型 ...

  2. Netty Reactor 线程模型笔记

    引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...

  3. netty之==线程模型

    1.1 netty线程模型本质遵循了Reactor的基础线程模型,所以得先介绍Reactor模型  1.2  Reactor模型 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模 ...

  4. 面试官:Netty的线程模型可不只是主从多Reactor这么简单

    笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...

  5. Netty服务器线程模型概览

    一切从ServerBootstrap开始 ServerBootstrap负责初始话netty服务器,并且开始监听端口的socket请求. bootstrap bootstrap =newServerB ...

  6. SDN三种模型解析

    数十年前,计算机科学家兼网络作家Andrew S. Tanenbaum讽刺标准过多难以选择,当然现在也是如此,比如软件定义网络模型的数量也很多.但是在考虑部署软件定义网络(SDN)或者试点之前,首先需 ...

  7. Javascript事件模型系列(一)事件及事件的三种模型

    一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...

  8. Atitit.web三编程模型 Web Page Web Forms 和 MVC

    Atitit.web三编程模型 Web Page    Web Forms 和 MVC 1. 编程模型是 Web Forms 和 MVC (Model, View, Controller). 2. W ...

  9. NLP —— 图模型(零):EM算法简述及简单示例(三硬币模型)

    最近接触了pLSA模型,该模型需要使用期望最大化(Expectation Maximization)算法求解. 本文简述了以下内容: 为什么需要EM算法 EM算法的推导与流程 EM算法的收敛性定理 使 ...

  10. PHP.23-ThinkPHP框架的三种模型实例化-(D()方法与M()方法的区别)

    三种模型实例化 原则上:每个数据表应对应一个模型类(Home/Model/GoodsModel.class.php --> 表tp_goods) 1.直接实例化 和实例化其他类库一样实例化模型类 ...

随机推荐

  1. 开发日记:中控PUSH协议

    using System; using System.IO; using System.Net; using System.Text.RegularExpressions; namespace Con ...

  2. 9.按需创建PV和PVC并使用

    官方文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-stor ...

  3. XYCTF pwn部分题解 (部分题目详解)

    hello_world(签到) 思路: 这道题就是利用printf函数泄露libc的基地址,然后再次进行栈溢出通过system,/bin/sh来获取shell wp: invisible_flag 思 ...

  4. 【简说Python WEB】Flask应用的单元测试

    [简说Python WEB]Flask应用的单元测试 tests/test_basics.py import unittest from flask import current_app from a ...

  5. 算法~PBKDF2-SHA让密码更安全

    摘要:在当今的数字世界中,密码安全是至关重要的.为了保护用户密码免受未经授权的访问和破解,Password-Based Key Derivation Function 2 (PBKDF2)算法成为了一 ...

  6. SHELL脚本获取域名对应的IP地址

    单个获取 编写角本pingip.sh #!/bin/sh ADDR=qq.com TMPSTR=`ping ${ADDR} -c 1 | sed '1{s/[^(]*(//;s/).*//;q}'` ...

  7. 数据转换2-无人机航拍倾斜摄影转换成OSGB格式

    首先软件的下载和安装参考下面链接 http://www.xue51.com/soft/53013.html 0.首先打开软件,要打开2个哦. 打数据处理开后台 ContextCapture Engin ...

  8. postgresql 创建索引

    --查询索引 select * from pg_indexes where tablename='tab1'; --创建索引(查询用到哪几列,就对哪几个字段创建索引) CREATE INDEX ind ...

  9. 字节面试:说说Java中的锁机制?

    Java 中的锁(Locking)机制主要是为了解决多线程环境下,对共享资源并发访问时的同步和互斥控制,以确保共享资源的安全访问. 锁的作用主要体现在以下几个方面: 互斥访问:确保在任何时刻,只有一个 ...

  10. Java JVM——1.JVM与Java体系结构

    前言 作为Java工程师的你曾被伤害过吗?你是否也遇到过这些问题? ✘ 运行着的线上系统突然卡死,系统无法访问,甚至直接OOMM! ✘ 想解决线上JVM GC问题,但却无从下手. ✘ 新项目上线,对各 ...