Netty学习

Netty+SpringBoot+FastDFS+Html5实现聊天App,项目介绍:https://segmentfault.com/a/11...

Netty+SpringBoot+FastDFS+Html5实现聊天App,项目github链接:https://github.com/ShimmerPig...

本章练习完整代码链接:https://github.com/ShimmerPig...

IO编程与NIO编程

传统IO编程性能分析

IO编程模型在客户端较少的情况下运行良好,但是对于客户端比较多的业务来说,单机服务端可能需要支撑成千上万的连接,IO模型可能就不太合适了。这是因为在传统的IO模型中,每个连接创建成功之后都需要一个线程来维护,每个线程包含一个while死循环,那么1w个连接对应1w个线程,继而1w个while死循环,这就带来如下几个问题:

1.线程资源受限:线程是操作系统中非常宝贵的资源,同一时刻有大量的线程处于阻塞状态是非常严重的资源浪费,操作系统耗不起。

2.线程切换效率低下:单机cpu核数固定,线程爆炸之后操作系统频繁进行线程切换,应用性能急剧下降。

3.除了以上两个问题,IO编程中,我们看到数据读写是以字节流为单位,效率不高。

为了解决这三个问题,JDK在1.4之后提出了NIO。下面简单描述一下NIO是如何解决以上三个问题的。

线程资源受限

NIO编程模型中,新来一个连接不再创建一个新的线程,而是可以把这条连接直接绑定到某个固定的线程,然后这条连接所有的读写都由这个线程来负责。
这个过程的实现归功于NIO模型中selector的作用,一条连接来了之后,现在不创建一个while死循环去监听是否有数据可读了,而是直接把这条连接注册到selector上,然后,通过检查这个selector,就可以批量监测出有数据可读的连接,进而读取数据。

线程切换效率低下

由于NIO模型中线程数量大大降低,线程切换效率因此也大幅度提高。

IO读写以字节为单位

NIO解决这个问题的方式是数据读写不再以字节为单位,而是以字节块为单位。IO模型中,每次都是从操作系统底层一个字节一个字节地读取数据,而NIO维护一个缓冲区,每次可以从这个缓冲区里面读取一块的数据。

hello netty

完整代码链接:https://github.com/ShimmerPig...

首先定义一对线程组——主线程bossGroup与从线程workerGroup。
bossGroup——用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事。
workerGroup——bossGroup会将任务丢给他,让workerGroup去处理。

//主线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
//从线程
EventLoopGroup workerGroup = new NioEventLoopGroup();

定义服务端的启动类serverBootstrap,需要设置主从线程,NIO的双向通道,与子处理器(用于处理workerGroup),这里的子处理器后面我们会手动创建。

// netty服务器的创建, ServerBootstrap 是一个启动类ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup)            // 设置主从线程组.channel(NioServerSocketChannel.class)    // 设置nio的双向通道.childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup

启动服务端,绑定8088端口,同时设置启动的方式为同步的,这样我们的Netty就会一直等待,直到该端口启动完毕。

ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

监听关闭的通道channel,设置为同步方式。

channelFuture.channel().closeFuture().sync();

将两个线程优雅地关闭。

bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

创建管道channel的子处理器HelloServerInitializer,用于处理workerGroup。
HelloServerInitializer里面只重写了initChannel方法,是一个初始化器,channel注册后,会执行里面相应的初始化方法。
在initChannel方法中通过SocketChannel获得对应的管道,通过该管道添加相关助手类handler。
HttpServerCodec是由netty自己提供的助手类,可以理解为拦截器,当请求到服务端,我们需要做解码,响应到客户端做编码。
添加自定义的助手类customHandler,返回"hello netty~"

ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("HttpServerCodec", new HttpServerCodec());
pipeline.addLast("customHandler", new CustomHandler());

创建自定义的助手类CustomHandler继承SimpleChannelInboundHandler,返回hello netty~
重写channelRead0方法,首先通过传入的上下文对象ChannelHandlerContext获取channel,若消息类型为http请求,则构建一个内容为"hello netty~"的http响应,通过上下文对象的writeAndFlush方法将响应刷到客户端。

if (msg instanceof HttpRequest) {// 显示客户端的远程地址System.out.println(channel.remoteAddress());// 定义发送的数据消息ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8);// 构建一个http responseFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);// 为响应增加数据类型和长度response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());// 把响应刷到客户端ctx.writeAndFlush(response);
}

访问8088端口,返回"hello netty~"

netty聊天小练习

完整代码链接:https://github.com/ShimmerPig...

服务器

定义主从线程与服务端的启动类

public class WSServer {public static void main(String[] args) throws Exception {EventLoopGroup mainGroup = new NioEventLoopGroup();EventLoopGroup subGroup = new NioEventLoopGroup();try {ServerBootstrap server = new ServerBootstrap();server.group(mainGroup, subGroup).channel(NioServerSocketChannel.class).childHandler(new WSServerInitialzer());ChannelFuture future = server.bind(8088).sync();future.channel().closeFuture().sync();} finally {mainGroup.shutdownGracefully();subGroup.shutdownGracefully();}}}

创建channel的子处理器WSServerInitialzer
加入相关的助手类handler

public class WSServerInitialzer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();// websocket 基于http协议,所以要有http编解码器pipeline.addLast(new HttpServerCodec());// 对写大数据流的支持 pipeline.addLast(new ChunkedWriteHandler());// 对httpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse// 几乎在netty中的编程,都会使用到此hanlerpipeline.addLast(new HttpObjectAggregator(1024*64));// ====================== 以上是用于支持http协议    ======================// ====================== 以下是支持httpWebsocket ======================/*** websocket 服务器处理的协议,用于指定给客户端连接访问的路由 : /ws* 本handler会帮你处理一些繁重的复杂的事* 会帮你处理握手动作: handshaking(close, ping, pong) ping + pong = 心跳* 对于websocket来讲,都是以frames进行传输的,不同的数据类型对应的frames也不同*/pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));// 自定义的handlerpipeline.addLast(new ChatHandler());}}

创建自定义的助手类ChatHandler,用于处理消息。
TextWebSocketFrame:在netty中,是用于为websocket专门处理文本的对象,frame是消息的载体。
创建管道组ChannelGroup,用于管理所有客户端的管道channel。

private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

重写channelRead0方法,通过传入的TextWebSocketFrame获取客户端传入的内容。通过循环的方法对ChannelGroup中所有的channel进行回复。

@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg)
throws Exception {// 获取客户端传输过来的消息String content = msg.text();System.out.println("接受到的数据:" + content);//        for (Channel channel: clients) {
//            channel.writeAndFlush(
//                new TextWebSocketFrame(
//                        "[服务器在]" + LocalDateTime.now()
//                        + "接受到消息, 消息为:" + content));
//        }// 下面这个方法,和上面的for循环,一致clients.writeAndFlush(new TextWebSocketFrame("[服务器在]" + LocalDateTime.now() + "接受到消息, 消息为:" + content));}

重写handlerAdded方法,当客户端连接服务端之后(打开连接),获取客户端的channle,并且放到ChannelGroup中去进行管理。

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {clients.add(ctx.channel());
}

重写handlerRemoved方法,当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel。

@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {// 当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel//        clients.remove(ctx.channel());System.out.println("客户端断开,channle对应的长id为:" + ctx.channel().id().asLongText());System.out.println("客户端断开,channle对应的短id为:" + ctx.channel().id().asShortText());
}

客户端

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title></head><body><div>发送消息:</div><input type="text" id="msgContent"/><input type="button" value="点我发送" onclick="CHAT.chat()"/><div>接受消息:</div><div id="receiveMsg" style="background-color: gainsboro;"></div><script type="application/javascript">window.CHAT = {socket: null,init: function() {if (window.WebSocket) {CHAT.socket = new WebSocket("ws://192.168.1.4:8088/ws");CHAT.socket.onopen = function() {console.log("连接建立成功...");},CHAT.socket.onclose = function() {console.log("连接关闭...");},CHAT.socket.onerror = function() {console.log("发生错误...");},CHAT.socket.onmessage = function(e) {console.log("接受到消息:" + e.data);var receiveMsg = document.getElementById("receiveMsg");var html = receiveMsg.innerHTML;receiveMsg.innerHTML = html + "<br/>" + e.data;}} else {alert("浏览器不支持websocket协议...");}},chat: function() {var msg = document.getElementById("msgContent");CHAT.socket.send(msg.value);}};CHAT.init();</script></body>
</html>

测试

Netty+SpringBoot+FastDFS+Html5实现聊天App详解(一)相关推荐

  1. Netty+SpringBoot+FastDFS+Html5实现聊天App详解(四)

    Netty+SpringBoot+FastDFS+Html5实现聊天App,项目介绍. Netty+SpringBoot+FastDFS+Html5实现聊天App,项目github链接. 本章完整代码 ...

  2. Netty+SpringBoot+FastDFS+Html5实现聊天App

    Netty+SpringBoot+FastDFS+Html5实现聊天App github链接: https://github.com/ShimmerPig... 已将前端代码上传至github,修改a ...

  3. pomelo分布式聊天服务器详解

    pomelo分布式聊天服务器详解 2014-01-05 11:43:49|  分类: node |  标签:pomelo  pomelo聊天  nodejs分布式聊天  pomelo分布式  |举报| ...

  4. 视频教程-HTML5+CSS3项目实战详解-HTML5/CSS

    HTML5+CSS3项目实战详解 13年软件开发经验,设计开发30多个大型软件,涉及政府.银行.电信.能源等大型软件项目. 精通J2EE体系架构,熟练使用Struts.Spring.hibernate ...

  5. 《HTML5网页开发实例详解》连载(四)HTML5中的FileSystem接口

    HTML 5除了提供用于获取文件信息的File对象外,还添加了FileSystem相关的应用接口.FileSystem对于不同的处理功能做了细致的分类,如用于文件读取和处理的FileReader和Fi ...

  6. SpringBoot使用AOP,PointCut表达式详解以及使用

    SpringBoot使用AOP,PointCut表达式详解以及使用 1.相关注解 2.PointCut 表达式详解 2.1 execution: 2.1 within: 2.3. this: 2.4. ...

  7. canvas插件_HTML系列之-HTML5新元素之Canvas详解

    课程简介: 课程目标:通过本课程学习,掌握HTML5中图形绘制canvas的基本原理和使用,并利用canvas解决实际相关问题. 适用人群:具有一定html.css.javascript开发基础的人员 ...

  8. php仿苹果,关于8个超炫酷仿苹果应用的HTML5动画的图文详解

    苹果的产品一直以精美的UI著称,无论是软件应用还是硬件设备.本文主要分享了8个很不错的HTML5动画应用,这些动画正式模仿了苹果的各类应用,有焦点图.钟表.菜单等HTML5应用和jQuery插件,大家 ...

  9. html5 调用手机摄像头详解

    html5 调用手机摄像头详解   首先,我们看看HTML代码结构,当然,这部分的DOM内容应该是在用户允许使用其摄像头事件出发后,动态加载生成的.  注意: 我们采用的是 640X480的分辨率,如 ...

最新文章

  1. Linux系统管理必备知识之查看系统用户和用户组
  2. NSD WINDOWS--2014.8.11
  3. 推荐几个BAT大佬的公众号
  4. UA MATH567 高维统计I 概率不等式1 Hoeffding不等式与Chernoff不等式
  5. IoC-spring 的灵魂(带你轻松理解IOC思想及bean对象的生成过程)
  6. python源码精要(2)-C代码规范
  7. C程序范例(2)——学生管理系统”链表“实现
  8. 下载eclipse出现a java_java - 运行eclipse出现问题?
  9. 远程办公的 33 种预测
  10. markdown使用markdown-viewer生成目录_谷歌浏览器查看m文件
  11. Eclipse Maven编译报不支持muti-catch
  12. redis-实现排行榜
  13. 【备忘】mysql优化工具
  14. 十道经典javaWeb面试题
  15. 图像 YUV与RGB格式转换
  16. 微信公众平台如何审核
  17. aruba交换机配置命令_Aruba 无线交换机基本操作命令
  18. java applet 打印_applet 打印常见问题与解决方法
  19. linux-文件和目录
  20. hexo 博客小功能添加-评论、萌妹纸、相册、字数统计...

热门文章

  1. 排序算法——直接插入排序
  2. linux服务器nvidia驱动的安装与卸载
  3. html代码中本地路径里斜杠 / 和反斜杠 \ 的区别
  4. zip解压mysql安装图解_Mysql安装教程-zip格式压缩包
  5. Leetcode题库203.移除链表元素(尾指针填充 / 虚头指针定义 c实现)
  6. [BUUCTF-pwn]——pwnable_orw   (ORW)
  7. [BUUCTF-pwn]——jarvisoj_level3_x64
  8. 关于FragmentPager实现Fragment的滑动切换
  9. java数据结构排序实验报告_java数据结构与算法之插入排序详解
  10. Java NIO、NIO.2学习笔记