Home > Article > Backend Development > Message system design and implementation, message system design and implementation_PHP tutorial
First, let’s take a look at how messages are implemented in the market.
Jianshu’s messaging system is mainly divided into two types
Short message
The nature of a short message is actually the same as a private message. It is a message sent from a user to a user with specific information content.
Reminder
A reminder is a message sent by the system, its copy format is fixed, and it usually has a hyperlink to a special object.
Zhihu is the same as Jianshu, there are two main types:
Private message
Similar to Jianshu, it can be a message sent by users to users, or it can also be a message sent by administrators to users.
Messages
Zhihu’s messages are even better than Jianshu’s reminders. Zhihu will gather multiple similar messages to reduce users’ reading pressure.
Through a simple analysis of the two products, we can conclude that their messages have two categories. On this basis, we add another one: announcement.
The main nature of the announcement is that the system sends a message with specific content, which can be read by all users on the site.
So, there are three categories of messages:
We take a set of reminder samples from the short book:
Analyze the sentence structure and the content of the reminder is nothing more than
<p>「谁对一样属于谁的事物做了什么操作」<br />「someone do something in someone's something」</p>
someone = the triggerer of the reminder, or the sender, marked as sender
do something = the action of the reminder, comments, likes, and attention all belong to one action, marked as action
something = the action object of the reminder , this is specific to which article it is, marked as target
someone's = the owner of the reminder action object, marked as targetOwner
This is clear. The sender and targetOwner are the users of the website, and the target is the specific article. If the reminder object is not limited to the article, but there are others, you need to add a targetType to mark it. Is the goal an article or something else? The action is fixed. There may be only a few actions that will trigger reminders on the entire website: comment, like, follow... (or other actions that the business needs to remind)
Take Zhihu as an example
Pushing is more common. It is necessary to maintain a list of followers for a certain question. Whenever the conditions for pushing this question are triggered (for example, someone answers the question ), send this notification to each follower.
Pulling is a bit more troublesome, which is the reverse of pushing. For example, each user has a list of concerns. Whenever the user goes online, each issue is polled. When the event list of the issue appears, I originally pulled the information with a large timestamp.
而我们则根据消息的不同分类采用不同的获取方式:
通告和提醒,适合使用拉取的方式,消息产生之后,会存在消息表中,用户在某一特定的时间根据自己关注问题的表进行消息的拉取,然后添加到自己的消息队列中,
信息,适合使用推的方式,在发送者建立一条信息之后,同时指定接收者,把消息添加到接收者的消息队列中。
根据提醒使用拉取的方式,需要维护一个关注某一事物的列表。
这种行为,我们称之为:「订阅」Subscribe
一则订阅有以下三个核心属性:
比如我发布了一篇文章,那么我会订阅文章《XXX》的评论动作,所以文章《XXX》每被人评论了,就需要发送一则提醒告知我。
订阅的规则还可以扩展
我喜欢了一篇文章,和我发布了一篇文章,订阅的动作可能不一样。
喜欢了一篇文章,我希望我订阅这篇文章更新、评论的动作。
而发布了一篇文章,我希望我只是订阅这篇文章的评论动作。
这时候就需要多一个参数:subscribReason
不同的subscribReason,对应着一个动作数组,
subscribReason = 喜欢,对应着 actions = [更新,评论]
subscribReason = 发布,对应着 actions = [评论]
订阅的规则还还可以扩展
用户可能会有一个自己的订阅设置,比如对于所有的喜欢的动作,我都不希望接收。
比如Knewone的提醒设置
所以我们需要再维护一个表:SubscriptionConfig,来存放用户的提醒设置。
并且,当用户没有提醒设置的时候,可以使用系统提供的一套默认设置:defaultSubscriptionConfig
如果我发布了一篇文章《XXX》,在我不在线的时候,被评论了10遍,当我一上线的时候,应该是收到十条信息类似于:「谁谁谁评论了你的文章《XXX》」?
还是应该收到一条信息:「甲、乙、丙、丁...评论了你的文章《XXX》」?
知乎在聚合上做的很优秀,要知道他们要实现这个还是挺有技术的:
知乎的消息机制,在技术上如何设计与规划?
网站的消息(通知)系统一般是如何实现的?
关于这部分功能,我们还没有具体的实现方法,暂时也无法讲得更加详细。⊙﹏⊙
通过上面的分析,大概知道做这个消息系统,需要哪些实体类:
说了这么多,整理一下整个消息流程的一些行为:
<code class="javascript">id : {type: <span class="hljs-string">'integer', primaryKey: <span class="hljs-literal">true}, <span class="hljs-comment">// 主键 content : {type: <span class="hljs-string">'text'}, <span class="hljs-comment">// 消息的内容 type : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true, enum: [<span class="hljs-number">1, <span class="hljs-number">2, <span class="hljs-number">3]}, <span class="hljs-comment">// 消息的类型,1: 公告 Announce,2: 提醒 Remind,3:信息 Message target : {type: <span class="hljs-string">'integer'}, <span class="hljs-comment">// 目标的ID targetType : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 目标的类型 action : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 提醒信息的动作类型 sender : {type: <span class="hljs-string">'integer'}, <span class="hljs-comment">// 发送者的ID createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
Save Remind
消息表,我们需要target
、targetType
字段,来记录该条提醒所关联的对象。而action
字段,则记录该条提醒所关联的动作。
比如消息:「小明喜欢了文章」
则:
<code class="javascript">target = <span class="hljs-number">123, <span class="hljs-comment">// 文章ID targetType = <span class="hljs-string">'post', <span class="hljs-comment">// 指明target所属类型是文章 sender = <span class="hljs-number">123456 <span class="hljs-comment">// 小明ID</span></span></span></span></span></span></code>
Save Announce and Message
当然,Notify还支持存储公告和信息。它们会用到content
字段,而不会用到target
、targetType
、action
字段。
<code class="javascript">id : {type: <span class="hljs-string">'integer', primaryKey: <span class="hljs-literal">true}, <span class="hljs-comment">// 主键 isRead : {type: <span class="hljs-string">'boolean', required: <span class="hljs-literal">true}, user : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 用户消息所属者 notify : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true} <span class="hljs-comment">// 关联的Notify createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></span></span></code>
我们用UserNotify来存储用户的消息队列,它关联一则提醒(Notify)的具体内容。
UserNotify的创建,主要通过两个途径:
<code class="javascript">target : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 目标的ID targetType : {type: <span class="hljs-string">'string', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 目标的类型 action : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 订阅动作,如: comment/like/post/update etc. user : {type: <span class="hljs-string">'integer'}, createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></code>
订阅,是从Notify表拉取消息到UserNotify的前提,用户首先订阅了某一个目标的某一个动作,在此之后产生这个目标的这个动作的消息,才会被通知到该用户。
如:「小明关注了产品A的评论」,数据表现为:
<code class="javascript">target: <span class="hljs-number">123, <span class="hljs-comment">// 产品A的ID targetType: <span class="hljs-string">'product', action: <span class="hljs-string">'comment', user: <span class="hljs-number">123 <span class="hljs-comment">// 小明的ID</span></span></span></span></span></span></code>
这样,产品A下产生的每一条评论,都会产生通知给小明了。
<code class="javascript">action: {type: <span class="hljs-string">'json', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 用户的设置 user: {type: <span class="hljs-string">'integer'}</span></span></span></span></code>
不同用户可能会有不一样的订阅习惯,在这个表中,用户可以统一针对某种动作进行是否订阅的设置。而默认是使用系统提供的默认配置:
<code class="javascript">defaultSubscriptionConfig: { <span class="hljs-string">'comment' : <span class="hljs-literal">true, <span class="hljs-comment">// 评论 <span class="hljs-string">'like' : <span class="hljs-literal">true, <span class="hljs-comment">// 喜欢 }</span></span></span></span></span></span></code>
<p>在这套模型中,<code>targetType</code>、<code>action</code>是可以根据需求来扩展的,例如我们还可以增加多几个动作的提醒:<code>hate</code>被踩、<code>update</code>被更新....诸如此类。</p>
<code class="javascript"><span class="hljs-comment">// 提醒关联的目标类型 targetType: { PRODUCT : <span class="hljs-string">'product', <span class="hljs-comment">// 产品 POST : <span class="hljs-string">'post' <span class="hljs-comment">// 文章 }, <span class="hljs-comment">// 提醒关联的动作 action: { COMMENT : <span class="hljs-string">'comment', <span class="hljs-comment">// 评论 LIKE : <span class="hljs-string">'like', <span class="hljs-comment">// 喜欢 }, <span class="hljs-comment">// 订阅原因对应订阅事件 reasonAction: { <span class="hljs-string">'create_product' : [<span class="hljs-string">'comment', <span class="hljs-string">'like'] <span class="hljs-string">'like_product' : [<span class="hljs-string">'comment'], <span class="hljs-string">'like_post' : [<span class="hljs-string">'comment'], }, <span class="hljs-comment">// 默认订阅配置 defaultSubscriptionConfig: { <span class="hljs-string">'comment' : <span class="hljs-literal">true, <span class="hljs-comment">// 评论 <span class="hljs-string">'like' : <span class="hljs-literal">true, <span class="hljs-comment">// 喜欢 }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
createAnnounce(content, sender)
createRemind(target, targetType, action, sender, content)
createMessage(content, sender, receiver)
pullAnnounce(user)
pullRemind(user)
subscribe(user, target, targetType, reason)
cancelSubscription(user, target ,targetType)
getSubscriptionConfig(userID)
updateSubscriptionConfig(userID)
getUserNotify(userID)
read(user, notifyIDs)
我们可以在产品创建之后,调用NotifyService.subscribe
方法,
然后在产品被评论之后调用NotifyService.createRemind
方法,
再就是用户登录系统或者其他的某一个时刻调用NotifyService.pullRemind
方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify
方法。
在管理员发送了一则公告的时候,调用NotifyService.createAnnounce
方法,
然后在用户登录系统或者其他的某一个时刻调用NotifyService.pullAnnounce
方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify
方法。
信息的创建,只需要直接调用NotifyService.createMessage
方法就可以了,
在下一次用户查询消息队列的时候,就会查询这条信息。