Netty聊天室(2):从0开始实战100w级流量应用
目录
- 客户端 Client 登录和响应处理
- 写在前面
- 客户端的会话管理
- 客户端的逻辑构成
- 连接服务器与Session 的创建
- Session和 channel 相互绑定
- AttributeMap接口的使用
- 客户端登录请求
- 处理登录成功的响应
- 写在最后
- 疯狂创客圈 Java 死磕系列
客户端 Client 登录和响应处理
疯狂创客圈 Java 分布式聊天室【 亿级流量】实战系列之 17【 博客园 总入口 】
源码IDEA工程获取链接:Java 聊天室 实战 源码
写在前面
大家好,我是作者尼恩。目前和几个小伙伴一起,组织了一个高并发的实战社群【疯狂创客圈】。正在开始高并发、亿级流程的 IM 聊天程序 学习和实战
前面,已经完成一个高性能的 Java 聊天程序的四件大事:
完成了协议选型,选择了性能更佳的 Protobuf协议。具体的文章为: Netty+Protobuf 整合一:实战案例,带源码
介绍了 通讯消息数据包的几条设计准则。具体的文章为: Netty +Protobuf 整合二:protobuf 消息通讯协议设计的几个准则
解决了一个非常基础的问题,这就是通讯的 粘包和半包问题。具体的文章为:Netty 粘包/半包 全解 | 史上最全解读
前一篇文件,已经完成了 系统三大组成模块的组成介绍。 具体的文章为:Netty聊天程序(实战一):从0开始实战100w级流量应用
今天介绍非常重要的一个内容:
客户端的通讯、登录请求和登录响应设计。
下面,开启今天的 惊险和刺激实战之旅。
客户端的会话管理
什么是会话?
为了方便客户端的开发,管理与服务器的连接,这里引入一个非常重要的中间角色——Session (会话)。有点儿像Web开发中的Tomcat的服务器 Session,但是又有很大的不同。
客户端的会话概念图,如下图所示:
客户端会话有两个很重的成员,一个是user,代表了拥有会话的用户。一个是channel,代表了连接的通道。两个成员的作用是:
通过user,可以获得当前的用户信息
通过channel,可以向服务器发送消息
所以,会话左拥右抱,左手用户资料,右手服务器的连接。在本例的开发中,会经常用到。
客户端的逻辑构成
从逻辑上来说,客户端有三个子的功能模块。
模块一:Handler
入站处理器。
在Netty 中非常重要,负责处理入站消息。比方,服务器发送过来登录响应,服务器发送过来的聊天消息。
模块二:MsgBuilder
消息组装器。
将 Java 内部的 消息 Bean 对象,转成发送出去的 Protobuf 消息。
模块三:Sender
消息发送器。
Handler 负责收的工作。Sender 则是负责将消息发送出去。
三大子模块的类关系图:
介绍完成了主要的组成部分后,开始服务器的连接和Session 的创建。
连接服务器与Session 的创建
通过bootstrap 帮助类,设置完成线程组、通道类型,向管道流水线加入处理器Handler后,就可以开始连接服务器的工作。
本小节需要重点介绍的,是连接成功之后,创建 Session,并且将 Session和 channel 相互绑定。
代码如下:
package com.crazymakercircle.chat.client;
//...
@Data
@Service("EchoClient")
public class ChatClient
{static final Logger LOGGER =LoggerFactory.getLogger(ChatClient.class);//..private Channel channel;private ClientSender sender;public void doConnect(Bootstrap bootstrap, EventLoopGroup eventLoopGroup){ChannelFuture f = null;try{if (bootstrap != null){bootstrap.group(eventLoopGroup);bootstrap.channel(NioSocketChannel.class);bootstrap.option(ChannelOption.SO_KEEPALIVE, true);bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);bootstrap.remoteAddress(host, port);// 设置通道初始化bootstrap.handler(new ChannelInitializer<SocketChannel>(){public void initChannel(SocketChannel ch) throws Exception{ch.pipeline().addLast(new ProtobufDecoder());ch.pipeline().addLast(new ProtobufEncoder());ch.pipeline().addLast(chatClientHandler);}});LOGGER.info(new Date() + "客户端开始登录[疯狂创客圈IM]");f = bootstrap.connect().addListener((ChannelFuture futureListener) ->{final EventLoop eventLoop = futureListener.channel().eventLoop();if (!futureListener.isSuccess()){LOGGER.info("与服务端断开连接!在10s之后准备尝试重连!");eventLoop.schedule(() -> doConnect(new Bootstrap(), eventLoop), 10, TimeUnit.SECONDS);initFalg = false;}else{initFalg = true;}if (initFalg){LOGGER.info("EchoClient客户端连接成功!");LOGGER.info(new Date() + ": 连接成功,启动控制台线程……");channel = futureListener.channel();// 创建会话ClientSession session = new ClientSession(channel);channel.attr(ClientSession.SESSION).set(session);session.setUser(ChatClient.this.getUser());startConsoleThread();}});// 阻塞f.channel().closeFuture().sync();}} catch (Exception e){LOGGER.info("客户端连接失败!" + e.getMessage());}}//...}
Session和 channel 相互绑定
Session和 channel 相互绑定,再截取出来,分析一下。
ClientSession session = new ClientSession(channel);
channel.attr(ClientSession.SESSION).set(session);
session.setUser(ChatClient.this.getUser());
为什么要Session和 channel 相互绑定呢?
- 发的时候, 需要从Session 写入 Channel ,这相当于正向的绑定。
- 收的时候,是从Channel 过来的,需要找到 Session ,这相当于反向的绑定。
Netty 中的 channel ,实现了AttributeMap接口 ,相当于一个 Map容器。 反向的绑定,利用了channel 的这个特点。
看一下AttributeMap接口 如何使用的?
AttributeMap接口的使用
AttributeMap 是一个接口,并且只有一个attr()方法,接收一个AttributeKey类型的key,返回一个Attribute类型的value。按照Javadoc,AttributeMap实现必须是线程安全的。
AttributeMap内部结构看起来像下面这样:
不要被吓着了,其实很简单。
AttributeMap 的使用,主要是设置和取值。
- 设值 Key-> Value
AttributeMap 的设值的方法,举例如下:
channel.attr(ClientSession.SESSION).set(session);
这个是链式调用,attr() 方法中的是 Key, set()方法中的是Value。 这样就完成了 Key-> Value 的设置。
- 取值
AttributeMap 的取值的方法,举例如下:
ClientSession session =ctx.channel().attr(ClientSession.SESSION).get();
这个是链式调用,attr() 方法中的是 Key, get()方法返回 的是Value。 这样就完成了 取值。
关键是,这个key比较特殊。
一般的Map,Key 的类型多半为字符串。但是这里的Key不行,有特殊的约定。
Key的类型必须是 AttributeKey 类型,而且这是一个泛型类,它的优势是,不需要对值进行强制的类型转换。
Key的例子如下:
public static final AttributeKey<ClientSession> SESSION = AttributeKey.valueOf("session");
客户端登录请求
登录的请求,大致如下:
ClientSender的 代码如下:
package com.crazymakercircle.chat.client;@Service("ClientSender")
public class ClientSender
{static final Logger LOGGER = LoggerFactory.getLogger(ClientSender.class);private User user;private ClientSession session;public void sendLoginMsg(){LOGGER.info("开始登陆");ProtoMsg.Message message = LoginMsgBuilder.buildLoginMsg(user);session.writeAndFlush(message);}//...public boolean isLogin(){return session.isLogin();}
}
Sender 首先通过 LoginMsgBuilder,构造一个protobuf 消息。然后调用session发送消息。
session 会通过绑定的channel ,将消息发送出去。
session的代码,如下:
public synchronized void writeAndFlush(Object pkg)
{channel.writeAndFlush(pkg);
}
其他的客户端请求流程,大致也是类似的。
一个客户端的请求大致的流程有三步,分别从Sender 到session到channel。
处理登录成功的响应
这是从服务器过来的入站消息。 如果登录成功,服务器会发送一个登录成功的响应过来。 这个响应,会从channel 传递到Handler。
处理器 LoginResponceHandler 的代码如下:
package com.crazymakercircle.chat.clientHandler;//...public class LoginResponceHandler extends ChannelInboundHandlerAdapter
{static final Logger LOGGER = LoggerFactory.getLogger(LoginResponceHandler.class);/*** 业务逻辑处理*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{LOGGER.info("msg:{}", msg.toString());if (msg != null && msg instanceof ProtoMsg.Message){ProtoMsg.Message pkg = (ProtoMsg.Message) msg;ProtoMsg.LoginResponse info = pkg.getLoginResponse();ProtoInstant.ResultCodeEnum result =ProtoInstant.ResultCodeEnum.values()[info.getCode()];if (result.equals(ProtoInstant.ResultCodeEnum.SUCCESS)){ClientSession session =ctx.channel().attr(ClientSession.SESSION).get();session.setLogin(true);LOGGER.info("登录成功");}}}}
LoginResponceHandler 对消息类型进行判断,如果是请求响应消息,并且登录成功。 则取出绑定的session,通过session,进一步完成登录成功后的业务处理。
比如设置成功的状态,完成一些成功的善后处理操作等等。
其他的客户端响应处理流程,大致也是类似的。
写在最后
至此为止,可以看到,客户端登录的完整流程。
下一篇:服务器的请求处理和通讯的全流程闭环介绍。
疯狂创客圈 Java 死磕系列
Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
- Netty 源码、原理、JAVA NIO 原理
- Java 面试题 一网打尽
疯狂创客圈 【 博客园 总入口 】
转载于:https://www.cnblogs.com/crazymakercircle/p/9992394.html
Netty聊天室(2):从0开始实战100w级流量应用相关推荐
- Netty聊天程序(2):从0开始实战100w级流量应用 - 图解Netty系列
客户端 Client 登录和响应处理 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 17[ 博客园 总入口 ] 文章目录 客户端 Client 登录和响应处理 写在前面 客户端的会话管理 ...
- java实战案例聊天系统_Netty聊天器(实战一):从0开始实战100w级流量应用
Java 聊天程序(百万级流量实战一):系统介绍疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之14 [博客园 总入口] 写在前面 大家好,我是作者尼恩. 前面,已经完成一个高性能的 ...
- java netty聊天室_java架构之路-(netty专题)netty的基本使用和netty聊天室
上次回顾: 上次博客,我们主要说了我们的IO模型,BIO同步阻塞,NIO同步非阻塞,AIO基于NIO二次封装的异步非阻塞,最重要的就是我们的NIO,脑海中应该有NIO的模型图. Netty概念: Ne ...
- java netty聊天室_netty实现消息中心(二)基于netty搭建一个聊天室
前言 上篇博文(netty实现消息中心(一)思路整理 )大概说了下netty websocket消息中心的设计思路,这篇文章主要说说简化版的netty聊天室代码实现,支持群聊和点对点聊天. 此demo ...
- 【开源】SpringBootNetty聊天室V1.2.0升级版本介绍
前言 SpringBoot!微服务微架构的基础,Netty通信框架的元老级别框架,即之前的SpringBoot与Netty的实现聊天室的功能后已经过了不到一周的时间啦,今天我们更新了项目版本从V1.0 ...
- 简单netty聊天室(前端部分)
序 分享一下学习 netty 时练手的聊天室项目,目前已经完成了前端部分的一大半,因此写一篇博客来记录一下. (因为本人是前端白痴,所以只用 jquery 和 bootstrap 等简单工具实现了前端 ...
- 虚拟聊天室(中介者模式实战)
某论坛系统欲增加一个虚拟聊天室,允许论坛会员通过该聊天室进行信息交流,普通会员(CommonMember)可以给其他会员发送文本信息,钻石会员(DiamondMember)既可以给其他会员发送文本信息 ...
- Vue3 PC桌面端聊天室|vue3.0+elementPlus仿微信/QQ界面
vue3-webchat 基于vue3.0.5开发的仿微信|QQ界面桌面端聊天实例. 运用vue3.x全家桶技术+element-plus+v3layer+v3scroll搭建的仿微信/QQ界面网页聊 ...
- 视频教程-Workerman入门到精通实战layIM聊天室-PHP
Workerman入门到精通实战layIM聊天室 多年一线互联网开发实战以及培训经验,对php开发,linux运维架构有丰富的经验,善于分析问题,解决问题. lampol ¥149.00 立即订阅 扫 ...
最新文章
- 近场通讯技术 (1)
- bootstrape实战案例_第二百五十二节,Bootstrap项目实战-首页
- 蓝桥杯2016初赛-生日蜡烛-枚举
- mybatis+spring报错PropertyAccessException 1: org.springframework.beans.MethodInvocationException
- 手机端测试时用的几个软件
- Bootstrap报错:Bootstrap's JavaScript requires jQuery
- 【Elasticsearch】请在64位平台上使用Lucene的MMapDirectory
- 计算机主机配件及图解,电脑主机配件组成解析图文
- pcm系统设计及matlab仿真实现,DOC:25页毕业设计PCM系统设计及MATLAB仿真实现.doc文档优秀范文...
- 华人工程师在美国-从微软高管离职说起
- 电脑文件怎么加密?第一种方法最简单
- 利用python的爬虫技术爬取百度贴吧的帖子
- 【深度好文】python加速库cython简介
- android修改自动背光,android 背光控制
- android程序字体大小,Android 动态调整应用字体大小
- 移动端手机调试的方法
- CNN经典网络模型(四):GoogLeNet简介及代码实现(PyTorch超详细注释版)
- 【ArcGIS教程】专题图制作-人口密度分布图——人口密度分析
- Hadoop 空间不足怎么办?
- matlab毕达哥拉斯质数,“毕达哥拉斯-史仲夏”常用勾股数组表