前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >简易聊天系统-聊天服务

简易聊天系统-聊天服务

作者头像
全栈程序员站长
发布于 2022-07-21 07:41:07
发布于 2022-07-21 07:41:07
2.4K00
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

聊天负责私人聊天,群组聊天。私人聊天接受信息后保存至数据库再转发给目标用户。群组聊天当前没有离线消息保存,也就是用户登录后无法知道多少消息未读,而是直接拉取指定数量群聊天。当有成员发送后会将聊天信息存储数据库(没有缓存进redis,因为在线用户会直接发送,目前没有这个优化必要),从redis中检索所有群组在线用户并通过消息队列发送至对应网关。

大致代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
// 处理群消息
func DealGroupMsg(delivery *amqp.Delivery, transfer1 *transfer) {
	now := time.Now()

	Userid := transfer1.Id
	var mess SendMessage
	err := mapstructure.Decode(transfer1.Data.Data, &mess)
	if err != nil {
		fmt.Println("DealGroupMsg json err :", err)
		return
	}
	if mess.UserId != Userid {
		fmt.Println("DealGroupMsg 发送者id不一致 ", mess.UserId, "--", Userid)
		//	delivery.Ack(true)
		return
	}

	ItemId := mess.To
	if mess.MsgType != GroupMessage {
		fmt.Println("DealGroupMsg 信息类型不一致 ", mess.MsgType, "--", Userid)
		//	delivery.Ack(true)
		return
	}
	// 判断用户是否为群成员
	r, err := IsGroupMember(Userid, ItemId)
	if err != nil {
		fmt.Println("DealGroupMsg json err :", err)
		return
	}
	if !r {
		fmt.Println("DealGroupMsg 不属于群成员 ", mess.To, "--", Userid)
		//	delivery.Ack(true)
		return
	}
	// 获取全局id
	id, err := redisconn.GetGlobalID()
	if err != nil {
		fmt.Println("DealGroupMsg json err :", err)
		return
	}

	//fmt.Println(id)
	//	time.Sleep(4 * time.Second)
	// 获取当前时间戳
	ti := time.Now().UnixNano() / 1e6
	Msgid1 := strconv.FormatInt(ti, 10) + fmt.Sprintf("%06d", id)
	mess.Id = Msgid1
	// 将id 发送给发信息者
	var sendmessagereply SendMessageReply
	sendmessagereply.Id = Msgid1
	sendmessagereply.MsgReplyID = mess.MsgReplyID
	sendmessagereply.To = mess.To
	sendmessagereply.MsgType = GroupMessage
	nowtime := time.Now().UnixNano() / 1e6
	// 如果发送时间差值小于2S 选用发送者时间 否则选择后台时间
	UserSendTime, err := strconv.ParseInt(mess.SendTime, 10, 64)
	if err != nil {
		fmt.Println("DealGroupMsg", err)
		return
	}
	var ReplyTime string
	if Abs(UserSendTime-nowtime) < 1000*2 {
		ReplyTime = strconv.FormatInt(UserSendTime, 10)
	} else {
		ReplyTime = strconv.FormatInt(nowtime, 10)
	}
	sendmessagereply.ReplyTime = ReplyTime
	var json = jsoniter.ConfigCompatibleWithStandardLibrary
	data2, err := json.Marshal(sendmessagereply)
	if err != nil {
		fmt.Println("DealGroupMsg", err)
		return
	}
	time11 := time.Now()
	err = RabbitMqPublish(mq, data2, Userid, SendGroupMsgAckReply, transfer1.From)
	if err != nil {
		fmt.Println("DealGroupMsg", err)
		return
	}
	fmt.Println("RabbitMqPublish", time.Now().Sub(time11))
	// 查询群成员
	//	delivery.Ack(true)
	var rmsg ReceiveMessage
	rmsg.Id = Msgid1
	rmsg.MsgData = mess.MsgData
	rmsg.MsgDataType = mess.MsgDataType
	rmsg.MsgType = GroupMessage
	rmsg.To = mess.To
	rmsg.UserId = mess.UserId
	rmsg.SendTime = ReplyTime
	// 使用Mysql存储起来
	go InsertMessages(rmsg)
	// err = msql.InsertChatMessage(Msgid1, mess.UserId, mess.To, mess.MsgData, mess.MsgType, mess.MsgDataType, ReplyTime)
	// if err != nil {
	// 	fmt.Println("DealGroupMsg e", Msgid1, err)
	// 	return
	// }
	fmt.Println("DealGroupMsg", time.Now().Sub(now))
	go DealGroupMessage(ItemId, rmsg)
}

// 群成员发送信息后后续处理
func DealGroupMessage(GroupId string, Msg ReceiveMessage) {
	now := time.Now()

	// 获取群成员 在线则发送
	members, err := GetGroupMemberListSimply(GroupId)
	if err != nil {
		fmt.Println("DealGroupMessage", err)
		return
	}

	data2, err := json.Marshal(Msg)
	if err != nil {
		fmt.Println("DealGroupMessage", err)
		return
	}

	for _, member := range members {
		// 获取用户信息
		u, err := redisconn.RedisGetUser(member.User.Userid)
		if err != nil {
			fmt.Println("DealGroupMessage", err)
			continue
		}
		//如果群成员在线则发送
		if u.Status == OnLine {
			//		fmt.Println("Send")
			err = RabbitMqPublish(mq, data2, u.Userid, ReceiveGroupMsg, u.GateWay)
			if err != nil {
				fmt.Println("AddUserDeal", err)
				return
			}
		}
	}
	fmt.Println("DealGroupMessage", time.Now().Sub(now))
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/124739.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月4,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
golang 使用时间通过md5生成token
package main import ( "crypto/md5" "fmt" "io" "strconv" "time" ) func main() { crutime := time.Now().Unix() fmt.Println("crutime-->", crutime) h := md5.New() fmt.Println("h-->", h)
李海彬
2018/03/23
2.1K0
Golang语言情怀-第15期 Go 语言设计模式 创建模式-生成器
3.有时我们希望生成器能够自动的退出,这时可以使用golang channel的
李海彬
2021/01/21
3720
如何开发ChatOps
现在各种Ops,比如DevOps,AIOps,ChatOps,NoOps等等,反正其背后的意义就是各种卷运维,让运维无路可走。
没有故事的陈师傅
2022/12/06
5520
如何开发ChatOps
文件上传下载
package main import ( "fmt" "html/template" "log" "net/http" "os" "io" "crypto/md5" "time" "strconv" "archive/zip" ) var buf []byte func sayhelloName(w http.ResponseWriter, r *http.Request){ /*if r.Method == "Ge
李海彬
2018/03/27
1.2K0
golang源码分析:grpc 链接池(4)自定义resolver 、balancer和picker
在分析完源码后golang源码分析:grpc 链接池(3)resolver 、balancer和picker,我们尝试自定义实现相应的插件。grpc 通过服务发现或者直连形式获取到 gRPC server 的实例的 endpoints,然后通知负载均衡器进行 SubConn 更新,对于新加入的 endpoint 进行实例创建,移出废弃的 endpoint, 最后通过状态更新将状态为 Idle 的 SubConn 进行管理,gRPC 在调用 Invoke时,则会通过负载均衡器中的 Picker 去按照某一个负载均衡算法选择一个 SubConn 创建链接,如果创建成功则不再进行其他 SubConn 的尝试,否则会按照一定的退避算法进行重试,直到退避失败或者创建链接成功为止。上述三个组件的功能分别如下:
golangLeetcode
2023/03/01
9870
golang源码分析:grpc 链接池(4)自定义resolver 、balancer和picker
Go 每日一库之 nutsdb
nutsdb是一个完全由 Go 编写的简单、快速、可嵌入的持久化存储。nutsdb与我们之前介绍过的buntdb有些类似,但是支持List、Set、Sorted Set这些数据结构。
用户7731323
2020/09/08
4960
Go语言基于Socket编写服务器端与客户端通信的实例
在golang中,网络协议已经被封装的非常完好了,想要写一个Socket的Server,我们并不用像其他语言那样需要为socket、bind、listen、receive等一系列操作头疼,只要使用Golang中自带的net包即可很方便的完成连接等操作~ 在这里,给出一个最最基础的基于Socket的Server的写法: package main import ( "fmt" "net" "log" "os" ) func main() {
李海彬
2018/03/23
2.2K0
转--Go语言基于Socket编写服务器端与客户端通信的实例
在golang中,网络协议已经被封装的非常完好了,想要写一个Socket的Server,我们并不用像其他语言那样需要为socket、bind、listen、receive等一系列操作头疼,只要使用Golang中自带的net包即可很方便的完成连接等操作~ 在这里,给出一个最最基础的基于Socket的Server的写法: 代码如下: package main import ( "fmt" "net" "log" "os" ) func main()
李海彬
2018/03/22
1.2K0
从0使用gin框架搭建博客(2)-问题解决
binding是引入了validator10.0 ,这里是系统可自动校验email格式
爽朗地狮子
2022/10/20
3150
52. Socket Server 自定义协议的简单实现 | 厚土Go学习笔记
在 Server 和 Client 通讯中,由于网络等原因很有可能会发生数据丢包的现象。如果数据确实,服务端接收的信息不完整,就会造成混乱。 我们就需要在 Server 和 Client 之间建立一个通讯协议,通过协议中的规则,判断当前接收到的信息是否完整。根据信息的完整情况,采取不同的处理方法。 通讯协议 protocol 的核心就是设计一个头部。如果传来的信息不包含这个头部,就说明当前信息和之前的信息是同一条。那么就把当前信息和之前的那条信息合并成一条。 而协议主要包含的功能是封装(Enpack)和解析
李海彬
2018/03/19
2K0
52. Socket Server 自定义协议的简单实现 | 厚土Go学习笔记
golang使用 mongo
 连接集群 mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?o
架构师刀哥
2018/03/20
2.3K0
GO-字符串常用操作
package main import ( "fmt" "strconv" "strings" ) func main() { /*字符串基本操作--strings*/ str := "wangdy" //是否包含 fmt.Println(strings.Contains(str, "wang"), strings.Contains(str, "123")) //true false //获取字符串长度
李海彬
2018/03/27
6270
Golang 泛型实现类型转换
Golang 标准库提供了很多类型转换的函数,如 strconv 包可完成 string 与基本数据类型之间的转换。
恋喵大鲤鱼
2022/12/30
3.5K0
54. 心跳的实现 | 厚土Go学习笔记
在多客户端同时访问服务器的工作模式下,首先要保证服务端的运行正常。因此,Server在和Client建立通讯后,确保连接的及时断开就非常重要。否则,多个客户端长时间占用着连接不关闭,是非常可怕的服务器资源浪费。会使得服务器可服务的客户端数量大幅度减少。 因此,针对短连接和长连接,根据业务的需要,配套不同的处理机制。 短连接 一般建立完连接,就立刻传输数据。传输完数据,连接就关闭。服务端根据需要,设定连接的时长。超过时间长度,就算客户端超时。立刻关闭连接。 长连接 建立连接后,传输数据,然后要保持连接,然后再
李海彬
2018/03/19
2.2K0
54. 心跳的实现 | 厚土Go学习笔记
Golang 将切片连接成字符串
Join 将字符串切片的所有元素连接成一个字符串,各个元素间使用给定的字符串分隔。
恋喵大鲤鱼
2023/10/12
3320
Golang 将切片连接成字符串
[系列] - go-gin-api 路由中间件 - 签名验证(七)
上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大神,只不过在本地实践了而已,对于 Go 语言的使用,我还是个新人,在这里感谢大家的厚爱!
新亮
2019/10/14
2.7K0
[系列] - go-gin-api 路由中间件 - 签名验证(七)
go语言学习-类型转换
1.字符串到整形(string to int):ParseInt 返回的是 int64
solate
2019/07/19
9900
go 如何给服务做限流?
【1】限流就是限制流量进入或者从系统出去的速率,防止流量过高导致系统过载或者崩溃。
Johns
2021/07/15
3K0
go 如何给服务做限流?
go语言微信公众号开发后台接口封装
业余时间做了个有意思的小功能,每天早上7点准时给发天气预报,每晚8点发布一条英语说说,提醒自己不能忘记学习。
杨永贞
2020/08/04
2.1K0
【共识算法】--“raft的实现”
看过之前几期的朋友们应该知道在1号第1期最初的时候就实现过一次raft,但之前实现基本是基于python实现的,这次可结合着PBFT,用golang实现了raft。
帆说区块链
2022/04/26
4770
【共识算法】--“raft的实现”
相关推荐
golang 使用时间通过md5生成token
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验