Azure 通知中心延迟问题
Posted
技术标签:
【中文标题】Azure 通知中心延迟问题【英文标题】:Azure Notification Hub Delay Issue 【发布时间】:2016-05-07 10:48:36 【问题描述】:我正在使用 Azure 通知中心将推送通知从 Raspberry pi 发送到 android 应用程序。
根据这篇文章How to use Notification Hubs from Python,我正在使用使用 REST API 的 python 客户端应用程序
一切正常,但从 Raspberry pi 到 Android 应用程序的每个通知都有近 2 秒的延迟!如何避免这种延迟并在同一秒内获得 Android 设备上的通知。
我的 Python 脚本
import time
import hmac
import base64
import hashlib
import json
import urllib.parse
import http.client
class Notification:
def __init__(self, notification_format=None, payload=None, debug=0):
valid_formats = ['template', 'apple', 'gcm', 'windows', 'windowsphone', "adm", "baidu"]
if not any(x in notification_format for x in valid_formats):
raise Exception(
"Invalid Notification format. " +
"Must be one of the following - 'template', 'apple', 'gcm', 'windows', 'windowsphone', 'adm', 'baidu'")
self.format = notification_format
self.payload = payload
# array with keynames for headers
# Note: Some headers are mandatory: Windows: X-WNS-Type, WindowsPhone: X-NotificationType
# Note: For Apple you can set Expiry with header: ServiceBusNotification-ApnsExpiry
# in W3C DTF, YYYY-MM-DDThh:mmTZD (for example, 1997-07-16T19:20+01:00).
self.headers = None
class NotificationHub:
API_VERSION = "?api-version=2013-10"
DEBUG_SEND = "&test"
def __init__(self, connection_string=None, hub_name=None, debug=0):
self.HubName = hub_name
self.Debug = debug
# Parse connection string
parts = connection_string.split(';')
if len(parts) != 3:
raise Exception("Invalid ConnectionString.")
for part in parts:
if part.startswith('Endpoint'):
self.Endpoint = 'https' + part[11:]
if part.startswith('SharedAccessKeyName'):
self.SasKeyName = part[20:]
if part.startswith('SharedAccessKey'):
self.SasKeyValue = part[16:]
@staticmethod
def get_expiry():
# By default returns an expiration of 5 minutes (=300 seconds) from now
return int(round(time.time() + 300))
@staticmethod
def encode_base64(data):
return base64.b64encode(data)
def sign_string(self, to_sign):
key = self.SasKeyValue.encode('utf-8')
to_sign = to_sign.encode('utf-8')
signed_hmac_sha256 = hmac.HMAC(key, to_sign, hashlib.sha256)
digest = signed_hmac_sha256.digest()
encoded_digest = self.encode_base64(digest)
return encoded_digest
def generate_sas_token(self):
target_uri = self.Endpoint + self.HubName
my_uri = urllib.parse.quote(target_uri, '').lower()
expiry = str(self.get_expiry())
to_sign = my_uri + '\n' + expiry
signature = urllib.parse.quote(self.sign_string(to_sign))
auth_format = 'SharedAccessSignature sig=0&se=1&skn=2&sr=3'
sas_token = auth_format.format(signature, expiry, self.SasKeyName, my_uri)
return sas_token
def make_http_request(self, url, payload, headers):
parsed_url = urllib.parse.urlparse(url)
connection = http.client.HTTPSConnection(parsed_url.hostname, parsed_url.port)
if self.Debug > 0:
connection.set_debuglevel(self.Debug)
# adding this querystring parameter gets detailed information about the PNS send notification outcome
url += self.DEBUG_SEND
print("--- REQUEST ---")
print("URI: " + url)
print("Headers: " + json.dumps(headers, sort_keys=True, indent=4, separators=(' ', ': ')))
print("--- END REQUEST ---\n")
connection.request('POST', url, payload, headers)
response = connection.getresponse()
if self.Debug > 0:
# print out detailed response information for debugging purpose
print("\n\n--- RESPONSE ---")
print(str(response.status) + " " + response.reason)
print(response.msg)
print(response.read())
print("--- END RESPONSE ---")
elif response.status != 201:
# Successful outcome of send message is HTTP 201 - Created
raise Exception(
"Error sending notification. Received HTTP code " + str(response.status) + " " + response.reason)
connection.close()
def send_notification(self, notification, tag_or_tag_expression=None):
url = self.Endpoint + self.HubName + '/messages' + self.API_VERSION
json_platforms = ['template', 'apple', 'gcm', 'adm', 'baidu']
if any(x in notification.format for x in json_platforms):
content_type = "application/json"
payload_to_send = json.dumps(notification.payload)
else:
content_type = "application/xml"
payload_to_send = notification.payload
headers =
'Content-type': content_type,
'Authorization': self.generate_sas_token(),
'ServiceBusNotification-Format': notification.format
if isinstance(tag_or_tag_expression, set):
tag_list = ' || '.join(tag_or_tag_expression)
else:
tag_list = tag_or_tag_expression
# add the tags/tag expressions to the headers collection
if tag_list != "":
headers.update('ServiceBusNotification-Tags': tag_list)
# add any custom headers to the headers collection that the user may have added
if notification.headers is not None:
headers.update(notification.headers)
self.make_http_request(url, payload_to_send, headers)
def send_apple_notification(self, payload, tags=""):
nh = Notification("apple", payload)
self.send_notification(nh, tags)
def send_gcm_notification(self, payload, tags=""):
nh = Notification("gcm", payload)
self.send_notification(nh, tags)
def send_adm_notification(self, payload, tags=""):
nh = Notification("adm", payload)
self.send_notification(nh, tags)
def send_baidu_notification(self, payload, tags=""):
nh = Notification("baidu", payload)
self.send_notification(nh, tags)
def send_mpns_notification(self, payload, tags=""):
nh = Notification("windowsphone", payload)
if "<wp:Toast>" in payload:
nh.headers = 'X-WindowsPhone-Target': 'toast', 'X-NotificationClass': '2'
elif "<wp:Tile>" in payload:
nh.headers = 'X-WindowsPhone-Target': 'tile', 'X-NotificationClass': '1'
self.send_notification(nh, tags)
def send_windows_notification(self, payload, tags=""):
nh = Notification("windows", payload)
if "<toast>" in payload:
nh.headers = 'X-WNS-Type': 'wns/toast'
elif "<tile>" in payload:
nh.headers = 'X-WNS-Type': 'wns/tile'
elif "<badge>" in payload:
nh.headers = 'X-WNS-Type': 'wns/badge'
self.send_notification(nh, tags)
def send_template_notification(self, properties, tags=""):
nh = Notification("template", properties)
self.send_notification(nh, tags)
isDebug = True
myConnectionString = "connection string"
hub = NotificationHub(myConnectionString, "cavenotificationhub", isDebug)
data =
data['response'] = 'data: R1|X1|S1,1|$'
json_data = json.dumps(data,separators=(',',':'))
print(json_data)
#gcm_payload = "response":R1|X1|S1,1
val= "R1|X1|S1,1|$"
gcm_payload = 'data' : 'response': ''+val+''
hub.send_gcm_notification(gcm_payload)
附加日志:
Windows 10 Surface Pro 3 - Python 3.4 script
**** Send GCM Notitification START> 2016-05-08 10:42:07.883226
*** make_http_request connection OPEN> 2016-05-08 10:42:08.139328
--- REQUEST ---
#Request header
--- END REQUEST ---
*** make_http_request START> 2016-05-08 10:42:08.165356
#Sending request to Azure
*** make_http_request END> 2016-05-08 10:42:09.016024
--- RESPONSE ---
#Response received from Azure
--- END RESPONSE ---
*** make_http_request connection CLOSE> 2016-05-08 10:42:09.184785
**** Send GCM Notitification END> 2016-05-08 10:42:09.188788
################################################################################################################################ Raspberry Pi Model B+ V1.2 - Python 2.7 script
('*** Send GCM Notitification START> ', '2016-05-08 10:46:32.692844') ('*** make_http_request connection OPEN> ', '2016-05-08 10:46:32.698456')
--- REQUEST ---
#Request header
--- END REQUEST ---
('*** make_http_request START> ', '2016-05-08 10:46:32.705946')
#Sending request to Azure ('*** make_http_request END> ', '2016-05-08 10:46:39.557759')
--- RESPONSE ---
#Response received from Azure
--- END RESPONSE ---
('*** make_http_request connection CLOSE> ', '2016-05-08 10:46:39.569713') ('*** Send GCM Notitification END> ', '2016-05-08 10:46:39.570986')
################################################################################################################################ Raspberry Pi 2 Model B V1.1 - Python 2.7 script
('*** Send GCM Notitification START> ', '2016-05-08 05:36:49.721024') ('*** make_http_request connection OPEN> ', '2016-05-08 05:36:49.732056')
--- REQUEST ---
#Request header
--- END REQUEST ---
('*** make_http_request START> ', '2016-05-08 05:36:49.733069')
#Sending request to Azure ('*** make_http_request END> ', '2016-05-08 05:36:50.741605')
--- RESPONSE ---
#Response received from Azure
--- END RESPONSE ---
('*** make_http_request connection CLOSE> ', '2016-05-08 05:36:50.746248') ('*** Send GCM Notitification END> ', '2016-05-08 05:36:50.747000')
【问题讨论】:
感谢您提出这个问题。即使我面临同样的问题。通知在我的安卓设备上也有点晚了。有没有人可以帮我们解决这个问题..? 【参考方案1】:这很难!首先,我们需要了解导致该问题的原因。
1) 从您的 Raspberry Pi 2 到后端的请求可能很长。要了解这是否是问题所在,请使用Service Bus Explorer,连接到您的 SB 命名空间,然后打开通知中心,选择 Android 窗格并尝试从该窗格发送推送(见屏幕截图)。如果您能更快地收到通知,那么 RPI 会以某种方式参与其中。
2) 如果您发现延迟没有区别,并且使用 RPI 或 SB 发送通知导致 2-3 秒后推送,那么我会说这绝对没问题。 See that topic,例如。这实际上取决于许多因素,当我们尝试减少时间时(有时我们观察到 5-10 秒),我们发现经常出现的问题不是客户端或经纪人(在这种情况下为 NH),而是通知平台。
当然,您不应该期望在同一秒发送通知。我们拥有的最好的 - 接下来或 2 秒后。但确实不能保证。
【讨论】:
感谢@Alex 的回复。我们使用三个不同的平台进行了测试,我们的日志附加在 OP 中。根据时间戳,我们在 Raspberry Pi Model B+ V1.2 上获得了大约 6 秒的延迟(几乎可以接受 1-2 秒的延迟)。您能否分享您对如何减少这种延迟的见解? @SatishGunjal 我不能说我知道最终的真相。根据我过去的经验,减少平均延迟并没有太多工作要做。显然,延迟可能长达 10 分钟。请参阅这些线程 - ***.com/questions/9204767/… 和 ***.com/questions/19560448/…。您可以尝试从 Android 问题中实现该技术,然后看看效果如何。 @AlexBelotserkovskiy 我试图在 android 中继续使用“服务”方法,但它对电池寿命有严重影响,我必须继续合并以检查是否有新通知可用。您能否建议任何其他方法,让我们可以完全确保 Android 设备上的通知能够快速到达? @SatishGunjal 现在很有意义。在 Android 上使用 SB 是可能的,例如,您可能会看到 that 以供参考。 您可能还想尝试 Azure IoT Hub - 它是针对 IoT 工作负载优化的类似队列的服务。这将允许您快速通知应用程序。但是,我会尝试从您的应用程序中卸载通知内容,例如 Web API 后端以避免支持问题。方法见[链接] (azure.microsoft.com/en-us/documentation/articles/…)以上是关于Azure 通知中心延迟问题的主要内容,如果未能解决你的问题,请参考以下文章