Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/smileexpression/im
A repo to learn IM.
https://github.com/smileexpression/im
Last synced: 3 days ago
JSON representation
A repo to learn IM.
- Host: GitHub
- URL: https://github.com/smileexpression/im
- Owner: smileexpression
- License: mit
- Created: 2024-03-05T05:29:55.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2024-03-08T05:02:30.000Z (8 months ago)
- Last Synced: 2024-10-12T13:41:33.141Z (about 1 month ago)
- Language: JavaScript
- Homepage:
- Size: 7.47 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# IM学习笔记
为什么选择go?
1. **并发模型**:Go 语言的并发模型是它的一大特色。Go 语言的 goroutine(轻量级线程)和 channel(用于在 goroutine 之间传递数据和同步的机制)使得并发编程更加简单和高效。
2. **静态类型和编译**:Go 是静态类型语言,这意味着在编译时就会发现很多类型错误,而不是在运行时。此外,Go 语言的编译速度非常快,这使得开发过程更加高效。
3. **内置网络库**:Go 语言有强大的内置网络库,包括 http、grpc 等,非常适合开发网络应用和服务端应用。
4. **跨平台**:Go 语言支持跨平台编译,你可以在一种操作系统下编译生成另一种操作系统的可执行程序。
5. **垃圾回收**:Go 语言有垃圾回收功能,这意味着开发者不需要(也不能)自己管理内存,降低了内存泄漏的可能性。
6. **简洁和清晰的语法**:Go 语言的语法简洁而清晰,易于学习,易于阅读,易于维护。
7. **标准库**:Go 语言拥有丰富的标准库,覆盖了 I/O、文本处理、图像处理、数据库、网络编程、并发编程等许多领域。
8. **工具链**:Go 语言的工具链非常完整,包括用于下载依赖、格式化代码、生成文档、运行测试、编译和安装程序等的工具。
9. **云原生支持**:Go 语言是 Docker 和 Kubernetes 等多个重要的云原生项目的首选语言,对云原生应用有很好的支持。## 3、深入理解WebSocket协议
### 3.1简介
WebSocket:全双工、双向通信。使用自定义协议,客户端和服务端之间发送非常少量数据。
http:请求-相应模式,半双工通信。字节级开销。
> AJAX 是 "Asynchronous JavaScript and XML"(异步 JavaScript 和 XML)的缩写。它不是一种新的编程语言,而是一种使用现有的标准和技术的新方法。AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
>
> 在 AJAX 中,JavaScript 通过异步方式与服务器进行数据交换,这意味着它可以在不影响页面的其余部分或用户交互的情况下,请求额外的数据。这使得网页在等待服务器响应时可以继续处理其他事情,而不会阻塞或延迟,从而极大地提高了用户体验。
>
> AJAX 的工作原理如下:
>
> 1. 用户执行某个动作,如点击一个按钮,这会触发 JavaScript 事件。
> 2. JavaScript 创建一个 XMLHttpRequest 对象,然后向服务器发送一个请求,这个请求可以是获取数据,也可以是发送数据。
> 3. 服务器处理这个请求,然后返回一个响应。
> 4. JavaScript 使用这个响应更新网页。
>
> 虽然 AJAX 的名字中包含 "XML",但实际上它可以处理各种数据格式,包括纯文本、HTML、JSON,甚至是二进制数据。
>
> AJAX 技术大大改善了网页的响应性和灵活性,使得网页能够提供类似于桌面应用的交互体验。### 3.2WebSocket复用了HTTP的握手通道
WebSocket复用了http的握手通道。ajax就是在这样的通道上完成数据传输的,只不过ajax交互是短连接,在一次 request->response 之后,“通道”连接就断开了。
### 3.3HTTP协议升级为WebSocket协议
**在这个握手的过程当中,客户端和服务端主要做了两件事情:**
- 1)建立了一条连接“握手通道”用于通信(这点和HTTP协议相同,不同的是HTTP协议完成数据交互后就释放了这条握手通道,这就是所谓的“短连接”,它的生命周期是一次数据交互的时间,通常是毫秒级别的);
- 2)将HTTP协议升级到WebSocket协议,并复用HTTP协议的握手通道,从而建立一条持久连接。注意:维持“长连接”需要消耗服务器资源。
### 3.4WebSocket的帧和数据分片传输
HTTP协议是基于TCP实现的,HTTP发送数据也是分包转发的,就是将大数据根据报文形式分割成一小块一小块发送到服务端,服务端接收到客户端发送的报文后,再将小块的数据拼接组装。WebSocket协议也是通过分片打包数据进行转发的,不过策略上和HTTP的分包不一样。
WebSocket通信中,客户端发送数据分片是有序的,这一点和HTTP不一样,HTTP将消息分包之后,是并发无序的发送给服务端的,包信息在数据中的位置则在HTTP报文中存储,而WebSocket仅仅需要一个FIN比特位就能保证将数据完整的发送到服务端。
### 3.5Websocket连接保持和心跳检测
WebSocket长连接浪费服务端资源。但是,举个例子:你几个月没有和一个QQ好友聊天了,突然有一天他发QQ消息告诉你他要结婚了,你还是能在第一时间收到。那是因为,客户端和服务端一直再采用心跳来检查连接。
>在即时通讯(IM)系统中,服务器发送心跳的一个主要原因是,服务器通常需要维护与多个客户端的连接,而每个客户端可能会有不同的网络条件和活动状态。通过定期从服务器发送心跳消息,服务器可以更有效地管理和监控所有的连接状态,及时检测到任何断开的连接,并可以在必要时关闭那些无响应的连接,释放资源。
>`go func()` 是 Go 语言中创建协程(goroutine)的一种方式。在 Go 语言中,协程是一种轻量级的线程,由 Go 运行时管理。
>
>协程在 Go 中是非常重要的一个特性,它使得并发编程变得非常简单。你可以轻易地启动成千上万的协程,而不需要担心线程切换的开销。这使得 Go 语言非常适合编写高并发的程序。**线程**:
1. 线程是操作系统层面的并发执行实体,每个线程都有自己的栈和寄存器,线程切换需要涉及到**内核态和用户态的切换**,开销较大。
2. 线程之间的通信通常使用**共享内存**,这需要程序员小心处理同步和锁,以避免出现竞态条件。
3. 线程的生命周期由**操作系统**来管理。**协程**:
1. 协程是程序级别的,也就是说它完全运行在**用户态**,协程切换不涉及内核态和用户态的切换,开销较小。
2. 协程可以使用共享内存进行通信,但更常见的是通过使用**通道(Channel)**进行通信。这种方式更安全,更容易避免竞态条件。
3. 协程的生命周期由**程序**来管理。## 4、开始动手,快速搭建高性能、可拓展的IM系统
### 4.1系统架构和代码文件目录结构
![跟着源码学IM(六):手把手教你用Go快速搭建高性能、可扩展的IM系统_10.jpg](http://www.52im.net/data/attachment/forum/202004/26/143945a1vvlldxb3vv3z03.jpg)
> WebSocket 协议在即时通讯(IM)系统的接入层中常常被选择,其主要优点包括:
>
> 1. **全双工通信**:WebSocket 提供了全双工通信,这意味着服务器和客户端可以在任何时候互相发送数据,而不需要等待对方的响应。这对于 IM 系统来说非常重要,因为它允许服务器实时地向客户端推送新的消息。
> 2. **低延迟**:与 HTTP 轮询或长轮询相比,WebSocket 的延迟更低。这是因为一旦建立了 WebSocket 连接,数据就可以立即在服务器和客户端之间传输,而不需要每次发送请求和等待响应。
> 3. **减少网络流量**:由于 WebSocket 连接一旦建立就会保持打开状态,因此它可以减少由于频繁建立和关闭 HTTP 连接所产生的网络流量。
> 4. **实时性**:WebSocket 支持服务器向客户端推送信息,客户端无需请求即可实时接收信息,这对于 IM 系统来说非常重要。
> 5. **兼容性**:WebSocket 被所有主流浏览器支持,因此可以在多种平台和设备上使用。### 4.210行代码万能模版渲染
暂时渲染不出来
### 4.3注册、登录和鉴权
```go
type User struct {
Id int64 `xorm:"pk autoincr bigint(64)" form:"id" json:"id"`
Mobile string `xorm:"varchar(20)" form:"mobile" json:"mobile"`
Passwd string `xorm:"varchar(40)" form:"passwd" json:"-"` // 用户密码 md5(passwd + salt)
Avatar string `xorm:"varchar(150)" form:"avatar" json:"avatar"`
Sex string `xorm:"varchar(2)" form:"sex" json:"sex"`
Nickname string `xorm:"varchar(20)" form:"nickname" json:"nickname"`
Salt string `xorm:"varchar(10)" form:"salt" json:"-"`
Online int `xorm:"int(10)" form:"online" json:"online"` //是否在线
Token string `xorm:"varchar(40)" form:"token" json:"token"` //用户鉴权
Memo string `xorm:"varchar(140)" form:"memo" json:"memo"`
Createat time.Time `xorm:"datetime" form:"createat" json:"createat"` //创建时间, 统计用户增量时使用
}
```我认为比较重要的几个字段
- Salt(随机值,增强安全性)
- Online
- Token存储了token标示用户在用户登录之后,http协议升级为websocket协议进行鉴权。
注册和登录的业务逻辑比较简单。
比较难的是util.Bind(request, &user)将用户参数绑定到user对象上,具体实现细节读者可以自行研究,主要模仿了Gin框架的参数绑定。
用户客户端如何请求建立与服务端的websocket长连接?
服务端收到建立连接的请求之后,会对客户端请求进行校验,以确实是否建立长连接,然后将这条长连接的句柄添加到map当中(因为服务端不仅仅对一个客户端服务,可能存在千千万万个长连接)维护起来。
```go
//userid和Node映射关系表
type Node struct {
Conn *websocket.Conn
//并行转串行,
DataQueue chan []byte
GroupSets set.Interface
}
```每一个用户都是Node的实例,消息存放在管道(channel)DataQueue中,GroupSets中存放自己的聊天对象(包括个人用户和群聊)。
注意:建立userid和Node映射的时候加写锁,保证原子安全。
```go
rwlocker.Lock()
clientMap[userId] = node
rwlocker.Unlock()
```服务端和客户端建立了长链接之后,会开启两个协程专门来处理客户端消息的收发工作。
例子:客户端A发送消息给客户端B,服务端充当中介,使用recvproc接收客户端A发送来的消息,通过一系列处理,将消息填入B的DataQueue中。sendproc检测到B的管道中有消息,就会将消息发送给客户端B。
### 4.4实现单聊和群聊
| | 服务端接收 | 服务端发送 |
| ---- | ------------------------ | -------------------------- |
| 单聊 | 填入目标的管道 | 无区别,管道收到消息就发送 |
| 群聊 | 遍历群友,填入群友的管道 | 同上 |添加好友的业务逻辑也很简单,但是要注意用事务,同时添加双方的联系。
### 4.5发送表情和图片
为什么是将文件的内容复制到新文件中,而不是将原文件存到本地?
> 这是因为在处理 HTTP 文件上传时,上传的文件通常是以数据流的形式存在的,而不是一个实体的文件。当你在浏览器中选择一个文件并点击上传时,你的浏览器会将这个文件的内容读取到内存中,然后通过 HTTP 请求将这些内容发送到服务器。在服务器端,这些内容会被封装成一个临时的文件对象(在 Go 语言中,这个对象通常是一个 `*multipart.File` 类型的对象)。
>
> 因此,当服务器接收到这个上传的文件时,它实际上是在接收一个包含了文件内容的数据流,而不是一个实体的文件。服务器需要做的是将这个数据流保存到一个新的文件中,这就是为什么需要将上传的文件的内容复制到新的文件中。
>
> 另外,这种方式还有以下几点好处:
>
> 1. 可以在保存文件时对文件名进行修改,例如添加时间戳或随机字符串,以避免文件名冲突。
> 2. 可以在保存文件时对文件内容进行处理,例如进行压缩或加密。
> 3. 可以控制文件保存的位置,例如保存到特定的目录或者分布式文件系统中。## 参考文献
[**跟着源码学IM(六):手把手教你用Go快速搭建高性能、可扩展的IM系统**](http://www.52im.net/thread-2988-1-1.html)