netty实现消息群发
netty是什么
我所理解的netty,是一个比较底层的网络编程的框架,它和tomcat的区别是什么呢?就是tomcat是一个已经封装好的容器,你可以直接使用,而netty是可以写出像tomcat这种容器的。而且tomcat支持的网络协议是http,但是使用netty,可以写出支持任何协议的容易。
当然,由于所学还不够深入,暂时使用netty实现简单的功能。网上很多大神对netty的分析都很深入很到位,优点也说了很多,简单来说,使用netty可以自己搭建并实现一套高性能的服务端通讯服务,自由灵活,可扩展性好,并能支撑较好的高并发性能,国内许多知名的互联网大厂,在一些通讯组件的底层上,选用的就是netty,比如阿里的,dubbo和rocketMq,读过源码的同学应该知道就是使用netty进行封装的,所以效率才会那么高;
实例分析
关于netty的一些基本概念和里面的一些常用组件的了解,建议可以先查阅相关资料进行学习,比如netty的编程模型,各种handler的理解,以及客户端与服务端建立连接时的channel的生命周期等概念,都是学习netty必要的基础知识;
下面有这样一个简单的需求,也是实际生活中会遇到的:用netty实现一个群聊的功能,每个用户上线和下线都能检测到,每个用户都可以发消息,每个用户发出去的消息都能被群里的其他用户接收到,下面用代码来简单实现一下,
显而易见,既然是群聊,必然有服务端和客户端,由于是后端代码,没有页面,我们将直接通过控制台发消息模拟效果即可,
服务端代码,在上一篇中我们简单分析了netty的编程特点,3个步骤,初始化服务端配置,加上一个自定义的handler即可,
1、服务端配置
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;/*** 服务端基本参数配置*/
public class ChatServer {private int port;public ChatServer(int port) {this.port = port;}public void startServer() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChatServerInitializer()).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);System.out.println("Server 启动");ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();System.out.println("Server 关闭");}}public static void main(String[] args) throws Exception {int port;if (args.length > 0) {port = Integer.parseInt(args[0]);} else {port = 2333;}new ChatServer(port).startServer();}}
2、服务端自定义ChatServerInitializer
这部分的代码格式比较固定,可以这样理解,在客户端和服务端进行消息通讯的时候,两端都需要对进入channel的消息做一些前置的处理,比如编解码的操作,格式化输入输出校验,在netty中是通过诸如pipeline.addLast的代码实现的,也就是java中的一个责任链模式的具体实现,即把你需要的handler(处理具体某一种功能的代码块)加入到处理链中使其生效,比如这个里面最后添加了我们自定义的ChatServerHandler
public class ChatServerInitializer extends ChannelInitializer<SocketChannel> {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//以"\n"或者"\r\n"作为分隔符pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast("decoder", new StringDecoder());pipeline.addLast("encoder", new StringEncoder());pipeline.addLast("handler", new ChatServerHandler());System.out.println("ChatClient:" + ch.remoteAddress() + " channel_id :" + ch.id() + " 连接上");}}
3、服务端自定义ChatServerHandler
ChatServerHandler是一个很重要的类,netty监控channel的生命周期就是在这个类里面完成的,下图可以看到,继承了SimpleChannelInboundHandler这个类之后有很多方法,在这些方法中,可以理解为netty管理具体的channel的生命周期的方法,可以根据业务的需要对这些方法进行重写,
见名知意,比如说channelRegistered()这个方法,可以理解为当channel刚注册的时候方法被触发;channelActive()当某个客户端连接进来的时候被触发;exceptionCaught(),当channel出现异常的时候被触发,不同的回调方法都有其特定的用途,在实际生产中都是可以派上用场的,
要特别说明的是,继承了上述的SimpleChannelInboundHandler这个类之后,有一个方法必须被复写,在4.X版本中,我这里显示的是messageReceived(),但在有一些版本中是channelRead(),但大家在学习的时候,一样的理解,里面的参数和功能是一样的,本人已经验证过了;
理解了上述的概念,下面直接贴上handler的代码,具体的使用都有备注
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;public class ChatServerHandler extends SimpleChannelInboundHandler<String> {/*** 管理channel的组,可以理解为channel的池*/public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);// 客户端的 Channel 存入 ChannelGroup列表中,并通知列表中的其他客户端 Channel@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {Channel currchannel = ctx.channel();channels.writeAndFlush("[服务器] - " + currchannel.remoteAddress() + " channel_id :" + currchannel.id() + " 加入\n");channels.add(ctx.channel());}//每当从服务端收到客户端断开时,客户端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客户端 Channel@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {Channel currchannel = ctx.channel();channels.writeAndFlush("[服务器] - " + currchannel.remoteAddress() + " 离开\n");}//每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel。// 其中如果你使用的是 Netty 5.x 版本时,需要把 channelRead0() 重命名为messageReceived()@Overrideprotected void messageReceived(ChannelHandlerContext channelHandlerContext, String msg) {Channel incoming = channelHandlerContext.channel();for (Channel channel : channels) {//遍历ChannelGroup中的channelif (channel != incoming){//找到加入到ChannelGroup中的channel后,将录入的信息回写给除去发送信息的客户端channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + msg + "\n");}else {channel.writeAndFlush("[服务器消息]" + msg + "\n");}}}//服务端监听到客户端活动@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)Channel incoming = ctx.channel();System.out.println("服务器:" + incoming.remoteAddress() + "上线");}//服务端监听到客户端不活动@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)Channel incoming = ctx.channel();System.out.println("服务器:" + incoming.remoteAddress() + "掉线");}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {Channel incoming = ctx.channel();System.out.println("服务器:" + incoming.remoteAddress() + "异常");ctx.close();}}
4、客户端配置
客户端的基本连接配置和服务端差不多,不过这里的不再是ServerBootstrap,因为它是服务端专用的,这里将会变成Bootstrap,其他的都差不多,绑定端口,加入自定义的handler即可,
public class SimpleChatClient {public static void main(String[] args) throws Exception{new SimpleChatClient("localhost", 2333).run();}private final String host;private final int port;public SimpleChatClient(String host, int port){this.host = host;this.port = port;}public void run() throws Exception{EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap().group(group).channel(NioSocketChannel.class).handler(new ChatClientInitializer());Channel channel = bootstrap.connect(host, port).sync().channel();//录入信息BufferedReader in = new BufferedReader(new InputStreamReader(System.in));while(true){//死循环,模拟不断向服务端发送消息channel.writeAndFlush(in.readLine() + "\r\n");}} catch (Exception e) {e.printStackTrace();} finally {group.shutdownGracefully();}}
}
5、客户端ChatClientInitializer
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;public class ChatClientInitializer extends ChannelInitializer<SocketChannel> {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//使用netty自带的string类型的编解码器pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast("decoder", new StringDecoder());pipeline.addLast("encoder", new StringEncoder());pipeline.addLast("handler", new ChatClientHandler());}
}
6、客户端自定义handler
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class ChatClientHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void messageReceived(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {//客户端读取服务端发送回来的消息,只是进行显示System.out.println(msg);}}
代码的部分基本上就是这些,下面我们运行一下程序看一下效果,首先启动服务端,
启动第一个客户端,客户端启动完毕,在服务端的控制台打印如下信息,说明channel的相关回调方法生效了,
再启动一个客户端,有两个客户端的连接了,同时第一个客户端收到了一条消息,即被通知另一个客户端上线了,
某个客户端的控制台输入一条消息,由于服务端是转发,因此在另一个客户端那边,收到了hello world 这条消息,
到这里,我们用一段简单的程序演示了netty模拟消息群发的效果,感兴趣的同学可以继续做深入的研究,本篇到此结束,最后感谢观看!
netty实现消息群发相关推荐
- netty 实现消息群发
主程序启动模版 package net.jlxxw.wechat.temp;import io.netty.bootstrap.ServerBootstrap; import io.netty.cha ...
- java 微信群发多图文_[Java教程]httpClient实现微信公众号消息群发
[Java教程]httpClient实现微信公众号消息群发 0 2016-09-21 20:00:10 1.实现功能 向关注了微信公众号的微信用户群发消息.(可以是所有的用户,也可以是提供了微信ope ...
- 微信公众号开发 [03] 结合UEditor实现图文消息群发功能
0.写在前面的话 如何实现微信平台后台管理中的,图文消息发送功能? 大概的过程如下: 通过类似表单的形式,将文章各部分内容提交到后台,封装成一个实体类,并持久化到数据库中 需要推送的时候,将不同的文章 ...
- 公众平台模板消息所在行业_第三方工具微信公众号模板消息群发如何操作?
当下,公众平台模板消息功能仅支持添加模板,修改所在行业,如果想要群发模板消息,可以自己根据公众平台的接口编程实现,也可通过微号帮平台的模板消息群发功能实现,均可以让微信公众号群发模板消息,模板消息即按 ...
- 公众平台模板消息所在行业_微信公众号模板消息群发第三方平台可以免费试用吗?...
使用公众号后台模板消息功能,只能添加模板和修改所在行业,如果想要群发模板消息,可以通过公众号后台的接口自己编程实现,也可使用第三方平台微号帮功能模板消息群发,都可以实现微信公众号模板消息群发,突破公众 ...
- SpringBoot 集成 WebSocket 实现消息群发推送
一. 什么是 WebSocket WebSocket 是一种全新的协议.它将 TCP 的 Socket(套接字)应用在了web page上,从而使通信双方建立起一个保持在活动状态的连接通道,并且属于全 ...
- 公众号客服消息超过48小时_免费模板消息群发的方法,在这里!
不知道大家会不会因为服务号4次推送机会用完,又遇到老板施压,让发布重要消息,而苦恼? 经过我苦心搜索,找到了一个解决方法!原理就是,利用公众号模板消息,给粉丝推送. 作为运营同学,大多是不会技术的,所 ...
- 使用模板消息及利用模板消息群发的说明
下周就是国庆节,不知道你公众号的群发次数用完了吗?如果用完了,还可以使用模板消息群发对全部粉丝推送,仅限服务号使用.订阅号依然每天只能用群发1次,开发公众号高级群发接口,不会开发的人员可以直接使用微号 ...
- java BIO tcp服务端向客户端消息群发代码教程实战
前言 项目需要和第三方厂商的服务需要用TCP协议通讯,考虑到彼此双方可能都会有断网重连.宕机重启的情况,需要保证 发生上述情况后,服务之间能够自动实现重新通信.研究测试之后整理如下代码实现.因为发现客 ...
最新文章
- 阿里内核月报2015年03月
- linux安装python库报错pywin32,Linux windows安装paramiko模块
- sshd修改端口后几次启动失败
- 正则表达式: 正向预查和负向预查
- 裂墙推荐!IntelliJ IDEA 常用插件一览,让效率成为习惯
- 前端开发 什么是网页 什么是html
- Adding Keyword And Description meta tags to each page by inheritence
- 谷歌免费GPU训练星际2AI好难?你需要份debug指南
- PHP中的simplehtmldom学习
- 【房价预测】基于matlab Elman神经网络开盘价预测【含Matlab源码 708期】
- abaqus帮助文档翻译,中英对照
- 远程桌面 无法打开连接文件default.rdp
- 使用SpreadJS迅速开发一套属于自己的欧洲杯赛程小工具
- Spring入门学习
- JMeter基础系列(八) JMeter断言之JSON断言
- 适用于Webstorm的25个最佳Javascript插件
- 【实习_面试全程辅导分享】简历篇
- 会玩会生活!兴趣标签体系的背后方案是......
- vue 验证是否数字类型_vue.js如何判断输入的是不是数字
- 【笔记】小米电视屏蔽广告好评,亲测方法及相关设备参数