初始化项目

This commit is contained in:
皮蛋13361098506
2025-01-06 16:01:02 +08:00
commit 1b77f62820
575 changed files with 69193 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
package mqMgr
// 消息队列配置对象
type QueueConfig struct {
// 地域
Region string
// 队列名称
QueueName string
// API密钥Id
SecretId string
// API密钥key
SecretKey string
}
@@ -0,0 +1,109 @@
/*
请求结构简介
最近更新时间:2019-08-01 19:14:44
编辑 查看pdf
在这篇文章中:
服务地址
通信协议
请求方法
请求参数
字符编码
对腾讯云的 API 接口的调用是通过向腾讯云 API 的服务端地址发送请求,并按照接口说明在请求中加入相应的请求参数来完成的。腾讯云 API 的请求结构由:服务地址、通信协议、请求方法、请求参数和字符编码组成。具体描述如下:
服务地址
腾讯云 API 的服务接入地址与具体模块相关,详细请参见各接口相关描述。
通信协议
腾讯云 API 的大部分接口都通过 HTTPS 进行通信,为您提供高安全性的通信通道。
请求方法
腾讯云 API 同时支持 POST 和 GET 请求。
注意:
POST 和 GET 请求不能混合使用,若使用 GET 方式,则参数均从 Querystring 取得;
若使用 POST 方式,则参数均从 Request Body 中取得,而 Querystring 中的参数将忽略。
两种请求方式的参数格式规则相同,一般情况下使用 GET 请求,当参数字符串过长时推荐使用 POST。
如果用户的请求方法是 GET,则对所有请求参数值均需要做 URL 编码,若为 POST,则无需对参数编码。
GET 请求的最大长度根据不同的浏览器和服务器设置有所不同,例如,传统 IE 浏览器限制为 2K,Firefox 限制为 8K;对于一些参数较多、长度较长的 API 请求,建议您使用 POST 方法以免在请求过程中会由于字符串超过最大长度而导致请求失败。
对于 POST 请求,您需要使用 x-www-form-urlencoded 的形式传参,因为云 API 侧是从 $_POST 中取出请求参数的。
请求参数
腾讯云 API 的每个请求都需要指定两类参数:公共请求参数以及接口请求参数。其中公共请求参数是每个接口都要用到的请求参数,具体可参见 公共请求参数,而接口请求参数是各个接口所特有的,具体见各个接口的“请求参数”描述。
字符编码
腾讯云 API 的请求及返回结果均使用 UTF-8 字符集进行编码。
*/
package model
import (
"fmt"
"time"
"goutil/mathUtil"
)
// CommonRequest 公共请求参数对象
type CommonRequest struct {
// Action 指令接口名称(必须)
Action string
// 地域参数(必须)
Region string
// Timestamp 当前UNIX时间戳(必须)
Timestamp uint64
// Nonce 随机正整数(必须)
Nonce uint32
// SecretId 在云API密钥上申请的标识身份的SecretId(必须)
SecretId string
// 请求签名,用来验证此次请求的合法性,需要用户根据实际的输入参数计算得出。
Signature string
// 签名方式,目前支持 HmacSHA256 和 HmacSHA1。只有指定此参数为 HmacSHA256 时,才使用 HmacSHA256 算法验证签名,其他情况均使用 HmacSHA1 验证签名。
SignatureMethod string
// 队列名称(此属性虽然不是API文档中的公共属性,但是在队列模型中确实事实上的公共属性,所以将其转移到此处)
queueName string
}
// AssembleParamMap 组装请求参数字典
// 返回值
// map[string]interface{}:请求参数字
func (this *CommonRequest) AssembleParamMap() map[string]string {
result := make(map[string]string)
// 组装参数
result["Action"] = this.Action
result["Region"] = this.Region
result["Timestamp"] = fmt.Sprintf("%d", this.Timestamp)
result["Nonce"] = fmt.Sprintf("%d", this.Nonce)
result["SecretId"] = this.SecretId
result["SignatureMethod"] = this.SignatureMethod
result["queueName"] = this.queueName
return result
}
// NewCommonRequest 新建公共请求参数对象
// 参数
// action:指令接口名称
// region:地域
// secretId:在云API密钥上申请的标识身份的SecretId
// queueName:队列名称
// 返回值
// *CommonRequest:公共请求参数对象
func NewCommonRequest(action, region, secretId, queueName string) *CommonRequest {
return &CommonRequest{
Action: action,
Region: region,
Timestamp: uint64(time.Now().Unix()),
Nonce: mathUtil.GetRand().Uint32(),
SecretId: secretId,
SignatureMethod: "HmacSHA256",
queueName: queueName,
}
}
@@ -0,0 +1,28 @@
package model
// CommonResponse 公共请求结果对象
type CommonResponse struct {
// 错误码
Code ResultStatus `json:"code"`
// 错误提示信息
Message string `json:"message"`
// 服务器生成的请求Id
RequestId string `json:"requestId"`
}
// IsSuccess 请求结果是否成功
func (this *CommonResponse) IsSuccess() bool {
return this.Code == Success
}
// IsFailure 请求结果是否失败
func (this *CommonResponse) IsFailure() bool {
return this.Code != Success
}
// HasMessage 请求结果是否有信息
func (this *CommonResponse) HaveNoMessage() bool {
return this.Code == NoMessage
}
+13
View File
@@ -0,0 +1,13 @@
package model
// 错误信息对象
type ErrorInfo struct {
// 错误码
Code int `json:"code"`
// 错误提示信息
Message string `json:"message"`
// 对应删除失败的消息句柄
ReceiptHandle string `json:"receiptHandle"`
}
+13
View File
@@ -0,0 +1,13 @@
package model
// IRequest 请求对象接口
type IRequest interface {
// GetActionName 获取方法名
GetActionName() string
// SetCommonRequestObject 设置公共请求对象
SetCommonRequest(*CommonRequest)
// AssembleParamMap 组装参数字典
AssembleParamMap() map[string]string
}
@@ -0,0 +1,25 @@
package model
// 消息对象
type MessageInfo struct {
// 本次消费的消息正文
MsgBody string `json:"msgBody"`
// 服务器生成消息的唯一标识Id
MsgId string `json:"msgId"`
// 每次消费返回唯一的消息句柄。用于删除该消息,仅上一次消费时产生的消息句柄能用于删除消息。
ReceiptHandle string `json:"receiptHandle"`
// 消费被生产出来,进入队列的时间
EnqueueTime int64 `json:"enqueueTime"`
// 第一次消费该消息的时间
FirstDequeueTime int64 `json:"firstDequeueTime"`
// 消息的下次可见(可再次被消费)时间
NextVisibleTime int64 `json:"nextVisibleTime"`
// 消息被消费的次数
DequeueCount int64 `json:"dequeueCount"`
}
@@ -0,0 +1,55 @@
package model
import (
"fmt"
)
// 批量删除消息请求对象
type BatchDeleteMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 上次消费消息时返回的消息句柄列表
receiptHandleList []string
}
// 获取请求方法名
func (this *BatchDeleteMessageRequest) GetActionName() string {
return "BatchDeleteMessage"
}
// SetCommonRequestObject 设置公共请求对象
func (this *BatchDeleteMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *BatchDeleteMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
for index, msg := range this.receiptHandleList {
key := fmt.Sprintf("receiptHandle.%d", index)
paramMap[key] = msg
}
return paramMap
}
func NewBatchDeleteMessageRequest(receiptHandleList []string) *BatchDeleteMessageRequest {
return &BatchDeleteMessageRequest{
receiptHandleList: receiptHandleList,
}
}
// 批量删除消息请求返回结果对象
type BatchDeleteMessageResponse struct {
*CommonResponse
// 无法成功删除的错误列表
ErrorList []*ErrorInfo `json:"errorList"`
}
func NewBatchDeleteMessageResponse() *BatchDeleteMessageResponse {
return &BatchDeleteMessageResponse{}
}
@@ -0,0 +1,57 @@
package model
import (
"fmt"
)
// 批量消费消息请求对象
type BatchReceiveMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 本次消费的消息数量。取值范围 1-16。(必须)
numOfMsg int
// 本次请求的长轮询等待时间(非必须)
pollingWaitSeconds int
}
// 获取请求方法名
func (this *BatchReceiveMessageRequest) GetActionName() string {
return "BatchReceiveMessage"
}
// SetCommonRequest 设置公共请求对象
func (this *BatchReceiveMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *BatchReceiveMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
paramMap["numOfMsg"] = fmt.Sprintf("%d", this.numOfMsg)
paramMap["pollingWaitSeconds"] = fmt.Sprintf("%d", this.pollingWaitSeconds)
return paramMap
}
func NewBatchReceiveMessageRequest(numOfMsg, pollingWaitSeconds int) *BatchReceiveMessageRequest {
return &BatchReceiveMessageRequest{
numOfMsg: numOfMsg,
pollingWaitSeconds: pollingWaitSeconds,
}
}
// 批量消费消息请求返回结果对象
type BatchReceiveMessageResponse struct {
*CommonResponse
// message信息列表
MsgInfoList []*MessageInfo `json:"msgInfoList"`
}
func NewBatchReceiveMessageResponse() *BatchReceiveMessageResponse {
return &BatchReceiveMessageResponse{}
}
@@ -0,0 +1,60 @@
package model
import (
"fmt"
)
// 批量发送消息请求对象
type BatchSendMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 消息正文列表
msgBodyList []string
// 单位为秒,表示该消息发送到队列后,需要延时多久用户才可见该消息。
delaySeconds int
}
// 获取请求方法名
func (this *BatchSendMessageRequest) GetActionName() string {
return "BatchSendMessage"
}
// SetCommonRequest 设置公共请求对象
func (this *BatchSendMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *BatchSendMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
for index, msg := range this.msgBodyList {
key := fmt.Sprintf("msgBody.%d", index)
paramMap[key] = msg
}
paramMap["delaySeconds"] = fmt.Sprintf("%d", this.delaySeconds)
return paramMap
}
func NewBatchSendMessageRequest(msgBodyList []string, delaySeconds int) *BatchSendMessageRequest {
return &BatchSendMessageRequest{
msgBodyList: msgBodyList,
delaySeconds: delaySeconds,
}
}
// 批量发送消息请求返回结果对象
type BatchSendMessageResponse struct {
*CommonResponse
// message信息列表
MsgInfoList []*MessageInfo `json:"msgInfoList"`
}
func NewBatchSendMessageResponse() *BatchSendMessageResponse {
return &BatchSendMessageResponse{}
}
@@ -0,0 +1,46 @@
package model
// 删除消息请求对象l
type DeleteMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 上次消费返回唯一的消息句柄,用于删除消息。(必须)
receiptHandle string
}
// 获取请求方法名
func (this *DeleteMessageRequest) GetActionName() string {
return "DeleteMessage"
}
// SetCommonRequest 设置公共请求对象
func (this *DeleteMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *DeleteMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
paramMap["receiptHandle"] = this.receiptHandle
return paramMap
}
func NewDeleteMessageRequest(receiptHandle string) *DeleteMessageRequest {
return &DeleteMessageRequest{
receiptHandle: receiptHandle,
}
}
// 删除消息请求返回结果对象
type DeleteMessageResponse struct {
// 公共请求结果
*CommonResponse
}
func NewDeleteMessageResponse() *DeleteMessageResponse {
return &DeleteMessageResponse{}
}
@@ -0,0 +1,58 @@
package model
// 消费消息请求对象
type GetQueueAttributesRequest struct {
// 公共请求参数
common *CommonRequest
}
// 获取请求方法名
func (this *GetQueueAttributesRequest) GetActionName() string {
return "GetQueueAttributes"
}
// SetCommonRequestObject 设置公共请求对象
func (this *GetQueueAttributesRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *GetQueueAttributesRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
return paramMap
}
func NewGetQueueAttributesRequest() *GetQueueAttributesRequest {
return &GetQueueAttributesRequest{}
}
// 消费消息请求返回结果对象
type GetQueueAttributesResponse struct {
*CommonResponse
MaxMsgHeapNum int `json:"maxMsgHeapNum"` // 最大堆积消息数。取值范围在公测期间为 1,000,000 - 10,000,000,正式上线后范围可达到 1000,000-1000,000,000。默认取值在公测期间为 10,000,000,正式上线后为 100,000,000。
PollingWaitSeconds int `json:"pollingWaitSeconds"` // 消息接收长轮询等待时间。取值范围0 - 30秒,默认值0。
VisibilityTimeout int `json:"visibilityTimeout"` // 消息可见性超时。取值范围1 - 43200秒(即12小时内),默认值30。
MaxMsgSize int `json:"maxMsgSize"` // 消息最大长度。取值范围1024 - 1048576 Byte(即1K - 1024K),默认值65536。
MsgRetentionSeconds int `json:"msgRetentionSeconds"` // 消息保留周期。取值范围60-1296000秒(1min-15天),默认值345600秒(4 天)。
CreateTime int `json:"createTime"` // 队列的创建时间。返回 Unix 时间戳,精确到秒。
LastModifyTime int `json:"lastModifyTime"` // 最后一次修改队列属性的时间。返回 Unix 时间戳,精确到秒。
ActiveMsgNum int `json:"activeMsgNum"` // 在队列中处于 Active 状态(不处于被消费状态)的消息总数,为近似值。
InactiveMsgNum int `json:"inactiveMsgNum"` // 在队列中处于 Inactive 状态(正处于被消费状态)的消息总数,为近似值。
RewindSeconds int `json:"rewindSeconds"` // 回溯队列的消息回溯时间最大值,取值范围0 - 43200秒,0表示不开启消息回溯。
RewindmsgNum int `json:"rewindmsgNum"` // 已调用 DelMsg 接口删除,但还在回溯保留时间内的消息数量。
MinMsgTime int `json:"minMsgTime"` // 消息最小未消费时间,单位为秒。
QueueName string `json:"queueName"` // 消息队列名字。
QueueId string `json:"queueId"` // 消息队列ID。
CreateUin int64 `json:"createUin"` // 创建者Uin。
Bps int `json:"Bps"` // 带宽限制。
Qps int `json:"qps"` // 每秒钟生产消息条数的限制,消费消息的大小是该值的1.1倍。
Tags []string `json:"tags"` // 关联的标签。
}
func NewGetQueueAttributesResponse() *GetQueueAttributesResponse {
return &GetQueueAttributesResponse{}
}
@@ -0,0 +1,52 @@
package model
import (
"fmt"
)
// 消费消息请求对象
type ReceiveMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 本次请求的长轮询等待时间。取值范围0 - 30秒,如果不设置该参数,则默认使用队列属性中的 pollingWaitSeconds 值。
pollingWaitSeconds int
}
// 获取请求方法名
func (this *ReceiveMessageRequest) GetActionName() string {
return "ReceiveMessage"
}
// SetCommonRequestObject 设置公共请求对象
func (this *ReceiveMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *ReceiveMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
paramMap["pollingWaitSeconds"] = fmt.Sprintf("%d", this.pollingWaitSeconds)
return paramMap
}
func NewReceiveMessageRequest(pollingWaitSeconds int) *ReceiveMessageRequest {
return &ReceiveMessageRequest{
pollingWaitSeconds: pollingWaitSeconds,
}
}
// 消费消息请求返回结果对象
type ReceiveMessageResponse struct {
*CommonResponse
// 消息对象
*MessageInfo
}
func NewReceiveMessageResponse() *ReceiveMessageResponse {
return &ReceiveMessageResponse{}
}
@@ -0,0 +1,58 @@
package model
import (
"fmt"
)
// 发送消息请求对象
type SendMessageRequest struct {
// 公共请求参数
common *CommonRequest
// 消息正文
msgBody string
// 单位为秒,表示该消息发送到队列后,需要延时多久用户才可见该消息。
delaySeconds int
}
// 获取请求方法名
func (this *SendMessageRequest) GetActionName() string {
return "SendMessage"
}
// SetCommonRequest 设置公共请求对象
func (this *SendMessageRequest) SetCommonRequest(commonRequest *CommonRequest) {
this.common = commonRequest
}
// AssembleParamMap 组装参数字典
// 返回值
// map[string]string:请求参数字典
func (this *SendMessageRequest) AssembleParamMap() map[string]string {
paramMap := this.common.AssembleParamMap()
paramMap["msgBody"] = this.msgBody
paramMap["delaySeconds"] = fmt.Sprintf("%d", this.delaySeconds)
return paramMap
}
func NewSendMessageRequest(msgBody string, delaySeconds int) *SendMessageRequest {
return &SendMessageRequest{
msgBody: msgBody,
delaySeconds: delaySeconds,
}
}
// 发送消息请求返回结果对象
type SendMessageResponse struct {
// 公共请求结果
*CommonResponse
// 服务器生成消息的唯一标识Id
MsgId string `json:"msgId"`
}
func NewSendMessageResponse() *SendMessageResponse {
return &SendMessageResponse{}
}
@@ -0,0 +1,9 @@
package model
// 返回结果状态
type ResultStatus int
const (
Success = 0
NoMessage = 7000
)
+7
View File
@@ -0,0 +1,7 @@
package mqMgr
// 网络类型:内网、外网
const (
MQ_NETWORK_INTERNAL = "tencentyun"
MQ_NETWORK_PUBLIC = "tencenttdmq" //"qcloud" //todo:切换成tdmq之后,该值需要修改成:tencenttdmq
)
+378
View File
@@ -0,0 +1,378 @@
/*
我们使用腾讯云的CMQ作为公司的消息队列基础服务
产品的说明文档:https://cloud.tencent.com/document/product/406
*/
package mqMgr
import (
"encoding/json"
"errors"
"fmt"
"strings"
. "Framework/mqMgr/model"
"goutil/webUtil"
)
const (
EMPTY_MESSAGE_ERROR = "消息为空"
EMPTY_HANDLE_ERROR = "句柄为空"
MIN_DELAY_SECONDS = 0
MIN_POLLING_WAIT_SECONDS = 0
MAX_POLLING_WAIT_SECONDS = 30
MAX_BATCH_COUNT = 16
EMPTY_BATCH_LIST_ERROR = "批量消息为空"
EXCEED_MAX_BATCH_COUNT_ERROR = "批量消息数量超过上限,最多为16条"
)
// Queue对象
type Queue struct {
// 地域
region string
// 网络类型:内网、外网
network string
// 队列名称
queueName string
// API密钥Id
secretId string
// API密钥key
secretKey string
}
// 执行操作
func (this *Queue) action(requestObj IRequest, responseObj interface{}) error {
requestObj.SetCommonRequest(NewCommonRequest(requestObj.GetActionName(), this.region, this.secretId, this.queueName))
// 组装请求url
paramMap := requestObj.AssembleParamMap()
url, signature, err := AssembleUrl(this.region, this.network, MQ_TYPE_QUEUE, this.secretKey, paramMap)
if err != nil {
return err
}
paramMap["Signature"] = signature
//请求url,请求头
header := webUtil.GetFormHeader()
transport := webUtil.NewTransport()
transport.DisableKeepAlives = true
transport = webUtil.GetTimeoutTransport(transport, 10)
statusCode, result, err := webUtil.PostMapData(url, paramMap, header, transport)
//statusCode, result, err := webUtil.PostMapData(url, paramMap, header, nil)
if err != nil {
return err
}
if statusCode != 200 {
return fmt.Errorf("Wrong status from server:%d", statusCode)
}
// 解析请求结果
err = json.Unmarshal(result, responseObj)
return err
}
// 处理发送消息延迟的时间
func (this *Queue) handleDelaySeconds(delaySeconds int) int {
if delaySeconds < MIN_DELAY_SECONDS {
delaySeconds = MIN_DELAY_SECONDS
}
return delaySeconds
}
// 处理当没有消息是轮询等待的时间
func (this *Queue) handlePollingWaitSeconds(pollingWaitSeconds int) int {
if pollingWaitSeconds < MIN_POLLING_WAIT_SECONDS {
pollingWaitSeconds = MIN_POLLING_WAIT_SECONDS
} else if pollingWaitSeconds > MAX_POLLING_WAIT_SECONDS {
pollingWaitSeconds = MAX_POLLING_WAIT_SECONDS
}
return pollingWaitSeconds
}
// 验证批量处理的列表
func (this *Queue) validBatchList(list []string) error {
if list == nil || len(list) == 0 {
return errors.New(EMPTY_BATCH_LIST_ERROR)
}
if len(list) > MAX_BATCH_COUNT {
return errors.New(EXCEED_MAX_BATCH_COUNT_ERROR)
}
return nil
}
// error:错误对象
func (this *Queue) GetQueueAttributes() (err error) {
// 逻辑处理
requestObj := NewGetQueueAttributesRequest()
responseObj := NewGetQueueAttributesResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
return
}
// SendMessage 发送单条消息
// 参数
// message:消息内容
// delaySeconds:单位为秒,表示该消息发送到队列后,需要延时多久用户才可见该消息。
// 返回值
// error:错误对象
func (this *Queue) SendMessage(message string, delaySeconds int) (err error) {
// 参数验证和处理
if message == "" {
err = errors.New(EMPTY_MESSAGE_ERROR)
return
}
delaySeconds = this.handleDelaySeconds(delaySeconds)
// 逻辑处理
requestObj := NewSendMessageRequest(message, delaySeconds)
responseObj := NewSendMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
return
}
// BatchSendMessage 批量发送消息
// 参数
// messageList:消息内容列表
// delaySeconds:单位为秒,表示该消息发送到队列后,需要延时多久用户才可见该消息。
// 返回值
// error:错误对象
func (this *Queue) BatchSendMessage(messageList []string, delaySeconds int) (err error) {
// 参数验证和处理
err = this.validBatchList(messageList)
if err != nil {
return
}
delaySeconds = this.handleDelaySeconds(delaySeconds)
// 逻辑处理
requestObj := NewBatchSendMessageRequest(messageList, delaySeconds)
responseObj := NewBatchSendMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
return
}
// Receive 消费单条消息
// pollingWaitSeconds:本次请求的长轮询等待时间。取值范围0 - 30秒,如果不设置该参数,则默认使用队列属性中的 pollingWaitSeconds 值。
// 返回值
// receiptHandle:消息句柄
// message:消息内容
// exist:是否存在数据
// err:错误对象
func (this *Queue) ReceiveMessage(pollingWaitSeconds int) (receiptHandle, message string, exist bool, err error) {
// 参数验证和处理
pollingWaitSeconds = this.handlePollingWaitSeconds(pollingWaitSeconds)
// 逻辑处理
requestObj := NewReceiveMessageRequest(pollingWaitSeconds)
responseObj := NewReceiveMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
// 忽略掉没有消息的错误
if responseObj.HaveNoMessage() {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
receiptHandle = responseObj.ReceiptHandle
message = responseObj.MsgBody
exist = true
return
}
// BatchReceiveMessage 批量消费消息
// 参数
// numOfMsg:本次消费的消息数量
// pollingWaitSeconds:本次请求的长轮询等待时间。取值范围0 - 30秒,如果不设置该参数,则默认使用队列属性中的 pollingWaitSeconds 值。
// 返回值
// receiptHandleList:消息句柄列表
// messageList:消息内容列表
// exist:是否存在数据
// err:错误对象
func (this *Queue) BatchReceiveMessage(numOfMsg, pollingWaitSeconds int) (receiptHandleList, messageList []string, exist bool, err error) {
// 参数验证和处理
if numOfMsg > MAX_BATCH_COUNT {
err = errors.New(EXCEED_MAX_BATCH_COUNT_ERROR)
return
}
pollingWaitSeconds = this.handlePollingWaitSeconds(pollingWaitSeconds)
// 逻辑处理
requestObj := NewBatchReceiveMessageRequest(numOfMsg, pollingWaitSeconds)
responseObj := NewBatchReceiveMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
// 忽略掉没有消息的错误
if responseObj.HaveNoMessage() {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
// 组装返回
receiptHandleList = make([]string, 0, numOfMsg)
messageList = make([]string, 0, numOfMsg)
for _, msgInfo := range responseObj.MsgInfoList {
receiptHandleList = append(receiptHandleList, msgInfo.ReceiptHandle)
messageList = append(messageList, msgInfo.MsgBody)
}
exist = true
return
}
// DeleteMessage 删除单条消息
// 参数
// receiptHandle:消息句柄
// 返回值
// error:错误对象
func (this *Queue) DeleteMessage(receiptHandle string) (err error) {
// 参数验证和处理
if receiptHandle == "" {
err = errors.New(EMPTY_HANDLE_ERROR)
return
}
// 逻辑处理
requestObj := NewDeleteMessageRequest(receiptHandle)
responseObj := NewDeleteMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return err
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
return
}
return
}
// BatchDeleteMessage 批量删除消息
// 参数
// receiptHandleList:消息句柄列表
// 返回值
// errorMap:删除错误的字典(key:删除失败的消息句柄;value:删除失败的原因)
// err:错误对象
func (this *Queue) BatchDeleteMessage(receiptHandleList []string) (errorMap map[string]string, err error) {
// 参数验证和处理
err = this.validBatchList(receiptHandleList)
if err != nil {
return
}
// 逻辑处理
requestObj := NewBatchDeleteMessageRequest(receiptHandleList)
responseObj := NewBatchDeleteMessageResponse()
// 发送请求
err = this.action(requestObj, &responseObj)
if err != nil {
return
}
if responseObj.IsFailure() {
err = errors.New(responseObj.Message)
// 组装返回
errorMap = make(map[string]string)
for _, errInfo := range responseObj.ErrorList {
errorMap[errInfo.ReceiptHandle] = errInfo.Message
}
return
}
return
}
// 创建新的Queue对象
func NewQueue(region, queueName, secretId, secretKey string) *Queue {
queueConfigObj := &QueueConfig{
Region: region,
QueueName: queueName,
SecretId: secretId,
SecretKey: secretKey,
}
return NewQueueByConfig(queueConfigObj)
}
// 通过队列配置对象创建新的Queue对象
func NewQueueByConfig(queueConfigObj *QueueConfig) *Queue {
queueObj := &Queue{
region: queueConfigObj.Region,
network: MQ_NETWORK_INTERNAL,
queueName: queueConfigObj.QueueName,
secretId: queueConfigObj.SecretId,
secretKey: queueConfigObj.SecretKey,
}
err := queueObj.GetQueueAttributes()
if err != nil {
if strings.Contains(err.Error(), MQ_NETWORK_INTERNAL) {
queueObj.network = MQ_NETWORK_PUBLIC
}
}
return queueObj
}
+183
View File
@@ -0,0 +1,183 @@
package mqMgr
import (
"context"
"errors"
"fmt"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
// Queue对象
type HSYQueue struct {
conn *amqp.Connection
channel *amqp.Channel
// 队列名称
queueName string
// 交换机
exchange string
// routing Key
routingKey string
//MQ链接字符串
mqurl string
}
// 消息队列配置对象
type HSYQueueConfig struct {
QueueName string
Exchange string
RoutingKey string
Network string
Port int
UserName string
Password string
}
// 创建新的Queue对象
func NewHSYQueue(queueName, exchange, routingKey, userName, password string, network string, port int) *HSYQueue {
queueConfigObj := &HSYQueueConfig{
QueueName: queueName,
Exchange: exchange,
RoutingKey: routingKey,
UserName: userName,
Password: password,
Network: network,
Port: port,
}
return NewHSYQueueByConfig(queueConfigObj)
}
// 通过队列配置对象创建新的Queue对象
func NewHSYQueueByConfig(queueConfigObj *HSYQueueConfig) *HSYQueue {
queueObj := &HSYQueue{
queueName: queueConfigObj.QueueName,
exchange: queueConfigObj.Exchange,
routingKey: queueConfigObj.RoutingKey,
mqurl: fmt.Sprintf("amqp://%s:%s@%s:%d/", queueConfigObj.UserName, queueConfigObj.Password, queueConfigObj.Network, queueConfigObj.Port),
}
if err := queueObj.GetQueueAttributes(); err != nil {
return nil
}
return queueObj
}
// 连接RabbitMQ服务器
func (this *HSYQueue) GetQueueAttributes() (err error) {
this.conn, err = amqp.Dial(this.mqurl)
if err != nil {
return
}
this.channel, err = this.conn.Channel()
if err != nil {
return
}
return
}
// 释放连接
func (this *HSYQueue) ReleaseRes() {
this.conn.Close()
this.channel.Close()
}
// SendMessage 发送单条消息
func (this *HSYQueue) SendMessage(message string, ex string) (err error) {
// 声明队列
_, err = this.channel.QueueDeclare(
this.queueName, // 队列名
true, // 是否持久化
false, // 是否自动删除(前提是至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。注意:生产者客户端创建这个队列,或者没有消费者客户端与这个队列连接时,都不会自动删除这个队列)
false, // 是否为排他队列(排他的队列仅对“首次”声明的conn可见[一个conn中的其他channel也能访问该队列],conn结束后队列删除)
false, // 是否阻塞
nil, // 额外属性
)
if err != nil {
err = errors.New("声明队列失败")
return
}
// 声明交换器
err = this.channel.ExchangeDeclare(
this.exchange, //交换器名
ex, //exchange type:一般用fanout、direct、topic
true, // 是否持久化
false, //是否自动删除(自动删除的前提是至少有一个队列或者交换器与这和交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑)
false, //设置是否内置的。true表示是内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式
false, // 是否阻塞
nil, // 额外属性
)
if err != nil {
err = errors.New("声明交换器失败")
return
}
// Binding
err = this.channel.QueueBind(
this.queueName, // 绑定的队列名称
this.routingKey, // bindkey 用于消息路由分发的key
this.exchange, // 绑定的exchange名
false, // 是否阻塞
nil, // 额外属性
)
if err != nil {
err = errors.New("绑定队列和交换器失败")
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err = this.channel.PublishWithContext(ctx,
this.exchange,
this.routingKey,
false,
false,
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(message),
})
if err != nil {
err = errors.New("发布消息失败")
return
}
return
}
// ReceiveMessage 消费单条消息
func (this *HSYQueue) ReceiveMessage() (msgs <-chan amqp.Delivery, err error) {
_, err = this.channel.QueueDeclare(
this.queueName, // 队列名
true, // 是否持久化
false, // 是否自动删除(前提是至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。注意:生产者客户端创建这个队列,或者没有消费者客户端与这个队列连接时,都不会自动删除这个队列)
false, // 是否为排他队列(排他的队列仅对“首次”声明的conn可见[一个conn中的其他channel也能访问该队列],conn结束后队列删除)
false, // 是否阻塞
nil, // 额外属性(我还不会用)
)
if err != nil {
err = errors.New("声明队列失败")
return
}
msgs, err = this.channel.Consume(
this.queueName, // 队列名
"", // 消费者名,用来区分多个消费者,以实现公平分发或均等分发策略
true, // 是否自动应答
false, // 是否排他
false, // 是否接收只同一个连接中的消息,若为true,则只能接收别的conn中发送的消息
true, // 队列消费是否阻塞
nil, // 额外属性
)
if err != nil {
err = errors.New("获取消息失败")
return
}
return
}
+44
View File
@@ -0,0 +1,44 @@
package mqMgr
import (
"fmt"
"testing"
"time"
)
var (
queueHSYObj = NewHSYQueue("q-1", "ex-1", "rkey-1", "guest", "guest", "127.0.0.1", 5672)
)
func TestHSYSendMessage(t *testing.T) {
message := "这是测试内容. Test"
if queueHSYObj == nil {
fmt.Println("初始化失败")
return
}
defer queueHSYObj.ReleaseRes()
err := queueHSYObj.SendMessage(message, "direct")
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
time.Sleep(time.Second)
go func() {
msgs, err := queueHSYObj.ReceiveMessage()
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
for msg := range msgs {
fmt.Println("收到消息", string(msg.Body))
}
}()
time.Sleep(time.Second)
}
+179
View File
@@ -0,0 +1,179 @@
package mqMgr
import (
"fmt"
"testing"
)
var (
queueObj = NewQueue(MQ_REGION_GUANGZHOU, "AD-Click", "AKIDjBUWGmIkDPJHCqRb2DCQM2RbUUL1MWMx", "RcldV6PRCBwUkjGVdVjiPq0IJ2VhEZtO")
)
func TestHandleDelaySeconds(t *testing.T) {
delaySeconds := -1
expected := 0
delaySeconds = queueObj.handleDelaySeconds(delaySeconds)
if delaySeconds != expected {
t.Errorf("Expected %d, but now got %d", expected, delaySeconds)
}
delaySeconds = 10
expected = 10
delaySeconds = queueObj.handleDelaySeconds(delaySeconds)
if delaySeconds != expected {
t.Errorf("Expected %d, but now got %d", expected, delaySeconds)
}
}
func TestHandlePollingWaitSeconds(t *testing.T) {
pollingWaitSeconds := -1
expected := 0
pollingWaitSeconds = queueObj.handlePollingWaitSeconds(pollingWaitSeconds)
if pollingWaitSeconds != expected {
t.Errorf("Expected %d, but now got %d", expected, pollingWaitSeconds)
}
pollingWaitSeconds = 100
expected = 30
pollingWaitSeconds = queueObj.handlePollingWaitSeconds(pollingWaitSeconds)
if pollingWaitSeconds != expected {
t.Errorf("Expected %d, but now got %d", expected, pollingWaitSeconds)
}
pollingWaitSeconds = 15
expected = 15
pollingWaitSeconds = queueObj.handlePollingWaitSeconds(pollingWaitSeconds)
if pollingWaitSeconds != expected {
t.Errorf("Expected %d, but now got %d", expected, pollingWaitSeconds)
}
}
func TestValidBatchList(t *testing.T) {
var list []string
expected := EMPTY_BATCH_LIST_ERROR
err := queueObj.validBatchList(list)
if err == nil {
t.Errorf("There should be an error, but now there isn't")
}
if err.Error() != expected {
t.Errorf("Expected %s, but now got %s", EMPTY_BATCH_LIST_ERROR, err.Error())
}
list = make([]string, 0, 16)
expected = EMPTY_BATCH_LIST_ERROR
err = queueObj.validBatchList(list)
if err == nil {
t.Errorf("There should be an error, but now there isn't")
}
if err.Error() != expected {
t.Errorf("Expected %s, but now got %s", EMPTY_BATCH_LIST_ERROR, err.Error())
}
for i := 0; i <= 2*MAX_BATCH_COUNT; i++ {
list = append(list, fmt.Sprintf("Test.%d", i))
}
expected = EXCEED_MAX_BATCH_COUNT_ERROR
err = queueObj.validBatchList(list)
if err == nil {
t.Errorf("There should be an error, but now there isn't")
}
if err.Error() != expected {
t.Errorf("Expected %s, but now got %s", EMPTY_BATCH_LIST_ERROR, err.Error())
}
}
func TestSendMessage(t *testing.T) {
message := "这是测试内容. Test"
// SendMessage
err := queueObj.SendMessage(message, 0)
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
// ReceiveMessage
receiptHandle, actualMessage, exist, err := queueObj.ReceiveMessage(3)
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
if exist == false {
t.Errorf("There should be one message, but now it's empty.")
return
}
if message != actualMessage {
t.Errorf("Expected %s, but got %s", message, actualMessage)
return
}
// DeleteMessage
err = queueObj.DeleteMessage(receiptHandle)
if err != nil {
t.Errorf("There should be no error, but now there is:%s", err)
return
}
// Verify
_, _, exist, err = queueObj.ReceiveMessage(3)
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
if exist {
t.Errorf("There should be no message, but now there is.")
return
}
}
func TestBatchSendMessage(t *testing.T) {
count := 5
messageList := make([]string, 0, count)
for i := 0; i < count; i++ {
messageList = append(messageList, fmt.Sprintf("This is for test:%d", i+1))
}
// BatchSendMessage
err := queueObj.BatchSendMessage(messageList, 0)
if err != nil {
t.Errorf("There should be no error, but now there is:%s", err)
return
}
// BatchReceiveMessage
receiptHandleList, retMessageList, exist, err := queueObj.BatchReceiveMessage(count, 3)
if err != nil {
t.Errorf("There should be no error, but now there is:%s", err)
return
}
if !exist || messageList == nil || len(messageList) != count {
t.Errorf("There should be %d messages, but now there isn't.", count)
return
}
for i := 0; i < count; i++ {
if messageList[i] != retMessageList[i] {
t.Errorf("Expected %s, but got %s", messageList[i], retMessageList[i])
return
}
}
// BatchDeleteMessage
_, err = queueObj.BatchDeleteMessage(receiptHandleList)
if err != nil {
t.Errorf("There should be no error, but now there is:%s", err)
return
}
// Verify
_, _, exist, err = queueObj.ReceiveMessage(3)
if err != nil {
t.Errorf("There should be no error, but now there is: %s", err)
return
}
if exist {
t.Errorf("There should be no message, but now there is.")
return
}
}
+18
View File
@@ -0,0 +1,18 @@
package mqMgr
// 地域
const (
MQ_REGION_GUANGZHOU = "gz"
MQ_REGION_SAHNGHAI = "sh"
MQ_REGION_BEIJING = "bj"
MQ_REGION_SHANGHAIJINRONG = "shjr"
MQ_REGION_SHENZHENJINRONG = "szjr"
MQ_REGION_HONGKONG = "hk"
MQ_REGION_CHENGDU = "cd"
MQ_REGION_CANADA = "ca"
MQ_REGION_UNITED_STATES_EAST = "use"
MQ_REGION_UNITED_STATES_WEST = "usw"
MQ_REGION_INDIA = "in"
MQ_REGION_THILAND = "th"
MQ_REGION_SINGAPORE = "sg"
)
+7
View File
@@ -0,0 +1,7 @@
package mqMgr
// 消息队列类型:消息队列、消息主题
const (
MQ_TYPE_QUEUE = "queue"
MQ_TYPE_TOPIC = "topic"
)
+129
View File
@@ -0,0 +1,129 @@
/*
url的格式如下:https://cmq-{$type}-{$region}.api.{$network}.com
其最终内容受到以下因素的影响:地域、网络、消息队列模型
地域
gz(广州)、sh(上海)、bj(北京)、shjr(上海金融)、szjr(深圳金融)、hk(中国香港)、cd(成都)、ca(北美)、usw(美西)、use(美东)、in(印度)、th(泰国)、sg(新加坡)
网络
外网接口请求域名后缀:api.qcloud.com
内网接口请求域名后缀:api.tencentyun.com
队列模型
请参照下面说明将域名中的 {$region} 替换成相应地域:
外网接口请求域名:https://cmq-queue-{$region}.api.qcloud.com
内网接口请求域名:http://cmq-queue-{$region}.api.tencentyun.com
主题模型
请参照下面说明将域名中的 {$region} 替换成相应地域:
外网接口请求域名:https://cmq-topic-{$region}.api.qcloud.com
内网接口请求域名:http://cmq-topic-{$region}.api.tencentyun.com
*/
package mqMgr
import (
"fmt"
"strings"
"goutil/securityUtil"
"goutil/stringUtil"
"goutil/webUtil"
)
func getPrefix(network string) string {
if network == MQ_NETWORK_INTERNAL {
return "http://"
}
return "https://"
}
// // 获取请求url
// // region:地域
// // network:网络类型:内网、外网
// // _type:消息队列类型:消息队列、消息主题
// // 返回:
// // 请求url
// func getHost(region, network, _type string) string {
// url := "cmq-{$type}-{$region}.api.{$network}.com"
// url = strings.Replace(url, "{$region}", region, 1)
// url = strings.Replace(url, "{$network}", network, 1)
// url = strings.Replace(url, "{$type}", _type, 1)
// return url
// }
// 获取请求url todo:切换成tdmq之后需要用这个方法
// region:地域
// network:网络类型:内网、外网
// _type:消息队列类型:消息队列、消息主题
// 返回:
// 请求url
func getHost(region, network, _type string) string {
var url string = ""
if network == MQ_NETWORK_INTERNAL {
url = "{$region}.mqadapter.cmq.{$network}.com"
} else {
url = "cmq-{$region}.public.{$network}.com"
}
url = strings.Replace(url, "{$region}", region, 1)
url = strings.Replace(url, "{$network}", network, 1)
return url
}
func getPath() string {
return "/v2/index.php"
}
func getMethod() string {
return "POST"
}
// AssembleUrl 组装请求url
// 参数
// region:地域
// network:网络类型:内网、外网
// _type:消息队列类型:消息队列、消息主题
// secretKey:密钥的key
// paramMap:参数字典
// 返回值
// string:组装好的请求url
// string:签名
// error:错误
func AssembleUrl(region, network, _type, secretKey string, paramMap map[string]string) (url, signature string, err error) {
// 1. 申请安全凭证(已经得到)
// 2. 生成签名串
// 2.1、对参数排序
// 2.2、拼接请求字符串
// 注意:
// “参数值”为原始值而非 url 编码后的值。
// 若输入参数中包含下划线,则需要将其转换为“.”。(指的是参数的名称,不是参数的值)
paramStr := webUtil.AssembleRequestParamSort(paramMap, true)
// 2.3、拼接签名原文字符串
host := getHost(region, network, _type)
path := getPath()
signatureSource := fmt.Sprintf("%s%s%s?%s", getMethod(), host, path, paramStr)
// 2.4、生成签名串
data, err := securityUtil.HmacSha256(signatureSource, secretKey)
if err != nil {
return
}
signature = string(stringUtil.Base64Encode2(data))
// 3. 签名串编码
// 注意:
// 生成的签名串并不能直接作为请求参数,需要对其进行 URL 编码。
// 如果用户的请求方法是 GET,则对所有请求参数值均需要做 URL 编码。
// 如果是POST,则不用进行URL编码
// signature = url.QueryEscape(signature)
// 将签名添加到参数集合中
url = fmt.Sprintf("%s%s%s", getPrefix(network), host, path)
return
}