SpringBoot+SpringCloud+Nutty打造分布式在线消息推送服务-实例

前言

其实关于这个的话,我先前的几篇博文: SpringBoot+Netty+Vue+Websocket实现在线推送/聊天系统

实用水文篇--SpringBoot整合Netty实现消息推送服务器

其实已经说的非常明白了,但是每想到后台还是有人找我要代码,因为完整的代码其实在博文里面都是贴出来了的,当然前面的博文我都是给一个类似于脚手架的东西,没有给出实际的案例。那么今天也是直接给出代码的案例吧,这里再次声明,所有的代码都是在这两篇博文里面有的,如果做了修改在本文当中会给出提示。此外的话,我们的项目是完全开源的,但是在开发阶段不开源,因为有一些铭感信息。在开发完成之后开源,这里请见谅,当然也是为什么我没有办法给出源码的原因(暂时)也是为什么你可以在我的那两篇博文看到完整的代码信息的原因。

Tips: 在我的频道只会告诉你怎么做饭,不会把饭菜做好了再给你,如果需要,那是另外“价格”。

那么废话不多说,我们开始。

技术架构

我们今天来看到我们的一个案例。首先是我们的技术架构:

那么在我们今天的话是这样的:

  1. 用户上传博文
  2. 博文通过之后发送审核消息给前端
  3. 前端对消息进行展示

效果图

这个是我们上传博文,博文通过之后会看到消息有个提示。

之后到具体的页面可以看到消息

因为图片是中午截取的,有个小bug没有调整,但是不要在意这些,这个bug是很简单的,因为一开始我这边做过初始化,没有清除缓存罢了。

后端项目

之后的话来看到我们的后端的一个服务情况。

我们要进行互动的服务就是这三个,一个是网关,一个是博文的服务,还有就是我们的消息服务器。因为我们是用户上传成功后再显示的。

那么关于博客的模块的话在这块有完整的说明: SpringBoot + Vue实现博文上传+展示+博文列表

我们这边做的就是一个系列。当然只是演示实际上,你用先前我给出的代码是完全可以实现效果的,我们这边只是说用那一套代码来真正做我们具体的业务。

消息数据定义

存储结构

那么在开始之前的话,我们这边对我们的消息服务器设计了对应的数据库用来存储消息。

这一块看你自己,我们这边肯定是要的。

那么我们这次的案例需要使用到的表是这个表:

消息状态

之后的话,我们需要对我们的消息进行定义。 我们在这里对消息的状态做出如下定义:

  1. 消息具备两者状态,针对两个情况
  2. 签收状态,即,服务器确定用户在线,并且将消息传输到了客户端,为签收状态。
  3. 阅读状态,在保证已签收的情况下,用户是否已经阅读消息,这部分的逻辑有客户端代码处理。
  4. 对应未签收的消息,用户上线时,请求服务器是否存在未签收的消息,如果有,进行统一读取,存储到本地
  5. 对于未读消息,主要是对用户的状态进行一个判断,消息已经缓存到用户本地。

那么此时的话,我们就已经说清楚了这个。在我们的数据库里面status这个字段就是用来判断用户是不是签收了消息的。至于用户到底有没有读取消息,那么完全就是客户端需要做的判断了。

当然你也可以设计为全部由服务端来处理。

Nutty消息服务

项目结构

ok,说完了这个的话,我们再来看到我们的消息服务端是怎么处理的。

首先我们依然是和先前的博文一样,保留了先前的东西。 但是我们这边多了Controller,和Dao层。

那么在这边的话,我们需要关注的只有这几个东西:

这几个东西就是我们来实现前面的效果的实际的业务代码。

除了这些当然还有我们的Dao,但是这个是根据你的业务来的,这里我就不展示了,类比嘛。

改动

那么说完了这些,我们来看到和先前的代码有哪些改动的东西。

消息bean

首先是我们的消息的改动。

@AllArgsConstructor
@NoArgsConstructor
@ToString
/**
 * 由于我们这边Nutty处理的消息只有注册,所以话这里只需要
 * 保留action和userid即可
 * */
public class DataContent implements Serializable {
    private Integer action;
    private String userid;
}

复制代码

那么我们的消息的类型是这样的:

public enum MessageActionEnum {

    //定义消息类型

    CONNECT(1,"第一次(或重连)初始化连接"),
    CHAT(2,"聊天消息"),
    SIGNED(3,"消息签收"),
    KEEPALIVE(4,"客户端保持心跳"),
    PULL_FRIEND(5, "拉取好友"),
    HOLEADUITMSG(6,"审核消息");


    public final Integer type;
    public final String content;
    MessageActionEnum(Integer type,String content) {
        this.type = type;
        this.content = content;
    }

}
复制代码

消息处理器

既然我们的这个消息类型变了,那么我们的这个代码也变了:

@Component
@ChannelHandler.Sharable

public class ServerListenerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static final Logger log = LoggerFactory.getLogger(ServerBoot.class);
    static {
        //先初始化出来
        UserConnectPool.getChannelMap();
        UserConnectPool.getChannelGroup();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String content = msg.text();
        /**获取客户端传过来的消息*/
        DataContent dataContent = JsonUtils.jsonToPojo(content, DataContent.class);
        assert dataContent != null;
        Integer action = dataContent.getAction();
        Channel channel =  ctx.channel();
        /**
         * 根据消息类型对其进行处理,我们这里只做两个事情
         * 1. 注册用户
         * 2. 心跳在线
         * */
        if(Objects.equals(action, MessageActionEnum.CONNECT.type)){
            /**
             * 2.1 当websocket 第一次 open 的时候,
             * 初始化channel,把用的 channel 和 userid 关联起来
             * */
            String userid = dataContent.getUserid();
            AttributeKey<String> key = AttributeKey.valueOf("userId");
            ctx.channel().attr(key).setIfAbsent(userid);
            UserConnectPool.getChannelMap().put(userid,channel);
            UserConnectPool.output();

   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值