Netty学习笔记(一) - 简介和组件设计
在互联网发达的今天,网络已经深入到生活的方方面面,一个高效、性能可靠的网络通信已经成为一个重要的诉求,在Java方面需要寻求一种高性能网络编程的实践。
一、简介
当前JDK(本文使用的JDK 1.8)中已经有网络编程相关的API,使用过程中或多或少会存在以下几个问题:
- 阻塞:早期JDK里的API是用阻塞式的实现方式,在读写数据调用时数据还没有准备好,或者目前不可写,操作就会被阻塞直到数据准备好或目标可写为止。虽然可以采用每一个连接创建一个线程进行处理,但是可能会造成大量线程得不到释放,消耗资源。从JDK 1.4开始提供非阻塞的实现。
- 处理和调度IO烦琐:偏底层的API实现暴露了更多的与业务无关的操作细节,使得在高负载下实现一个可靠和高效的逻辑就变得复杂和烦琐。
Netty是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。它拥有简单而强大的设计模型,易于使用,拥有比Java API更高的性能等特点,它屏蔽了底层实现的细节,使开发人员更关注业务逻辑的实现。
二、组件和设计
- Channel:屏蔽底层网络传输细节,提供简单易用的诸如bind、connect、read、write方法。
- EventLoop:线程模型。处理连接生命周期过程中发生的事件,以及其他一些任务。
- ChannelFuture:异步接口,用于注册Listener以便在某个操作完成时得到通知。
- ChannelHandler:处理入站和出站数据的的一系列接口和抽象类,开发人员扩展这些类型来完成业务逻辑。
- ChannelPipline:管理ChannelHandler的容器,将多个ChannelHandler以链式的方式管理,数据将在这个链上依次流动并被ChannelHandler逐个处理。
- 引导(Bootstrap、ServerBootstrap):初始化客户端和服务端的入口类。
三、一个简单的Demo
创建一个maven工程,引入Netty。为了方便调试,Demo中引入了日志和junit5。
<!-- pom.xml --> <dependencies>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency> <dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
创建Client和Server
package com.niklai.demo; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; public class Client {
private static final Logger logger = LoggerFactory.getLogger(Client.class.getSimpleName()); public static void init() {
try {
Bootstrap bootstrap = new Bootstrap(); // 初始化客户端引导
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group) // 指定适用于NIO的EventLoop
.channel(NioSocketChannel.class) // 适用于NIO的Channel
.remoteAddress(new InetSocketAddress("localhost", 9999)) // 指定要绑定的IP和端口
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ClientHandler()); // 添加ChannelHandler到ChannelPipline
}
});
ChannelFuture future = bootstrap.connect().sync(); // 阻塞直到连接到远程节点
future.channel().closeFuture().sync(); // 阻塞直到关闭Channel
group.shutdownGracefully().sync(); // 释放资源
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
} static class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("channel active...."); String msg = "Client message!";
logger.info("send message: {}....", msg);
ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
logger.info("read message: {}....", buf.toString(CharsetUtil.UTF_8));
}
}
}
package com.niklai.demo; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; public class Server {
private static final Logger logger = LoggerFactory.getLogger(Server.class.getSimpleName()); public static void init() {
try {
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 初始化客户端引导
NioEventLoopGroup group = new NioEventLoopGroup();
serverBootstrap.group(group) // 指定适用于NIO的EventLoop
.channel(NioServerSocketChannel.class) // 适用于NIO的Channel
.localAddress(new InetSocketAddress("localhost", 9999)) // 指定要绑定的IP和端口
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ServerHandler()); // 添加ChannelHandler到ChannelPipline
}
}); ChannelFuture future = serverBootstrap.bind().sync(); // 异步绑定阻塞直到完成
future.channel().closeFuture().sync(); // 阻塞直到关闭Channel
group.shutdownGracefully().sync(); // 释放资源
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
} static class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("channel active.....");
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
logger.info("read message: {}.....", buf.toString(CharsetUtil.UTF_8));
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
logger.info("read complete.....");
ctx.writeAndFlush(Unpooled.copiedBuffer("receive message!", CharsetUtil.UTF_8))
.addListener(ChannelFutureListener.CLOSE);
}
}
}
日志配置文件
<?xml version="1.0" encoding="UTF-8"?> <configuration>
<!-- 定义控制台输出 -->
<appender name="consoleOut" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date %level [%thread] %class#%method [%file:%line] %msg%n</pattern>
</encoder>
</appender> <root level="info">
<appender-ref ref="consoleOut" />
</root>
</configuration>
logback.xml
单元测试代码
package com.niklai.demo; import org.junit.jupiter.api.Test; public class NettyTest { @Test
public void test1() throws InterruptedException {
new Thread(() -> {
// 服务端
Server.init();
}).start();
Thread.sleep(1000); // 客户端
Client.init(); Thread.sleep(5000);
}
}
运行结果如下图
从控制台日志中可以看到当Client连接到Server后, ServerHandler 和 ClientHandler 的 channerActive 方法都会被调用, ClientHandler 会调用 ctx.writeAndFlush() 方法给Server发送一条消息, ServerHandler 的 channelRead 方法被调用读取到消息,消息读取完毕后 channelReadComplete 方法被调用,发送应答消息给Client, ClientHandler 的 channelRead 方法被调用获取到应答消息。到此一个完整的发送--应答流程就结束了。
Netty学习笔记(一) - 简介和组件设计的更多相关文章
- Netty学习笔记-入门版
目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...
- C#学习笔记——面向对象、面向组件以及类型基础
C#学习笔记——面向对象.面向组件以及类型基础 目录 一 面向对象与面向组件 二 基元类型与 new 操作 三 值类型与引用类型 四 类型转换 五 相等性与同一性 六 对象哈希码 一 面向对象与面向组 ...
- Linux内核学习笔记-1.简介和入门
原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- React学习笔记 - JSX简介
React Learn Note 2 React学习笔记(二) 标签(空格分隔): React JavaScript 一.JSX简介 像const element = <h1>Hello ...
- amazeui学习笔记--css(常用组件16)--文章页Article
amazeui学习笔记--css(常用组件16)--文章页Article 一.总结 1.基本使用:文章内容页的排版样式,包括标题.文章元信息.分隔线等样式. .am-article 文章内容容器 .a ...
- UML和模式应用学习笔记-1(面向对象分析和设计)
UML和模式应用学习笔记-1(面向对象分析和设计) 而只是对情节的记录:此处的用例场景为:游戏者请求掷骰子.系统展示结果:如果骰子的总点数是7,则游戏者赢得游戏,否则为输 (2)定义领域模型:在领域模 ...
- Netty学习笔记(二) 实现服务端和客户端
在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- amazeui学习笔记--css(常用组件15)--CSS动画Animation
amazeui学习笔记--css(常用组件15)--CSS动画Animation 一.总结 1.css3动画封装:CSS3 动画封装,浏览器需支持 CSS3 动画. Class 描述 .am-anim ...
- amazeui学习笔记--css(常用组件14)--缩略图Thumbnail
amazeui学习笔记--css(常用组件14)--缩略图Thumbnail 一.总结 1.基本样式:在 <img> 添加 .am-thumbnail 类:也可以在 <img> ...
随机推荐
- 学习HTML
前端三剑客:HTML.CSS.JavaScript.现在就开始学习HTML啦. 学习资源:http://www.freecodecamp.cn/ 学习笔记: h1,head1,一级标题 <h1& ...
- html之表单元素form
表单元素form \(都是嵌套input标签来实现,其中type属性选择不同则不同\) \(text:文本内容(输入内容可见)\) \(password:密码(输入内容不可见)\) \(submit: ...
- print函数的全面认识
# 输出打印 数字 print(123) a = 100 print(a) # 输出打印 字符串 print('字符串123') print('''锄禾日当午 汗滴禾下土''') # 输出打印 列表 ...
- 11_JavaScript基础入门(1)
JavaScript简介 1.JavaScript用途 前端三层: 结构层 HTML(从语义的角度描述页面的结构) 样式层 CSS(从审美的角度装饰页面) 行为层 JavaScript (从交互 ...
- 深入理解CSS定位—浮动模型
前面我们讲到了绝对定位,在这篇文章中,我们将讲到3种定位模型中的浮动模型.主要参考 张鑫旭在慕课网的 深入理解float 那些年我们一起清过的浮动---by 一丝丝凉 精通CSS 注意:第二小节基本参 ...
- Web_python_template_injection
0x01 pthon模板注入 判断是否为模板注入 paload http://124.126.19.106:34164/{{1+1}} //如果里面的值被执行了,那么存在模板注入 //调用os模块的p ...
- 智能制造:数字化协同技术在BIW焊装产线的应用
随着汽车工业的发展,如何利用数字化技术提高整车制造水平,已经成为各厂商亟待解决的问题.通过数字化工厂系统的应用使得白车身整车项目前期工艺设计.生产线规划质量有了显著提升,数字化工厂已经成为现代焊装生产 ...
- Dotnet core使用JWT认证授权最佳实践(二)
最近,团队的小伙伴们在做项目时,需要用到JWT认证.遂根据自己的经验,整理成了这篇文章,用来帮助理清JWT认证的原理和代码编写操作. 第一部分:Dotnet core使用JWT认证授权最佳实践(一) ...
- 计算python内部数据结构时间效率-源代码
#Author:qinjiaxi '''本程序计算各种循环的时间效率''' from timeit import Timer def test1(n): li = [] for i in range( ...
- angular2 + bootstrap +jquery 实例
一.Create a project process: 1.use Angular CLI to create an Angular Project "demo": need th ...