Netty - 从启蒙到搬砖(完整应用篇)(后端篇)(上)

简介: Netty - 从启蒙到搬砖(完整应用篇)(后端篇)(上)

前端篇:https://lux-sun.blog.csdn.net/article/details/101354215

 

后端篇


本文思路是按顺序进行的,必要说明的地方会唠嗑几句。

0、辅助类

packagecom.wxgj.center.common;
publicclassConst {
publicstaticfinalByteOP_SUCCESS=1;
// NettypublicstaticfinalIntegerINET_PORT=8888;
publicstaticfinalIntegerCONNECT=1;
publicstaticfinalIntegerHEART=2;
publicstaticfinalStringWEBSOCKET_URL="/websocket";
}
packagecom.wxgj.center.netty;
importio.netty.channel.ChannelId;
importio.netty.channel.group.ChannelGroup;
importio.netty.channel.group.DefaultChannelGroup;
importio.netty.util.concurrent.GlobalEventExecutor;
importjava.util.HashMap;
importjava.util.Map;
publicclassGlobal {
// 存储类,以下类是用来存储访问的channle,channelGroup的原型是set集合,保证channle的唯一,如需根据参数标注存储,可以使用currentHashMap来存储publicstaticChannelGroupgroup=newDefaultChannelGroup(GlobalEventExecutor.INSTANCE);
// 用户userId绑定用户自己的频道IDpublicstaticMap<String, Map<ChannelId,Integer>>channelMap=newHashMap<>();
/*** @Author Big Jin* @Description: 删除失效的频道* @Param: [c]* @Return: void* @Create: 2019/7/26 17:04*/publicstaticvoidremoveChannelMapByChannelId(ChannelIdc){
for (Map.Entry<String, Map<ChannelId,Integer>>entry:channelMap.entrySet()){
Map<ChannelId,Integer>channelIdMap=entry.getValue();
if(channelIdMap.get(c)!=null){
channelIdMap.remove(c);
if(channelIdMap.size()==0){
channelMap.remove(entry.getKey());
                }
return;
            }
        }
    }
}
packagecom.wxgj.center.netty;
publicclassSocketRequestBean {
privateIntegeroperand;
privateStringuserId;
publicSocketRequestBean() {
    }
// ... set / get}
packagecom.wxgj.center.netty;
publicclassSocketResultBean {
privateIntegerstatus;
privateStringmsg;
privateStringdata;
publicSocketResultBean() {
    }
// ... set / get}
packagecom.wxgj.center.netty;
importcom.alibaba.fastjson.JSON;
importio.netty.channel.Channel;
importio.netty.channel.ChannelId;
importio.netty.channel.group.ChannelGroup;
importio.netty.channel.group.DefaultChannelGroup;
importio.netty.handler.codec.http.websocketx.TextWebSocketFrame;
importio.netty.util.concurrent.GlobalEventExecutor;
importjava.util.ArrayList;
importjava.util.List;
importjava.util.Map;
publicclassNettyUtil {
/*** @Author Big Jin* @Description: 发送单个用户消息带留言* @Param: [receiverUserId, msg]* @Return: void* @Create: 2019/4/18 17:33*/publicstaticvoidsendMsgOneWithChannelByMsg(StringreceiverUserId, Stringmsg)
    {
SocketResultBeanresult=newSocketResultBean();
result.setMsg(msg);
sendOne(receiverUserId, result);
    }
/*** @Author Big Jin* @Description: 发送多个用户消息带留言* @Param: [receiverUserIdList, msg]* @Return: void* @Create: 2019/4/18 17:33*/publicstaticvoidsendMsgListWithChannelByMsg(List<String>receiverUserIdList, Stringmsg)
    {
SocketResultBeanresult=newSocketResultBean();
result.setMsg(msg);
sendList(receiverUserIdList, result);
    }
/*** @Author Big Jin* @Description: 发送单个用户消息带留言和数据* @Param: [receiverUserId, msg, data]* @Return: void* @Create: 2019/7/26 16:34*/publicstaticvoidsendMsgOneWithChannelByMsgData(StringreceiverUserId, Stringmsg, Stringdata)
    {
SocketResultBeanresult=newSocketResultBean();
result.setMsg(msg);
result.setData(data);
sendOne(receiverUserId, result);
    }
/*** @Author Big Jin* @Description: 发送多个用户消息带留言和数据* @Param: [receiverUserIdList, msg, data]* @Return: void* @Create: 2019/7/26 16:34*/publicstaticvoidsendMsgListWithChannelByMsgData(List<String>receiverUserIdList, Stringmsg, Stringdata)
    {
SocketResultBeanresult=newSocketResultBean();
result.setMsg(msg);
result.setData(data);
sendList(receiverUserIdList, result);
    }
/*** @Author Big Jin* @Description: 发送单个用户* @Param: [receiverUserId, msgResp]* @Return: void* @Create: 2019/4/18 17:34*/publicstaticvoidsendOne(StringreceiverUserId, SocketResultBeanresult)
    {
if(Global.channelMap.get(receiverUserId) !=null) {
Map<ChannelId, Integer>channelIdMap=Global.channelMap.get(receiverUserId);
if(channelIdMap!=null&&channelIdMap.size()>0) {
List<Channel>channelList=newArrayList<>();
for (ChannelIdchannelId:channelIdMap.keySet()) {
Channelch=Global.group.find(channelId);
if(ch!=null) {
channelList.add(ch);
                    }
                }
send(channelList,result);
            }
        }
    }
/*** @Author Big Jin* @Description: 发送多个用户* @Param: [receiverUserIdList, msgResp]* @Return: void* @Create: 2019/4/18 17:34*/publicstaticvoidsendList(List<String>receiverUserIdList, SocketResultBeanresult)
    {
List<Channel>channelList=newArrayList<>();
for(inti=0; i<receiverUserIdList.size(); i++){
StringreceiverUserId=receiverUserIdList.get(i);
if(Global.channelMap.get(receiverUserId) !=null) {
Map<ChannelId, Integer>channelIdMap=Global.channelMap.get(receiverUserId);
if(channelIdMap!=null&&channelIdMap.size()>0) {
for (ChannelIdchannelId:channelIdMap.keySet()) {
Channelch=Global.group.find(channelId);
if(ch!=null) {
channelList.add(ch);
                        }
                    }
                }
            }
        }
send(channelList,result);
    }
/*** @Author Big Jin* @Description: 发送消息* @Param: [receiverUserId, result]* @Return: void* @Create: 2019/7/26 16:38*/privatestaticvoidsend(List<Channel>channelList, SocketResultBeanresult)
    {
result.setStatus(2); // 业务逻辑处理StringresultStr=JSON.toJSONString(result);
TextWebSocketFrametws=newTextWebSocketFrame(resultStr);
if(channelList!=null&&channelList.size()>0) {
ChannelGroupgroup=newDefaultChannelGroup(GlobalEventExecutor.INSTANCE);//群发对象group.addAll(channelList);
group.writeAndFlush(tws);
        }
    }
}

1、项目启动时,启动 Netty 监听器

// web.xml<listener><listener-class>com.wxgj.center.listener.NettyListener</listener-class></listener>
packagecom.wxgj.center.listener;
importcom.wxgj.center.netty.NettyServer;
importjavax.servlet.ServletContextEvent;
importjavax.servlet.ServletContextListener;
publicclassNettyListenerimplementsServletContextListener {
@OverridepublicvoidcontextInitialized(ServletContextEventservletContextEvent) {
NettyServern=newNettyServer();
n.initNetty();
    }
@OverridepublicvoidcontextDestroyed(ServletContextEventservletContextEvent) {
    }
}

2、启动监听器后,就会调用 initNetty 方法

packagecom.wxgj.center.netty;
importcom.wxgj.center.common.Const;
importio.netty.bootstrap.ServerBootstrap;
importio.netty.channel.Channel;
importio.netty.channel.EventLoopGroup;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.nio.NioServerSocketChannel;
publicclassNettyServer {
publicvoidinitNetty() {
newThread() {
publicvoidrun() {
newNettyServer().run();
            }
        }.start();
    }
/*** @Description: NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,*               Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。 在这个例子中我们实现了一个服务端的应用,*               因此会有2个NioEventLoopGroup会被使用。 第一个经常被叫做‘boss’,用来接收进来的连接。*               第二个经常被叫做‘worker’,用来处理已经被接收的连接, 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。*               如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,*               并且可以通过构造函数来配置他们的关系。* @Param: []* @Return: void*/publicvoidrun() {
System.out.println("=======Netty端口启动=======");
// Boss线程:由这个线程池提供的线程是boss种类的,用于创建、连接、绑定socket, (有点像门卫)然后把这些socket传给worker线程池// 在服务器端每个监听的socket都有一个boss线程来处理。在客户端,只有一个boss线程来处理所有的socketEventLoopGroupbossGroup=newNioEventLoopGroup();
// Worker线程:Worker线程执行所有的异步I/O,即处理操作EventLoopGroupworkGroup=newNioEventLoopGroup();
try {
// ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器,并且开始监听端口的socket请求ServerBootstrapb=newServerBootstrap();
// 这一步是必须的,如果没有设置group将会报java.lang.IllegalStateException: group not set异常b.group(bossGroup, workGroup);
// 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类b.channel(NioServerSocketChannel.class);
// ChildChannelHandler 对出入的数据进行的业务操作,其继承ChannelInitializerb.childHandler(newChildChannelHandler());
System.out.println("服务端开启等待客户端连接...");
// 绑定端口并启动去接收进来的连接Channelch=b.bind(Const.INET_PORT).sync().channel();
// 这里会一直等待,直到socket被关闭ch.closeFuture().sync();
        }
catch (Exceptione) {
e.printStackTrace();
        }
finally {
// 关闭bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
        }
    }
}

3、当执行到 b.childHandler(new ChildChannelHandler()); 时候,触发该类,配置一些必要的参数

packagecom.wxgj.center.netty;
importio.netty.channel.ChannelInitializer;
importio.netty.channel.socket.SocketChannel;
importio.netty.handler.codec.http.HttpObjectAggregator;
importio.netty.handler.codec.http.HttpServerCodec;
importio.netty.handler.stream.ChunkedWriteHandler;
importio.netty.handler.timeout.IdleStateHandler;
publicclassChildChannelHandlerextendsChannelInitializer<SocketChannel> {
/*** @Description: 设置频道特性* @Param: [e]* @Return: void*/@OverrideprotectedvoidinitChannel(SocketChannele) throwsException {
// 设置300秒没有读到数据,则触发一个READER_IDLE事件。e.pipeline().addLast(newIdleStateHandler(300, 0, 0));
// HttpServerCodec:将请求和应答消息解码为HTTP消息e.pipeline().addLast("http-codec",newHttpServerCodec());
// HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息e.pipeline().addLast("aggregator",newHttpObjectAggregator(65536));
// ChunkedWriteHandler:向客户端发送HTML5文件e.pipeline().addLast("http-chunked",newChunkedWriteHandler());
// 在管道中添加我们自己的接收数据实现方法e.pipeline().addLast("handler",newMyWebSocketServerHandler());
    }
}

4、当执行到 e.pipeline().addLast(new IdleStateHandler(300, 0, 0)); 时,过了 300s 没有读取到数据,则触发下面这个函数 userEventTriggered

packagecom.wxgj.center.netty;
importio.netty.channel.ChannelHandlerContext;
importio.netty.channel.SimpleChannelInboundHandler;
importio.netty.handler.codec.http.FullHttpRequest;
importio.netty.handler.codec.http.websocketx.WebSocketFrame;
importio.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
importjava.util.logging.Logger;
publicclassMyWebSocketServerHandlerextendsSimpleChannelInboundHandler<Object> {
privatestaticfinalLoggerlogger=Logger.getLogger(WebSocketServerHandshaker.class.getName());
privateWebSocketServerHandshakerhandshaker=null;
/*** @Author Big Jin* @Description: 读写空闲调用* @Param: [ctx, evt]* @Return: void* @Create: 2019/9/24 10:11*/@OverridepublicvoiduserEventTriggered(ChannelHandlerContextctx, Objectevt) throwsException {
//System.out.println("------------------------userEventTriggered------------------------");super.userEventTriggered(ctx, evt);
    }
// ... 下面再补充完整}
目录
相关文章
|
11月前
|
Java 关系型数据库 API
探索后端技术:构建高效、可靠的服务器端应用
在当今数字化时代,后端技术是任何成功应用程序的基石。它涉及服务器、数据库和应用程序之间的交互,处理数据存储、业务逻辑和系统性能等关键任务。本文将深入探讨后端开发的核心概念、常见技术栈及其实际应用,帮助读者更好地理解和掌握构建高效、可靠后端系统的技巧与策略。
|
11月前
|
监控 中间件 Java
后端技术:构建高效、稳定的服务器端应用
【10月更文挑战第5天】后端技术:构建高效、稳定的服务器端应用
344 0
|
11月前
|
监控 关系型数据库 Serverless
探索后端技术:构建高效、可靠的服务器端应用
本文将深入探讨后端开发的核心概念和关键技术,从服务器架构到数据库管理,再到安全防护,为读者提供全面的后端技术指南。无论是初学者还是经验丰富的开发者,都能从中汲取灵感,提升自己的技术水平。
|
11月前
|
JavaScript 前端开发 API
探索后端技术:Node.js的优势和实际应用
【10月更文挑战第6天】 在当今数字化时代,后端开发是任何成功软件应用的关键组成部分。本文将深入探讨一种流行的后端技术——Node.js,通过分析其核心优势和实际应用案例,揭示其在现代软件开发中的重要性和潜力。
538 2
|
11月前
|
设计模式 算法 搜索推荐
后端开发中的设计模式应用与实践
在软件开发的广袤天地中,后端技术如同构筑高楼大厦的钢筋水泥,支撑起整个应用程序的骨架。本文旨在通过深入浅出的方式,探讨后端开发领域内不可或缺的设计模式,这些模式犹如精雕细琢的工具箱,能够助力开发者打造出既健壮又灵活的系统架构。从单例模式到工厂模式,从观察者模式到策略模式,每一种设计模式都蕴含着深刻的哲理与实践价值,它们不仅仅是代码的组织方式,更是解决复杂问题的智慧结晶。
|
11月前
|
算法 安全 关系型数据库
后端技术在现代软件开发中的重要性与应用
本文将深入探讨后端技术在现代软件开发中的关键作用及其广泛应用。我们将从后端开发的基本概念入手,逐步解析其在构建高性能、可扩展和安全的软件系统中的核心地位。通过具体案例,展示不同后端技术如何满足各种复杂业务需求,从而帮助企业实现数字化转型。最后,文章还将探讨未来后端技术的发展趋势,为开发者提供前瞻性的指导。
|
12月前
|
前端开发 JavaScript API
后端技术在现代软件开发中的应用与挑战
本文将深入探讨后端技术在当前软件开发中的重要性及其面临的主要挑战。通过分析后端技术的发展脉络,揭示其在数据处理、业务逻辑和系统安全等方面的关键作用。同时,本文还将讨论如何在快速变化的技术环境中保持后端技术的先进性和竞争力。
160 6
|
12月前
|
设计模式 算法 搜索推荐
后端开发中的设计模式应用
在软件开发的浩瀚海洋中,设计模式犹如一座座灯塔,为后端开发者指引方向。本文将深入探讨后端开发中常见的设计模式,并通过实例展示如何在实际项目中巧妙应用这些模式,以提升代码的可维护性、扩展性和复用性。通过阅读本文,您将能够更加自信地应对复杂后端系统的设计与实现挑战。
189 3
|
11月前
|
存储 安全 关系型数据库
后端技术深度剖析:构建高效稳定的企业级应用
【10月更文挑战第5天】后端技术深度剖析:构建高效稳定的企业级应用
230 0
|
10月前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。

热门文章

最新文章