【IM】如何保证消息可用性(一)

目录

  • 1. 基本概念
    • 1.1 长连接 和 短连接
    • 1.2 PUSH模式和PULL模式
  • 2. 背景介绍
    • 2.1 理解端到端的思想
  • 3. 方案选型
    • 3.1 技术挑战
    • 3.2 技术目标

1. 基本概念

在讲解消息可用性之前,需要理解几个通信领域的基本概念。

1.1 长连接 和 短连接

  • 什么是长连接,短连接
    在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

    但从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:

     Connection:keep-alive
    

    在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。

    HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接


  • TCP的长连接和短连接

    • 对于TCP短连接,一般由客户端发起三次握手,经过一次读写操作后,由客户端发起四次挥手,断开连接。短连接的优点在于管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段

    • 对于TCP长连接,简单来说,client向server发起连接,server接受client连接,双方建立连接。Client与server完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。

      连接本身是一种资源,连接建立后,server需要保存相关的状态信息,如果客户端已经关闭,此时的状态信息就白白占用了server的资源,因此需要保活功能的存在: 保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务 器端检测到这种半开放的连接。
      如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一:正常运行,已经崩溃,客户主机崩溃并已经重新启动,客户机正常运行但是服务器不可达(弱网环境)

  • 长短连接的选择
    根据之前的介绍,我们已经能感受到长短连接的优缺点。长连接通常用于收发频繁的场景,尤其是点对点通信,比如IM,数据库;而短连接通常用于请求不频繁的情况,比如一般的WEB网站,对于网页资源的请求一般不会十分频繁。

  • 简单的TCP长连接服务器DEMO
    TCP的保活机制保证了传输层的连接可用性,但是如何保证应用层的连接,需要我们做跟多的工作,之后会单独讲解。

    package main
    
    import (
    	"log"
    	"net"
    	"time"
    )
    
    func main() {
    	// 创建 TCP 连接
    	conn, err := net.Dial("tcp", "127.0.0.1:80")
    	if err != nil {
    		log.Fatal("Failed to connect:", err)
    	}
    	defer conn.Close()
    
    	// 将连接转换为 TCPConn 类型
    	tcpConn, ok := conn.(*net.TCPConn)
    	if !ok {
    		log.Fatal("Failed to convert to TCPConn")
    	}
    
    	// 设置 KeepAlive,启用 TCP 连接的 KeepAlive 功能
    	if err := tcpConn.SetKeepAlive(true); err != nil {
    		log.Fatal("Failed to set KeepAlive:", err)
    	}
    
    	// 设置 KeepAlive 周期,每隔一段时间发送一个 KeepAlive 包
    	if err := tcpConn.SetKeepAlivePeriod(10 * time.Second); err != nil {
    		log.Fatal("Failed to set KeepAlive period:", err)
    	}
    
    	// 在这里进行其他操作,保持 TCP 连接处于活跃状态
    }
    
    

1.2 PUSH模式和PULL模式

这个比较简单,可以理解为GIT里的push和pull.服务器主动向客户端发数据叫做push,客户端向客户端请求数据叫做pull

2. 背景介绍

在IM场景下,消息的可用性包含两个方面:可靠性和一致性,可以总结为:可达有序,不重不漏

  • 可靠性:消息一旦显示发送成功,则一定到达对端
  • 一致性:任何时刻消息保证由于发送端的发送顺序一致

2.1 理解端到端的思想

在设计IM系统的时候,最重要的思想是 端到端思维:

端到端原则是一种分布式系统中各个模块之间功能定位的设计原理,指的是从代价和性能的角度出发,在网络的最核心的部分应该只去做数据的传输而不能去做一些其他的应用,而数据是否正确传输则应该放到应用层去检查和判断,从而保证互联网核心的简单性,可维护性,可拓展性。

简单的理解端到端思想,可以以数据传输的可靠性为例子,在网络中传输文件,传输错误可能发生在端到端路线的多个节点,也许我们可以通过在每一个交换节点上都加入数据校验,超时,重传的机制来保证数据正确,但是,实际上,可能错误可能发生在端系统的自身,比如磁盘存取错误,缓存不足。这样一来,即使中间节点提供了数据的可靠,端系统也没有任何工作量的减少,依然需要进行正确性的检测。 因此,在中间节点只需要提供其能完全实现的功能,即路由转发即可。

OSI七层模型也是端到端思想的产物,主机网络的每一层都是一个端系统,只对本层负责,保证本层数据可靠,不保证上层数据可靠。这样的设计使得每一层功能不重叠,结构清晰:

  • 数据链路层(第二层)和物理层(第一层): 这两层负责将数据转换为比特流并通过物理介质传输。在端到端原则下,这些层不应对数据进行处理,而应只负责将数据从一个点传输到另一个点。
  • 网络层(第三层): 数据在网络中进行路由和转发,以便从计算机 A 到计算机 B。在端到端原则下,网络层应该提供最佳的数据传输路径,但不对数据进行修改或过滤
  • 传输层(第四层): 数据被传输到传输层,以便在计算机 A 和 B 之间进行可靠的数据传输。在端到端原则下,传输层负责数据的分段、重传和错误检测,以保证数据的完整性和可靠性

由此我们可以总结:底层可靠性仅能保证底层可靠,不对上层负责。传输层已经帮我们做到了数据的完整和可靠,但是并不能说它能保证应用层的可靠了


那么TCP到底做到了哪一步,我们又需要做哪些工作呢?
在这里插入图片描述
现在假设一个场景(应用层不做可用性处理):clientA发送msg1和msg2到server,两个消息通过一个长连接到达了服务端,此时可能会出现下面这些情况:

  1. msg1和msg2到达应用层,分别由一个线程去处理,msg2先落表成功,发送给clientB,造成数据乱序。
  2. msg1在server落盘失败,msg2成功,先发送给了clientB,造成数据丢失,乱序。
  3. 消息在由接入层到达业务层的时候,server进程崩溃,但是此时clientA认为已经送达,服务端业务无法感知,消息丢失。

因此对于IM这种三方通信来说,TCP/UDP的保证是远远不足够。

消息的端到端可用性 = 上行消息可用 + 服务端业务可用 + 下行消息可用

3. 方案选型

如何设计一个保证消息端到端可用性的协议是我们的本文的最重要任务。

3.1 技术挑战

  1. 三方通信,难以保证网络上的消息必达
  2. 没有全局时钟,那一确定唯一顺序,因果顺序
    • 时钟零点漂移问题:时钟零点漂移通常指的是在计算机系统中时钟的偏移或漂移,这可能会导致时钟与实际时间之间存在误差。这种情况可能由多种因素引起,包括硬件时钟的不准确性、操作系统的时钟管理、系统负载等。
      可行的解决方式有:
      • 使用网络时间协议(NTP): NTP 是一种用于同步计算机时钟的协议,可以从互联网上的时间服务器获取准确的时间信息,并调整本地时钟以匹配其时间。在大多数操作系统中,可以配置系统以自动与 NTP 服务器同步时间。
      • 定期同步时间: 即使已启用自动同步时间功能,也建议定期手动同步一次时间,以确保时钟的准确性
      • 减少系统负载: 高负载的系统可能会影响时钟同步的准确性。尽量减少系统的负载,特别是在进行时钟同步时
  3. 消息顺序性在多客户端,多服务端,多线程/协程下那一保持

3.2 技术目标

对消息可用性的进一步细化:

  1. 消息及时:实时接收,发送 (响应时间<=200ms,高峰期 <=1s)
  2. 消息可达:超时重试,ACK确认
  3. 消息幂等(每条消息只处理一次):分配seqId,服务端存储seqId
  4. 消息有序:seqId可比较,接收端可以按照发送端的顺序进行消息排序 (一个会话内保证消息有序即可)

在下一篇文章中,我们将探讨更加具体的实现方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《幻兽帕鲁》火遍全球,上百个游戏角色竟被曝是AI生成的?

原创 | 文 BFT机器人 最近&#xff0c;一款名为《幻兽帕鲁》&#xff08;Palworld&#xff09;的开放世界生存游戏在社交网络平台上引发了热议&#xff0c;成为了当下最受关注的游戏之一。 这款游戏在1月19日于Steam平台上线抢先体验版本&#xff0c;仅仅24小时之内&#xff0…

2024.1.29每日一题

LeetCode 自由之路 自由之路通向自由&#xff0c;通向睡觉吧&#x1f604; 514. 自由之路 - 力扣&#xff08;LeetCode&#xff09; 题目描述 电子游戏“辐射4”中&#xff0c;任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘&#xff0c;并使用表盘…

K8s 安装部署-Master和Minion(Node)

K8s 安装部署-Master和Minion(Node) 操作系统版本&#xff1a;CentOS 7.4 Master &#xff1a;172.20.26.167 Minion-1&#xff1a;172.20.26.198 Minion-2&#xff1a;172.20.26.210&#xff08;后增加节点&#xff09; ETCD&#xff1a;172.20.27.218 先安装部署ETCD y…

[论文阅读] |RAG评估_Retrieval-Augmented Generation Benchmark

写在前面 检索增强能够有效缓解大模型存在幻觉和知识时效性不足的问题&#xff0c;RAG通常包括文本切分、向量化入库、检索召回和答案生成等基本步骤。近期组里正在探索如何对RAG完整链路进行评估&#xff0c;辅助阶段性优化工作。上周先对评估综述进行了初步的扫描&#xff0…

Python 使用重构重命名一键更改变量名的方法

一个变量有多处引用的情况下&#xff0c;需要重命名&#xff0c;可以使用重构重命名进行一键更改。 方法是:选择变量名–>右键–>Refactor–>Rename&#xff08;也可以使用快捷&#xff1a;选择变量后按下ShiftF6&#xff09;&#xff0c;然后直接输入新的变量名即可…

基于springboot游戏分享网站源码和论文

网络的广泛应用给生活带来了十分的便利。所以把游戏分享管理与现在网络相结合&#xff0c;利用java技术建设游戏分享网站&#xff0c;实现游戏分享的信息化。则对于进一步提高游戏分享管理发展&#xff0c;丰富游戏分享管理经验能起到不少的促进作用。 游戏分享网站能够通过互…

邻接矩阵、关联矩阵

邻接矩阵&#xff1a; 邻接矩阵是一种用来表示图中顶点间相互连接关系的矩阵。在邻接矩阵中&#xff0c;矩阵的行和列都代表图中的顶点。 对于无权图&#xff0c;如果顶点 i 和顶点 j 之间有一条边&#xff0c;则矩阵中的元素 Aij​&#xff08;位于第 i 行和第 j 列&#xff…

3338 蓝桥杯 wyz的数组IV 简单

3338 蓝桥杯 wyz的数组IV 简单 //C风格解法1&#xff0c;通过率50% #include<bits/stdc.h>int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int n; std::cin >> n;int ans 0;std::vector<int>a(n);for(auto &am…

基于Java SSM框架实现家政服务中介网系统项目【项目源码+论文说明】

基于java的SSM框架实现家政服务中介网系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识…

【Git配置代理】Failed to connect to github.com port 443 问题解决方法

前言&#xff1a; 在学习代码审计时&#xff0c;有时会需要使用git去拉取代码&#xff0c;然后就出现了如下错误 看过网上很多解决方法&#xff0c;觉得问题的关键还是因为命令行在拉取/推送代码时并没有使用VPN进行代理。 解决办法 &#xff1a; 配置http代理&#xff1a;…

DOM 型 XSS 攻击演示(附链接)

一、介绍 DOM&#xff08;Document Object Model&#xff09;型 XSS&#xff08;Cross-Site Scripting&#xff09;攻击是一种 Web 应用程序中的安全漏洞&#xff0c;其特点是攻击者成功地注入了恶意脚本&#xff0c;这些脚本在用户的浏览器中执行&#xff0c;从而导致恶意行为…

第93讲:MySQL主从复制集群延时从库的核心概念以及使用

文章目录 1.延时从库的概念2.配置从库延时3.模拟主库误删除使用延时从库恢复数据3.1.模拟主库误删除操作3.2.利用从库延时恢复主库误删除的数据 1.延时从库的概念 延时从库和主从延时是两个概念&#xff0c;延时从库指的是认为手动配置一个从库延时复制主库的时间&#xff0c;…

蓝桥杯-循环节长度

两个整数做除法&#xff0c;有时会产生循环小数&#xff0c;其循环部分称为: 循环节。比如&#xff0c;11/136>0.8461553846153..... 其循环节为[846153] 共有 6 位。下面的方法&#xff0c;可以求出循环节的长度。请仔细阅读代码&#xff0c;并填写划线部分缺少的代码。 注…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例5-3 getBoundingClientRect()

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>getBoundingClientRect()</title> </head> <script>function getRect(){var obj document.getElementById(example); //获取元素对象var objR…

【BUG】联想Y7000电池电量为0且无法充电解决方案汇总

因为最近火灾很多&#xff0c;所以昨天夜晚睡觉的时候把插线板电源关掉了&#xff0c;电脑也关机了。 各位一定要注意用电安全&#xff0c;网上的那些事情看着真的很难受qvq。 第二天早上起床的时候一看发现电脑直接没电了&#xff0c;插上电源后也是显示 你一定要冲进去啊(ू˃…

编译Opencv3.3 版本遇到的Cuda版本变更导致:CUDA_nppicom_LIBRARY (ADVANCED)链接找不到的问题根本解法:

前言&#xff1a; Opencv 开源库的使用是必须的&#xff0c;但是&#xff0c;开源项目的特性&#xff0c;造成&#xff0c;版本的依赖性比较复杂&#xff0c; 尤其是针对某一款老硬件的SDK&#xff0c;往往随着某个开源库的使用&#xff0c;导致&#xff0c;无法编译的问题&am…

【极数系列】Flink配置参数如何获取?(06)

文章目录 gitee码云地址简介概述01 配置值来自.properties文件1.通过路径读取2.通过文件流读取3.通过IO流读取 02 配置值来自命令行03 配置来自系统属性04 注册以及使用全局变量05 Flink获取参数值Demo1.项目结构2.pom.xml文件如下3.配置文件4.项目主类5.运行查看相关日志 gite…

【Spark系列2】Spark编程模型RDD

RDD概述 RDD最初的概述来源于一片论文-伯克利实验室的Resilient Distributed Datasets&#xff1a;A Fault-Tolerant Abstraction for In-Memory Cluster Computing。这篇论文奠定了RDD基本功能的思想 RDD实际为Resilient Distribution Datasets的简称&#xff0c;意为弹性分…

04 Redis之命令(Hash型Value命令+List型Value命令+Set型Value命令+有序集合ZSET型Value命令)

3.4 Hash型Value命令 Hash 表就是一个映射表 Map&#xff0c;也是由键-值对构成&#xff0c;为了与整体的 key 进行区分&#xff0c;这里的键称为 field&#xff0c;值称为 value。注意&#xff0c;Redis 的 Hash 表中的 field-value 对均为 String 类型。 3.4.1 hset  格…

Python笔记14-实战小游戏飞机大战(上)

文章目录 功能规划安装pygame绘制游戏窗口添加玩家飞机图像屏幕上绘制飞船代码重构驾驶飞船全屏模式射击 本示例源码地址 点击下载 功能规划 玩家控制一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船&#xff0c;还可使用空格键射击。游戏开始时&#xff…