初始化项目

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

View File

@@ -0,0 +1,16 @@
package mqMgr
// 消息队列配置对象
type QueueConfig struct {
// 地域
Region string
// 队列名称
QueueName string
// API密钥Id
SecretId string
// API密钥key
SecretKey string
}

View File

@@ -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 浏览器限制为 2KFirefox 限制为 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,
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,13 @@
package model
// 错误信息对象
type ErrorInfo struct {
// 错误码
Code int `json:"code"`
// 错误提示信息
Message string `json:"message"`
// 对应删除失败的消息句柄
ReceiptHandle string `json:"receiptHandle"`
}

View File

@@ -0,0 +1,13 @@
package model
// IRequest 请求对象接口
type IRequest interface {
// GetActionName 获取方法名
GetActionName() string
// SetCommonRequestObject 设置公共请求对象
SetCommonRequest(*CommonRequest)
// AssembleParamMap 组装参数字典
AssembleParamMap() map[string]string
}

View File

@@ -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"`
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -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{}
}

View File

@@ -0,0 +1,9 @@
package model
// 返回结果状态
type ResultStatus int
const (
Success = 0
NoMessage = 7000
)

View File

@@ -0,0 +1,7 @@
package mqMgr
// 网络类型:内网、外网
const (
MQ_NETWORK_INTERNAL = "tencentyun"
MQ_NETWORK_PUBLIC = "tencenttdmq" //"qcloud" //todo:切换成tdmq之后该值需要修改成tencenttdmq
)

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
}

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
}

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)
}

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
}
}

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"
)

View File

@@ -0,0 +1,7 @@
package mqMgr
// 消息队列类型:消息队列、消息主题
const (
MQ_TYPE_QUEUE = "queue"
MQ_TYPE_TOPIC = "topic"
)

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
}