消息收发问题
查询队列消息为什么返回404?
存在两种情况:
1)队列不存在,则返回404。
2)队列中没有消息,则等待waitseconds时间之后返回404。长轮询的最大等待时间为30秒。
更多信息,请参见ReceiveMessage和使用限制。
MNS拉取消息为什么会延迟?
MNS采用分布式存储架构,一个拉取请求的目标节点不一定恰好是存储消息的节点,因此可能出现消息拉取延迟。
您可以通过以下方式减少延迟:
1)将长轮询改为短轮询。
2)增加消费者数量。
MessageNotExist为什么在服务器上无法过滤返回null?
问题现象:
调用MNS queue.batchPopMessage()返回404并报错MessageNotExist,在本地环境中可以正常适配返回null,但在服务器环境中无法过滤该错误。
原因:
服务端不过滤MessageNotExist错误,是为了将该状态返回给客户端,以便客户端根据业务场景按需重新发起请求。服务端不会永久挂起(hang)等待消息。
清空队列之后,未发送消息但过了一段时间又出现了消息?
清空队列操作只会清理队列中尚未被消费的消息。已被拉取但未ACK确认的消息相当于已出队,不会被清空操作处理。
如果这些正在被消费的消息超过了消息可见时间(VisibilityTimeout)仍未返回ACK,消息会重新变为可见状态,回到队列中。这就是清空队列后仍然出现消息的原因。
MNS支持消息优先级(Priority)设置吗?
不支持。MNS不提供消息优先级设置功能。
开启日志功能后,消费者空拉会打印日志吗?
不会。消费者空拉(即队列中无消息时的拉取操作)不会产生日志记录。
网络与访问问题
MNS的内网访问地址是否只要同地域就能访问?
是的。MNS的内网访问地址只要在同一地域内即可正常访问。
MNS向HTTP推送消息时是否只能走公网?
目前MNS的HTTP推送仅支持公网。只要网络可达即可正常推送。如果您有私网推送的需求,请提交工单反馈。
MNS的HTTP订阅目标为API网关时,网关返回400导致消息无法到达业务侧?
问题原因:MNS服务端推送消息到API网关时被拒绝。
请检查HTTP订阅时配置的消息格式(JSON、XML或Simplified)是否与API网关的要求一致。
API网关会对请求体(Body)进行MD5校验,MNS也会计算MD5校验值并放入Content-MD5头部。如果两端校验结果不一致,API网关会直接返回400错误。
事件触发与订阅问题
通过事件总线获取队列中以#开头的消息时拿不到消息体?
当同时满足以下条件时,以#开头的消息通过事件总线获取时会丢失消息体:
1)向MNS主题发送消息,且消息投递到订阅的队列中。
2)通过MNS发送的消息未进行Base64编码。
3)在事件总线上配置事件源时,未勾选Base64编码解析选项。
这是业界通用组件的解析行为,存在兼容性风险,暂不做特殊处理。建议您在发送消息到MNS之前,先对消息内容进行Base64编码。
MNS触发FC(函数计算)时报错怎么办?
报错现象:
MNS通知FC时报错500,错误信息为ConvertError或ConvertNull。
原因及解决方案:
该报错通常是因为创建订阅时选择了不兼容的消息格式。FC仅支持STREAM和JSON格式。如果您通过SDK创建订阅,可能会误选为Simplified格式,从而导致该报错。请将订阅的消息格式修改为STREAM或JSON。
从OSS创建MNS规则触发FC时报错怎么办?
原因:
FC的触发器仅支持JSON和STREAM格式。直接从OSS创建事件规则并触发FC时,默认的消息格式为Simplified,会导致触发失败。
解决方案:
建议从OSS创建事件规则时不要同时创建目标。规则创建完成后,在FC侧创建MNS触发器,即可正常触发。
在OSS创建事件规则时创建跨账号订阅报错怎么办?
目前不支持在创建OSS事件规则时同时创建跨账号订阅。
如果确实需要跨账号订阅,可以分两步操作:
1)在OSS创建事件规则时,选择"一对多订阅",将订阅填写内容清空后提交保存。此时需要订阅到MNS的主题。
2)点击刚创建的事件规则,选择"查看订阅详情"跳转到MNS控制台,然后在对应的Topic下创建跨账号订阅。
权限与账号问题
RAM如何设置清除队列消息的权限(MNS:PurgeQueue)?
PurgeQueue操作目前不支持资源级鉴权,因此在RAM授权策略中,Resource字段必须设置为*。
MNS点击立即开通时提示实例ID不唯一是什么原因?
这通常是由于系统存在短暂延迟导致的。建议您等待一段时间后,刷新控制台页面重试即可。
开发与集成问题
C# MNS签名鉴权示例代码?
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public class MnsClient
{
private readonly string _accessKeyId;
private readonly string _accessKeySecret;
private readonly string _accountId;
private readonly string _region;
private readonly HttpClient _httpClient;
public MnsClient(string accessKeyId, string accessKeySecret, string accountId, string region = "cn-hangzhou")
{
_accessKeyId = accessKeyId;
_accessKeySecret = accessKeySecret;
_accountId = accountId;
_region = region;
_httpClient = new HttpClient();
}
public async Task SendMessageAsync(string queueName, string messageBody)
{
var endpoint = $"https://{_accountId}.mns.{_region}.aliyuncs.com";
var uri = $"/queues/{queueName}/messages";
var url = endpoint + uri;
// 1) 构造消息XML
var xml = $@"<?xml version=""1.0"" encoding=""UTF-8""?>
<Message>
<MessageBody>{SecurityElement.Escape(messageBody)}</MessageBody>
</Message>";
// 2) Content & headers
var content = new StringContent(xml, Encoding.UTF8, "text/xml");
string contentType = content.Headers.ContentType!.ToString();
string date = DateTime.UtcNow.ToString("r");
string contentMd5 = CalculateBase64Md5(xml);
// 3) 组装签名参数
var parameters = new Dictionary<string, string>(StringComparer.Ordinal)
{
["method"] = "POST",
["content-md5"] = contentMd5,
["content-type"] = contentType,
["uri"] = uri,
["x-mns-version"] = "2015-06-06"
};
string signContent = BuildSignContent(parameters, date);
// 4) 签名:HMAC-SHA1(secret, signContent) + Base64
string signature = ComputeSignatureHmacSha1Base64(signContent, _accessKeySecret);
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = content;
request.Content.Headers.ContentMD5 = Convert.FromBase64String(contentMd5);
request.Headers.Date = DateTimeOffset.Parse(date);
request.Headers.TryAddWithoutValidation("x-mns-version", "2015-06-06");
request.Headers.Authorization = new AuthenticationHeaderValue("MNS", $"{_accessKeyId}:{signature}");
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"MNS Error: {response.StatusCode}\n{error}\n\nsignContent:\n{signContent}");
}
}
private static string BuildSignContent(IDictionary<string, string> parameter, string date)
{
var sb = new StringBuilder(256);
AppendIfExist(sb, parameter, "method", true);
AppendIfExist(sb, parameter, "content-md5", true);
AppendIfExist(sb, parameter, "content-type", true);
sb.Append(date).Append('\n');
var sorted = new SortedDictionary<string, string>(StringComparer.Ordinal);
foreach (var kv in parameter)
{
if (kv.Key.StartsWith("x-mns-", StringComparison.Ordinal))
sorted[kv.Key] = kv.Value ?? string.Empty;
}
foreach (var kv in sorted)
{
sb.Append(kv.Key.Trim()).Append(':').Append((kv.Value ?? string.Empty).Trim()).Append('\n');
}
AppendIfExist(sb, parameter, "uri", false);
return sb.ToString();
}
private static void AppendIfExist(StringBuilder sb, IDictionary<string, string> parameter, string key, bool newLine)
{
if (parameter.TryGetValue(key, out var val) && val != null)
sb.Append(val);
if (newLine) sb.Append('\n');
}
private static string CalculateBase64Md5(string input)
{
using var md5 = MD5.Create();
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
return Convert.ToBase64String(hash);
}
private static string ComputeSignatureHmacSha1Base64(string signContent, string accessKeySecret)
{
using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signContent));
return Convert.ToBase64String(hash);
}
}