动手实现一个消息代理

举报
码乐 发表于 2024/02/22 09:49:46 2024/02/22
【摘要】 2.8 本节简介自己动手写一个代理.我们可以将客户的消息直接发往服务中,这是一个逆向的过程,一般http操作是由客户端发起的,因此http协议很适合作为客户端触发事件的协议,当有客户发起事件时,服务直接响应.这里假设我们有成千上万的客户在触发该事件(比如订票,付款等),我们不希望这些操作直接与服务的数据库交互,让他们模拟现实人类的情况,排队吧. 这时候消息代理的用场就出现了. 2.8.0 ...

2.8 本节简介

自己动手写一个代理.

我们可以将客户的消息直接发往服务中,一般http操作是由客户端发起的,因此http协议很适合作为客户端触发事件的协议,当有客户发起事件时,服务直接响应,这里有一个逆向的过程,当服务端的票数存量发生变化,或者服务有实时的消息需要通知响应状态的客户端时,服务推送消息给客户端.

这里假设我们有成千上万的客户在触发该事件(比如订票,付款等),我们不希望这些操作直接与服务的数据库交互,让他们模拟现实人类的情况,排队处理吧. 这时候消息代理的用场就出现了.

2.8.0 接入服务

我们将动物园的预定消息,通过代理服务发布到订阅者那里。 本质上讲,代理服务将数据取出,并转发到相关频道的全部用户中。

缓存的可以帮助我们提高速度,经常需要查询的房间 放到缓存中 将帮助提高 软件 响应速度。

在矩阵中inspect 是类似的 作用

类型检查、获取源代码、检查类与函数、检查解释器的调用堆栈

 insoect.getfullargspec()
 获取可调用对象参数的名称和默认值。

返回一个包含七件事的元组:

	    (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations)。
	    'args' 是参数名称的列表。
	    'varargs' 和 'varkw' 是 * 和 ** 参数的名称或 None。
	    'defaults' 是最后 n 个参数的默认值的 n 元组。
	    'kwonlyargs' 是一个仅包含关键字的参数名称列表。
	    'kwonlydefaults' 是一个将名称从 kwonlyargs 映射到默认值的字典。
	    “注释”是将参数名称映射到注释的字典。

与inspect.signature()的显着区别:

	      - 始终报告“self”参数,即使是绑定方法
	      - 由 __wrapped__ 定义的包装链 * 不* 自动解包

我们在这一小节使用代理,监控动物园的预定信息,它们通过缓存发布到代理服务中。

2.8.1 创建代理处理函数

将动物园的订购信息,发布到对应的订阅者客户端, 我们获取消息的主题信息, 并根据主题封装消息,最后返回给对应客户端

	func DealMsg(txt string, msgMap map[string]string, stream *Proxyer) bool {

		msgMap["message"] = txt  

		bstr, err := json.Marshal(msgMap)
		if err != nil {
			return false
		}

		m := stream.MsgTopic(bstr)
		
		return stream.PublishSendToSuber(websocket.TextMessage, m.Topic, []byte(bstr))
	}

将接收到的消息,发给相关的订阅者用户

   PublishSendToSuber(msgType int, topic string, payload []byte) bool {

		var ss bool = true
		for _, p := range ps.Subscriptions {
			if topic != p.Topic {
				continue
			}
			sf := p.Clients.send(websocket.TextMessage, payload) 
		}

而代理管理者,保持了相关用户和订阅者的信息,必然哪些客户端订阅了 该 topic

    Proxyer struct {
	    Clients       []*WsClient
            Subscriptions []Subscribes 
	}

订阅者客户端具体信息如下,保存客户端信息:

	Subscribes struct {
		Topic   string
		Clients *WsClient
	}

并且通过ConnMsg , 从目标服务拉取并保持消息操作

	func ConnMsg(stream *Proxyer)  {

		go func() {
			SubService(stream)
		}()


	}

最后在接口 message 中绑定该消息代理服务

  wsHandler(ctx *gin.Context) {
    
    ....
  	go proxyer.ConnMsg(Ps)
  }

  GET("/message" wsHandler)

如此,在有参观者预定动物园票据时,我们将可以从代理得到实时的消息。

2.8.2 消息接收和观测

在客户端保持连接时,如果该客户端预定了相关主题的消息,服务器将实时的返回给对应的客户端。

使用启动服务

    go run main.go

浏览器连接 127.0.0.1:3002
在浏览器控制台:

客户端二 先订阅预定信息

 client2.send('{"action":"subscribe","topic":"booked" }')

客户端一预定票据时

client1.send('{"action":"publish","topic":"booked", "message":"1673254205340:2199:32925:en00030"}')

客户端二 将实时接收消息并在浏览器界面显示

 {"action":"publish","message":"1673254205340:2119:32925:en00030","topic":"booked"}

使用效果

从以下地址获取代理服务,
使用示例代理程序,其地址为:

完整获取该目录

 https://github.com/hahamx/examples/tree/main/service/proxyer/  

执行

  ./proxyer.exe  

启动时指定您本地的缓存地址,比如:

这将触发提示键入缓存地址,

 请键入缓存地址,例如:localhost:6379 :  localhost:6379  

	board monitor::3002

打开浏览器,访问地址 http://127.0.0.1:3002/ 将获得如下提示

    Welcome to ws client.
    connected to server:ws://localhost:3002/message

最后使用客户端模拟预定 动物园门票 , 这与上一节 2.6 的使用方式的一致,就不再重复。

效果如下:

  ![](https://bbs-img.huaweicloud.com/blogs/img/20240222/1708566942695584039.png)

3 小结

这是一个从0实现的 简单的消息代理服务,与主服务不同,它可以在任何地方使用。 当有人在主服务器预定票据时,在代理服务中可以实时观测到。

以此做为一个队列服务,可以实现预定商品的买卖和生产。
本节示例程序地址:

 https://github.com/hahamx/examples/tree/main/service/proxyer/ 

本部分完。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。