一、需求

需要实现直播间的以下功能:

  • 群发消息(文本、图片、推荐商品)
  • 点对点私发消息(文本、图片、推荐商品)
  • 单个用户禁言
  • 全体用户禁言
  • 撤回消息
  • 聊天记录持久化

二、技术实现

服务端消息中心采用netty实现,

微站、小程序使用websocket与消息中心通信,

安卓端使用netty与消息中心通信。

服务器端每过一定时间会给客户端推送一条ping消息,客户端收到ping消息后回复pong消息,通过心跳验证存活客户端,定时断开未回复pong消息的链接,剔除服务端连接会话信息。

客户端因网络等问题断开链接后,客户端需要实现定时重连机制,先设定断开后每5秒尝试一次重连,这个时间后续可能会修改。

服务端链接地址:ws://{ip}:{端口}/websocket?liveId=123&userId=00b084ea98e24e80a7f8be3c4b8a64d0

liveId为直播id,userId为用户id

1.消息格式

消息为json格式字符串,有如下属性:

字段名 类型 含义 客户端是否必填
id string uuid,服务端生成 不填
liveId string 直播id 必填
code int 系统消息类型 必填
type int 业务消息类型 业务消息必填,心跳消息不填
msg string 消息内容 必填,心跳消息不填
sendUserId string 发送人用户id 必填
sendUserName string 发送人用户名 不填,服务器端返回
sendUserHeadImg string 发送人头像 不填,服务器端返回
receiveUserId string[] 接收人用户id数组 code为群发时无需填写,私聊需要填写
sendTime string 发送时间,服务端生成,格式:yyyy-MM-dd HH:mm:ss 不填
ext string 扩展信息 选填

消息code定义

code 含义
1 点对点
2 群发
3 ping消息,服务器端心跳发送到客户端
4 pong消息,客户端收到ping消息后回应pong消息

消息type定义

type 含义
1001 普通文本消息
1002 图片消息
1003 推荐商品消息
1004 单个用户禁言消息
1005 全体用户禁言消息
1006 撤回用户发言消息
1007 打赏消息

pong消息示例:

{

"code":4,

"liveId":"asfasda",

"sendUserId":"sfasdasdasd"

}

a.普通文本消息示例:

{

"id":"fasdasdasd",

"liveId":"fasdasdas",

"code":2,

"msg":"你好啊",

"sendUserId":"这是发送人的用户id",

"sendUserName":"asfasdas",

"sendUserHeadImg":"fasdasdasdsad.jpg",

"receiveUserId":"这是接收人的用户id",

"type":1001,

"sendTime":133548798798,

"ext":"{}"

}

b.带表情的普通文本消息示例:

{

"id":"fasdasdasd",

"liveId":"fasdasdas",

"code":10001,

"msg":"你好啊[微笑]",

"sendUserId":"这是发送人的用户id",

"sendUserName":"asfasdas",

"sendUserHeadImg":"fasdasdasdsad.jpg",

"receiveUserId":"这是接收人的用户id",

"type":1,

"sendTime":133548798798,

"ext":"{}"

}

备注:[微笑]为微笑表情图片的字符串标识,客户端收到这条消息后需要把表情标识替换为图片显示到聊天窗口。

c.图片消息示例:

先把图片文件进行客户端压缩,尽量控制到1M以内,然后调用上传图片接口得到图片相对路径,如果上传成功,组装websocket消息把路径放入msg:

{

"id":"fasdasd",

"liveId":"fasdasdas",

"code":2,

"msg":"/static/img/chat/2020-02-11/xxx.jpg",

"sendUserId":"这是发送人的用户id",

"sendUserName":"asfasdas",

"sendUserHeadImg":"fasdasdasdsad.jpg",

"receiveUserId":"这是接收人的用户id",

"type":1002,

"sendTime":133548798798,

"ext":"{}"

}

d.推荐商品消息示例:

推荐商品的msg字段为商品信息json

{

"id":"asfasdsdads",

"liveId":"fasdasdas",

"code":2,

"msg":"{"productId":"asdasfas","productType":1,"originPrice":100,"currentPrice":100,"productName":"sfasd", "coverImg":"asfasdasd.jpg"}",

"sendUserId":"这是发送人的用户id",

"sendUserName":"asfasdas",

"sendUserHeadImg":"fasdasdasdsad.jpg",

"receiveUserId":"这是接收人的用户id",

"type":1003,

"sendTime":133548798798

}

e.打赏消息示例:

{

"id":"fasdasd",

"liveId":"fasdasdas",

"code":2,

"msg":"/static/img/chat/2020-02-11/xxx.jpg",

"sendUserId":"这是发送人的用户id",

"receiveUserId":"",

"type":1007,

"sendTime":133548798798,

"ext":"{"name":"被打赏用户的用户名", "price":100}"

}

f.单用户禁言消息示例:

{

“id”:”fasdasd”,

“liveId”:”fasdasdas”,

“code”:2,

“msg”:”被禁言用户的id”,

“sendUserId”:”这是发送人的用户id”,

“receiveUserId”:”这是接收人的用户id”,

“type”:1004,

“sendTime”:133548798798,

“ext”:"{'name':'被禁言的用户名'}”

}

f.单用户取消禁言消息示例:

{

“id”:”fasdasd”,

“liveId”:”fasdasdas”,

“code”:2,

“msg”:”被取消禁言用户的id”,

“sendUserId”:”这是发送人的用户id”,

“receiveUserId”:”这是接收人的用户id”,

“type”:1010,

“sendTime”:133548798798,

“ext”:"{'name':'被禁言的用户名'}”

}

f.撤回消息示例:

{

“id”:”fasdasd”,

“liveId”:”fasdasdas”,

“code”:2,

“msg”:”被撤销的消息id”,

“sendUserId”:”这是发送人的用户id”,

“receiveUserId”:”这是接收人的用户id”,

“type”:1006,

“sendTime”:133548798798,

“ext”:””

}

2.消息交互流程

接收消息步骤:

a.当用户打开直播介绍页面时,向注册中心发起建立连接请求,监听消息中心推送过来的消息。

b.当消息中心推送过来的消息触发了监听事件函数,判断type是普通文本消息、系统消息、推荐商品消息其中的某一种,然后执行对应的逻辑处理与展示。

发送消息步骤:

a.当用户打开直播介绍页面时,向注册中心发起建立连接请求,监听消息中心推送过来的消息。

b.如果是普通聊天文本消息或系统消息按约定好的消息格式组装好消息json,如果是图片消息则先调用图片上传接口得到url并把url组装到消息json,调用websocket或者netty sdk向消息中心发送消息。

撤回消息步骤:

a.助教端、ibos点击撤回时,组装一条type为撤回、code为群发类型、msg字段为需要撤回的消息id的消息,发送到消息中心。

b.消息中心收到这条消息后,将mongodb中的消息状态改为撤回,并广播撤回消息到所有客户端。

c.被广播的客户端收到这条消息后,按消息id隐藏对应的消息。

单个用户禁言步骤

a.助教端、ibos点击用户禁言时,组装一条type为禁言、code为点对点私聊类型、msg字段为需要禁言的用户id的消息,发送到消息中心。

b.消息中心收到这条消息后,将用户的禁言状态持久化到数据库,并在缓存中记录直播和用户的禁言关系。如果这个用户的客户端还保持会话连接,点对点推送这个被禁言用户的客户端。

c.被禁言用户客户端收到消息后,文本框禁用并提示用户已被禁言。

备注:

当用户刷新页面时,会从数据库中获取到最新的禁言状态并禁用/启用文本框。极端情况用户发出消息,消息经过消息中心时会查询一下缓存中是否有直播用户禁言关系,有的话该消息不予推送。

取消单个用户禁言的交互步骤和以上步骤相同,只是消息的type为取消禁言。

全体用户禁言步骤

a.助教端、ibos点击全体用户禁言时,将直播的禁言状态持久化到数据库,并在缓存中记录直播的禁言状态。组装一条type为全体禁言、code为广播的消息,发送到消息中心。

b.消息中心将消息广播到观看该直播的所有客户端。

c.客户端收到消息后,文本框禁用。

备注:

当用户刷新页面时,会从数据库中获取到最新的直播禁言状态并禁用/启用文本框。极端情况用户发出消息,消息经过消息中心时会查询一下缓存中是否有直播禁言状态,有的话该消息不予推送。

取消全体用户禁言的交互步骤和以上步骤相同,只是消息的type为取消全体禁言。

3.消息存储

在消息中心所在服务器的本地内存加一个队列作为缓冲区,经过消息中心的聊天记录会追加到缓冲区。开启异步任务定时检查缓冲区是否达到阈值,达到缓冲区阈值后批量存储到mongodb聊天记录表。

mongodb表结构

字段名 含义
id 主键
zbId 直播id
msgId 消息id
sendUserId 发送人id
receiveUserId 接收人id,多个逗号分隔
sendTime 发送时间
code 消息发送类型
type 消息类型
msg 消息内容
ext 消息扩展内容
createTime 保存时间

4.鉴权

在消息中心对用户的发言状态做验证。

5.扩展

如果需要支持热部署和扩容,需要解决如下的坑:

1.消息中心的客户端会话管理代码需要改造成分布式会话管理,例如使用redis做存储,但是连接数过大时网络请求传输的数据量也会增大,不妥。

2.集群需要解决负载均衡问题,目前只能做客户端负载均衡,无法像nginx转发http请求一样实现服务端负载均衡。

3.目前市面上没有成熟标准的解决以上问题的方案,需要自己结合一些零散的思路做一些尝试。

网上找到的消息服务集群思路:

如果从自己编程方面考虑socket集群,那么是有困难的。告诉你一个我曾使用过的架构模型。

1、HTTP服务做集群。

2、socket服务器启用后直接访问HTTP服务,主动告知有一个新的socket服务,socket服务状态用中间缓存层保存,具体服务状态可以使用HTTP心跳轮询检测,此部分为socket服务的主动发现、装载服务、卸载服务。

3、客户端请求HTTP服务,HTTP服务分析保存在其上的各个socket服务的存活状态和负载情况,然后返回给客户端最优的socket服务地址。

4、客户端获得最优负载的socket服务地址后连接对应的socket服务。

5、各服务之间的数据交换,可以添加一台socket服务作为socket服务的中转站,这种方式不太可靠,强依赖于中转服务的存活状态。

6、各socket服务的数据必须能保证全局共享,用于客户端之间数据的共通性,使用户在感知上就像完全连接在一台socket服务之上。

下一篇为netty实现消息推送的代码实现:netty实现消息中心(二)基于netty搭建一个聊天室

netty实现消息中心(一)思路整理的更多相关文章

  1. netty实现消息中心(二)基于netty搭建一个聊天室

    前言 上篇博文(netty实现消息中心(一)思路整理 )大概说了下netty websocket消息中心的设计思路,这篇文章主要说说简化版的netty聊天室代码实现,支持群聊和点对点聊天. 此demo ...

  2. html5引擎开发 -- 引擎消息中心和有限状态机 - 初步整理 一

    一 什么是有限状态机        FSM (finite-state machine),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型.他对于逻辑以及 ...

  3. 用代码打开通知中心(statusbar、通知栏、消息中心)

    我想用代码来打开android的消息中心,也叫做statusbar.通知栏.通知栏其实就是一个常驻的服务,至于原理这里就不多说了,简单说下思路和问题. 思路:API中没有实现的方法,那么就利用反射机制 ...

  4. 基于 SOA 概念 RPC 框架 的 消息中心 云部署 设计 漫谈

    一.背景 假设有一个系统的最大并发量有2000TPS左右.同时该系统有闲时和忙时,希望可以随时进行拓展和削减服务能力,以节省服务器费用开销. 该系统能提供站内消息.短信.app消息.邮箱的一个消息系统 ...

  5. eureka源码--服务的注册、服务续约、服务发现、服务下线、服务剔除、定时任务以及自定义注册中心的思路

    微服务注册后,在注册中心的注册表结构是一个map: ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>& ...

  6. Android 基于Netty的消息推送方案之对象的传递(四)

    在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...

  7. Android 基于Netty的消息推送方案之字符串的接收和发送(三)

    在上一篇文章中<Android 基于Netty的消息推送方案之概念和工作原理(二)> ,我们介绍过一些关于Netty的概念和工作原理的内容,今天我们先来介绍一个叫做ChannelBuffe ...

  8. Android 基于Netty的消息推送方案之概念和工作原理(二)

    上一篇文章中我讲述了关于消息推送的方案以及一个基于Netty实现的一个简单的Hello World,为了更好的理解Hello World中的代码,今天我来讲解一下关于Netty中一些概念和工作原理的内 ...

  9. Angular2发布思路(整理官网Deployment页面)

    本文是按着ng2官网的高级内容“Deployment”的思路整理得出的,原文虽然在angular2的中文站下挂着,截止目前却还是英文版未翻译,笔者就在这里结合自己的理解给出原文的一点点整理.这是原文地 ...

随机推荐

  1. 获取访问用户的客户端IP(适用于公网与局域网).

     /**   * 获取访问用户的客户端IP(适用于公网与局域网).   */   public final String getIpAddr(final HttpServletRequest requ ...

  2. hdu5967数学找规律+逆元

    Detachment Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total ...

  3. 17-4 delete-truncate语句-练习

    --删除数据语句: --delete from 表名 where ... --delete语句如果不加where条件,表示将表中所有的数据都删除,加where条件后,会按照where条件进行删除. - ...

  4. 基本sql语法

    SQL 语句主要可以划分为以下 3 个类别. DDL(Data Definition Languages)语句:数据定义语言,这些语句定义了不同的数据段.数据库.表.列.索引等数据库对象的定义.常用 ...

  5. [PHP学习教程 - 日期/时间]001.月份第一天&最后一天(Month First Day & Last Day)

    引言:在时间处理上,对于前/后台人性化的展示当前月份最大天数,这个是网站必须要处理的一个方面.但通常这一块会由第三方类库直接包装,这里我们做一个简单的Mark. 今天,我们就为大家提供一个函数,获得指 ...

  6. python 验证码处理

    一. 灰度处理,就是把彩色的验证码图片转为灰色的图片. 二值化,是将图片处理为只有黑白两色的图片,利于后面的图像处理和识别 # 自适应阀值二值化 def _get_dynamic_binary_ima ...

  7. $工具, 属性, TAB点击切换

    $工具方法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  8. Linux下db2V10.5命令行安装超详细图文教程(附下载地址)

    下载地址:https://pan.baidu.com/s/1GtF03x1FMF3IsGdSiBJu-g 提取码:8vfj 失效了发邮件:wells974@163.com 一.db2prereqche ...

  9. Centos8 删除了yum.repos.d 下面的文件

    原文: https://www.cnblogs.com/junjind/p/9016107.html centos-release-8.1-1.1911.0.9.el8.x86_64 找到 https ...

  10. Android简单应用程序破解——runtime.apk

    对于<Debugging Android Application>一文中最后附上的练习,我采用了另一种静态方法绕开原有的逻辑去破解.主要的过程如下: 利用apktool将练习的runtim ...