gin和gorm进阶功能(11) | 青训营笔记

272 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天

本文将介绍聊天功能的相关处理

方案

对于后端聊天接口

  1. 使用 WebSockets 或 Server-Sent Events (SSE) 来实现即时通讯。
  2. 使用第三方通讯工具,如 Pusher,Ably 等。
  3. 使用 XMPP (Extensible Messaging and Presence Protocol) 协议,这是一种开放的、可扩展的即时通讯协议。
  4. 使用 MQTT (Message Queuing Telemetry Transport) 协议,这是一种专门针对 IoT (Internet of Things) 设备的即时通讯协议。

思路

在这里,我采用了消息队列的方法 与评论的流程一致, 发送私信的话——就是用消息队列异步生产者把对应的消息发到指定主题中
main函数会协程(多线程)监听这个主题
如果监听到了会进行存储操作——存储到数据库(可以加个更新到缓存,过期时间可以设置短点)
获取聊天记录——直接数据库或者缓存读取

详情可看代码

消息队列知识点

1、Kafka 本质上是⼀个消息队列,一个高吞吐量、持久性、分布式的消息系统。
2、包含生产者(producer)和消费者(consumer),每个consumer属于一个特定的消费者组(Consumer Group)。
3、生产者生产消息(message)写入到kafka服务器(broker,kafka集群的节点),消费者从kafka服务器(broker)读取消息。
4、消息可分为不同的类型即不同的主题(topic)。
5、同一主题(topic)的消息可以分散存储到不同的服务器节点(partition)上,一个分区(partition)只能由一个消费者组内的一个消费者消费。
6、每个partition可以有多个副本,一个Leader和若干个Follower,Leader发生故障时,会选取某个Follower成为新的Leader。

教程

www.cnblogs.com/YLTFY1998/p…
Kafka可视化工具: blog.csdn.net/qq_45956730…

代码

main.go

// 监听私信聊天的消息队列  
go dao.ListenChat()

service/chat/chatService.go

package chat

import (
	"encoding/json"
	"fmt"
	"go_douyin/global/variable"
	"go_douyin/model"
	"go_douyin/utils/kafka_client"
)

type ChatService struct {
	//chatMapper *dao.ChatMapper
	kafkaClients *kafka_client.KafkaClient
}

func NewChatService() *ChatService {
	return &ChatService{
		kafkaClients: &kafka_client.KafkaClient{},
	}
}

// AddChat 发送消息
func (h *ChatService) AddChat(chat model.Chat) error {
	// 序列化评论数据
	chatJSON, err := json.Marshal(chat)
	if err != nil {
		return err
	}
	// 将私信放到消息队列
	err = variable.Kafka_chat.ProduceMessage(string(chatJSON))
	return err
}

// GetMessages 获取聊天记录
func (h *ChatService) GetMessages(senderID string, recipientID string) ([]string, error) {
	fmt.Println("此处直接获取数据库或者缓存的数据即可")
	return nil, nil
}

global/variable/variable.go

// 私信聊天的队列  
Kafka_chat *kafka_client.KafkaClient

Kafka_chat = kafka_client.NewKafkaClient([]string{"43.139.72.246:9092"}, "chat-topic")

chatMapper.go

package dao

import (
	"encoding/json"
	"fmt"
	"go_douyin/global/variable"
	"go_douyin/model"
)

type ChatMapper struct{}

func NewChatMapper() *ChatMapper {
	return &ChatMapper{}
}

// 监听私信消息队列
func ListenChat() {
	for {
		// 获取消息
		message, err := variable.Kafka_chat.ConsumeMessage()
		if err != nil {
			fmt.Printf("获取消息失败:%v\n", err)
			continue
		}
		// 反序列化私信数据
		var chat model.Chat
		err = json.Unmarshal([]byte(message), &chat)
		if err != nil {
			fmt.Printf("反序列化私信数据失败:%v\n", err)
			continue
		}
		// 存储私信数据
		err = SaveChat(chat)
		if err != nil {
			fmt.Printf("存储私信数据失败:%v\n", err)
			continue
		}
		fmt.Printf("成功存储私信数据:%+v\n", chat)
	}
}

// 监听预加载私信消息队列
func ListenPreloadChatList() {
	for {
		fmt.Printf("正在预加载……")
		// 获取消息
		message, err := variable.Kafka_preload.ConsumeMessage()
		if err != nil {
			fmt.Printf("获取消息失败:%v\n", err)
			continue
		}
		// 此次应写存储到缓存的函数
		fmt.Printf("正在缓存" + message + "视频id的私信")
	}
}

func SaveChat(chat model.Chat) error {
	// 这里是将私信数据存储到数据库的代码,具体实现方式取决于你使用的数据库类型
	// 这里还可以补个缓存
	fmt.Println("正在存储数据库,同时更新进缓存", chat)
	return nil
}

chatController.go

package controller  
  
import (  
   "fmt"  
   "github.com/gin-gonic/gin"   "go_douyin/model"   "go_douyin/service/chat"   "go_douyin/utils/response"   "time")  
  
type ChatController struct {  
   chatService *chat.ChatService  
}  
  
func NewChatController() *ChatController {  
   return &ChatController{  
      chatService: chat.NewChatService(),  
   }  
}  
  
// 发送信息  
func (h *ChatController) AddChat(c *gin.Context) {  
   // 获取请求参数  
   var requestBody map[string]interface{}  
   requestBody = make(map[string]interface{})  
   // 解析请求体  
   c.ShouldBindJSON(&requestBody)  
   // 获取请求参数  
   //在 HTTP POST 请求中,请求体中的数据通常是以字符串形式发送的。JSON 格式中的数字默认都是浮点型,默认都是 float64 类型  
   user_id := requestBody["user_id"].(float64)  
   to_user_id := requestBody["to_user_id"].(float64)  
   content, _ := requestBody["content"].(string)  
   var cc model.Chat  
   cc.RecipientID = uint64(to_user_id)  
   cc.Message = content  
   cc.SenderID = uint64(user_id)  
   cc.SendTime = time.Now()  
   err := h.chatService.AddChat(cc)  
   if err != nil {  
      response.Success(c, "私信失败", gin.H{})  
      fmt.Println(err)  
      return  
   }  
   response.Success(c, "私信成功", gin.H{})  
}  
  
// 聊天记录  
func (h *ChatController) ListChat(c *gin.Context) {  
   // 获取请求参数  
   ToUserId := c.Query("to_user_id")  
   userId := c.Query("user_id")  
   data, err := h.chatService.GetMessages(userId, ToUserId)  
   if err != nil {  
      response.Success(c, "获取失败", gin.H{})  
      return  
   }  
   response.Success(c, "获取成功", gin.H{  
      "message_list": data,  
   })  
}

router.go

chatController := controller.NewChatController()

// 社交组:私信聊天  
v5 := router.Group("/douyin/message")  
{  
   v5.POST("action", chatController.AddChat)  
   v5.GET("chat", chatController.ListChat)  
}

chat.go

package model  
  
import "time"  
  
// 这里后面的记得改和以前的一样  
type Chat struct {  
   ChatID      uint64    `gorm:"column:chat_id;primaryKey;autoIncrement"`  
   SenderID    uint64    `gorm:"column:sender_id;not null"`  
   RecipientID uint64    `gorm:"column:recipient_id;not null"`  
   Message     string    `gorm:"column:message;not null"`  
   SendTime    time.Time `gorm:"column:send_time;not null;default:CURRENT_TIMESTAMP"`  
}  
  
func (Chat) TableName() string {  
   return "tb_chat"  
}