Netty聊天室(2):从0开始实战100w级流量应用 (2)

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内部结构看起来像下面这样:

img

不要被吓着了,其实很简单。

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); /** * 业务逻辑处理 */ @Override public 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("登录成功"); } } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfzyp.html