As a universal component, push system has the following values

  1. The system can be used by multiple service projects. Independent maintenance of the system can reduce maintenance costs
  2. Generally, the push system calls the three-party API for push, and the three-party API generally has the call frequency/times limit. The pushed message needs to go through the queue to reasonably call the three-party API and control the call frequency and times
  3. Generally, the push system is designed not to care about business logic

The core technology

  1. The message queue
  2. Third party service API calls
    1. Android APP Push
    2. Apple App Push
    3. Wechat applet push
    4. The mail
    5. Nailing push
    6. SMS push

Message queue selects RocketMQ provided by Ali Cloud, official document: help.aliyun.com/document_de…

Push sequence diagram

Right-click a new window to open to view a large hd image

It can be seen that there are many third-party services connected to the message push system, and it is difficult to unify the quality of the three parties’ push, so the retry of message push needs to be considered

Thinking process

To control concurrency, all pushes are sent to the RocketMQ queue first, and the number of pushes per push is controlled by the number of clients consuming the queue

Carrier pigeon’s push service is available on both android and Apple

Carrier pigeon push requires client integration, client SDK reference: xg.qq.com/xg/ctr_inde…

Currently, carrier pigeon individual developers can still apply, and after the account is established, create Andorid and ios projects

Exceptions are pushed. When exceptions are pushed, retry is required. The retry can use the retry mechanism of the message queue or implement the retry logic

Android APP Push

Official documentation: xg.qq.com/docs/androi…

Code base: github.com/xingePush/x…

<! -- Carrier pigeon push client -->
<dependency>
    <groupId>com.github.xingePush</groupId>
    <artifactId>xinge</artifactId>
    <version>1.2.1</version>
</dependency>
Copy the code

The core code is as follows

Map<String, Object> messagePayload = new HashMap<String, Object>(1);
messagePayload.put("user_id".1);
messagePayload.put("msg_title"."Message title");
messagePayload.put("msg_content"."Message content");
messagePayload.put("msg_type".1);
messagePayload.put("data", Lists.newHashMap("order_id".1));

XingeApp xinge = new XingeApp(androidAccessId, androidSecretKey);
MessageAndroid message = new MessageAndroid();
ClickAction action = new ClickAction();
action.setActionType(ClickAction.TYPE_ACTIVITY);
message.setAction(action);
message.setContent(JsonUtil.toJsonString(messagePayload));
message.setType(MessageAndroid.TYPE_MESSAGE);
message.setExpireTime(86400);
message.setCustom(new HashMap<String, Object>(1));
JSONObject response = xinge.pushSingleDevice("User PushToken", message);
if(response.getInt(RET_CODE) ! =0) {
    // Push exception, log, etc
}
Copy the code

Apple App Push

Push using the Pushy library

<! -- IOS push client -->
<dependency>
    <groupId>com.turo</groupId>
    <artifactId>pushy</artifactId>
    <version>0.13.3</version>
</dependency>
Copy the code
Map<String, Object> aps = new HashMap<String, Object>(1);
aps.put("alert"."Push content");
aps.put("sound"."default");
aps.put("badge".1);

Map<String, Object> data = new HashMap<String, Object>(1);
data.put("msgTitle"."Push title");
data.put("msgContent"."Push content");
data.put("msgType"."1");
data.put("userId"."13288888888");
data.put("data", Lists.newHashMap("order_id".1));

Map<String, Object> messagePayload = new HashMap<String, Object>(1);
messagePayload.put("aps", aps);
messagePayload.put("data", data);

ApnsClient apnsClient = new ApnsClientBuilder()
        .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
        .setClientCredentials(this.getClass().getClassLoader().getResourceAsStream("Push certificate relative to resources directory path"), "")
        .build();

String payload = JsonUtil.toJsonString(messagePayload);
String token = TokenUtil.sanitizeTokenString("PushToken for app users");

SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.suxiaolin.app1", payload);

PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>>
        sendNotificationFuture = apnsClient.sendNotification(pushNotification);

final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse =
        sendNotificationFuture.get();

if (pushNotificationResponse.isAccepted()) {
    System.out.println("Push notification accepted by APNs gateway.");
} else {
    System.out.println("Notification rejected by the APNs gateway: " +
            pushNotificationResponse.getRejectionReason());

    if(pushNotificationResponse.getTokenInvalidationTimestamp() ! =null) {
        System.out.println("\ t... and the token is invalid as of "+ pushNotificationResponse.getTokenInvalidationTimestamp()); }}Copy the code

Use pigeon bank to push

Of course, you can also use the messenger pigeon’s ios push, which is similar in logic to the Android app’s push

The ios carrier pigeon client can be shared with the Android client without the need to introduce new JAR packages

JSONObject messagePayload = new JSONObject();
Map<String, Object> custom = new HashMap<String, Object>();

messagePayload.put("title"."Push title");
messagePayload.put("body"."Push content");

messagePayload.put("user_id".1);
messagePayload.put("msg_type".1);
messagePayload.put("data", Lists.newArrayList("orderId".1));

XingeApp xinge = new XingeApp(iosAccessId, iosSecretKey);
MessageIOS message = new MessageIOS();
message.setType(MessageIOS.TYPE_APNS_NOTIFICATION);
message.setExpireTime(86400);
message.setAlert(content);
message.setBadge(1);
message.setCategory("INVITE_CATEGORY");
message.setCustom(messagePayload);

response = xinge.pushSingleDevice("PushToken for app users", message, XingeApp.IOSENV_PROD);
if(response.getInt(RET_CODE) ! =0) {
    // Push is abnormal
}
Copy the code

Applet push

Official documentation: mp.weixin.qq.com/wiki?t=reso…

Applets API address: api.weixin.qq.com/cgi-bin/mes…

HTTP request, HTTP request library can be refer to: HttpUtil

Nailing push

The official document: open-doc.dingtalk.com/microapp/se… Code sample

public static boolean send(String content, String title, Set<String> receivers) {
    try {
        HttpUtil.ResponseWrap result = HttpUtil.postWrap(ddUrl,
                "{\n"
                        + " \"msgtype\": \"text\",\n"
                        + " \"text\": {\"content\":\"" + title + "\r\n" + content + "\n|"
                        + receivers.stream().map(r -> "@" + r).collect(Collectors.joining("")) + "\"},\n"
                        + " \"at\": {\n"
                        + " \"atMobiles\": [" + receivers.stream().map(r -> "\" " + r + "\" ").collect(Collectors.joining(",")) + "], \n"
                        + " \"isAtAll\": false\n"
                        + " }\n"
                        + "}");
        return result.getStatusCode() == 200;
    } catch (Exception e) {
        return false; }}Copy the code

See dingtalkutil.java for the complete code

This is done using an HTTP request

The mail

Mail can be sent using Java javax.mail library and SMTP protocol

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>
Copy the code

Example code reference: emailSender.java

SMS push

There are many SHORT message service providers. Mail generally has a unified SMTP protocol, but short messages do not have a protocol. However, HTTP is generally used to send short messages, such as the following short message service providers

  1. 253 Cloud Communication: zz.253.com/api_doc/kai…
  2. SMS – pat cloud again: www.upyun.com/products/sm…
  3. News & SMS communication _ _MSGSMS_ cloud – huawei cloud: www.huaweicloud.com/product/msg…

Features of message queues

The message queue will retry automatically if the consumption is abnormal

A couple of points to note

Wechat applet can generate a push code for each payment, which needs to be saved in the database or cache, and each code can only push 3 messages

Since the consumption of message queue has a certain delay when the message volume is large, it is possible to cancel message push. A unique UUID can be generated for each message. When canceling, this UUID is designed into Redis, and when pushing, it is checked whether this UUID is in Redis to decide whether to push

Although there are uncontrollable exceptions in push, such as exceptions in the three-party push service, there are also exceptions in parameter transfer by the caller. The return value of the interface call can be pushed to judge whether the push system is successfully invoked or recorded in the log, so that it is easier to investigate the cause of the exception

The default retry times and consumption time of message queues cannot be controlled. You can modify the client of message queues to support this feature. For details, see github.com/jibaole/spr… The core logic is to set a maximum number of consumption times and consumption duration for the message, and then set it to success when the number of consumption times and consumption duration reach the threshold

When ios uses carrier pigeon push, the developer certificate and production certificate must be uploaded. At least one of the two certificates must be uploaded