Go language 150 lines of code to handle Apple Apns high concurrent push

Keywords: Database github MySQL Unix

Apple news push Apns is a barrier that every apple app must face. At present, small projects need to push messages to all users regularly. Previously, we used pyapns, a third-party library of python, to implement it. As a result, we found that the memory consumption was very high and the push was not stable. Often, the mobile phone could not receive messages.

So I try to write a loop push program in GO language. The main process is: regularly (every 2-4 minutes), traverse all users' apple token s, and push messages.

Here is the code:

1. Query out the apple token of all users in the database

Query out the apple token in all user tables

Package used:

import (
	"os"
	"strconv"
	"math/rand"
	"time"
  	"github.com/gohouse/gorose"
  	_ "github.com/go-sql-driver/mysql"
 	"log"
	"fmt"
	"github.com/sideshow/apns2"
	"github.com/sideshow/apns2/certificate"
	"github.com/sideshow/apns2/payload"

)

Initialize database:

//Initialize database
func init_db(){
	var dbConfig = map[string]interface{} {
        "Default":         "mysql_dev",// Default database configuration
        "SetMaxOpenConns": 0,          // (connection pool) the maximum number of open connections. The default value is 0, indicating no limit
        "SetMaxIdleConns": 1,          // Number of idle connections (connection pool), default 1
    
        "Connections":map[string]map[string]string{
            "mysql_dev": {// Define the database configuration named MySQL? Dev
                "host": "127.0.0.1", // Database address
                "username": "root",       // Database user name
                "password": "123456",       // Database password
                "port": "3306",            // port
                "database": "xxx",        // Linked database name
                "charset": "utf8",         // character set
                "protocol": "tcp",         // Link protocol
                "prefix": "",              // Table prefix
                "driver": "mysql",         // Database driver (mysql,sqlite,postgres,oracle,mssql)
            },

    	},
    }
	connection, err := gorose.Open(dbConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	// close DB
	//defer connection.Close()
	
	Db = connection.GetInstance()

	}

Take the user token from the database:

users,err := Db.Table("users_userprofile").Fields("apple_token").Where("apple_token","!=","").Get()
fmt.Println("get all tokens end")
if err!=nil{
	fmt.Println(err)
	return
}

2. Initialize APNs

Third party packages used:

"github.com/sideshow/apns2"
"github.com/sideshow/apns2/certificate"
"github.com/sideshow/apns2/payload"

Initialization:

//Initialize APNSs
func initApns(){
	Info = log.New(os.Stdout,"Info:",log.Ldate | log.Ltime | log.Lshortfile)

	cert, err := certificate.FromP12File("./cert.p12", "123456")
	if err != nil {
		log.Fatal("Cert Error:", err)
	}
	Client = apns2.NewClient(cert).Production()
}

3. Define two channel s:

var (
	NotiChan chan *apns2.Notification
	responses chan *apns2.Response
)

4. Traverse the queried user token, build notification, and pass it into NotiChan

for _,item := range users{
	token:=item["apple_token"]
	fmt.Println("token",token)
	//Generate a job and put it into the Job channel for later worker s to send
	notification := &apns2.Notification{}
	notification.DeviceToken = token.(string)
	notification.Topic = "com.xxx.xxx"
	timeTamp:=time.Now().Unix()
	tm := time.Unix(timeTamp, 0)
	extras:=make(map[string]string)
	extras["command"]="wakeup"
	extras["msgId"]=strconv.FormatInt(timeTamp,10)
	extras["time"]=tm.Format("2006-01-02 03:04:05 PM")
	extras["counter"]=strconv.FormatInt(counter,10)

	payload := payload.NewPayload().Alert("").ContentAvailable()
	for k, v := range extras {
		payload.Custom(k, v)
	}

	notification.Payload = payload
	NotiChan <- notification
	//go sendApns(token.(string))
}

5. Create a worker process to accept the notification sent from NotiChan and send it to the user

//The worker takes the job from JobChan and sends the notification with apns2.client
func worker_send(){
	for{
		n:= <- NotiChan
		fmt.Println("start sending notification")
		res, err := Client.Push(n)
		if err != nil {
			log.Fatal("Push Error:", err)
		}
		responses <- res
	}
}

6. Create a process and handle the response

func work_response(){
	for{
		res := <- responses
		fmt.Println("%v %v %v\n", res.StatusCode, res.ApnsID, res.Reason)
	}
}

7. Main thread logic, timing cycle

//Loop timer thread
func startLoop(){
	fmt.Println("start main loop")
	NotiChan=make(chan *apns2.Notification, 3000)
	responses = make(chan *apns2.Response, 3000)
	counter=0
	for{
		fmt.Println("start get all tokens")
		users,err := Db.Table("users_userprofile").Fields("apple_token").Where("apple_token","!=","").Get()
		fmt.Println("get all tokens end")
		if err!=nil{
			fmt.Println(err)
			return
		}

		for _,item := range users{
			token:=item["apple_token"]
			fmt.Println("token",token)
			//Generate a job and put it into the Job channel for later worker s to send
			notification := &apns2.Notification{}
			notification.DeviceToken = token.(string)
			notification.Topic = "com.xx.xxx"
			timeTamp:=time.Now().Unix()
			tm := time.Unix(timeTamp, 0)
			extras:=make(map[string]string)
			extras["command"]="wakeup"
			extras["msgId"]=strconv.FormatInt(timeTamp,10)
			extras["time"]=tm.Format("2006-01-02 03:04:05 PM")
			extras["counter"]=strconv.FormatInt(counter,10)

			payload := payload.NewPayload().Alert("").ContentAvailable()
			for k, v := range extras {
				payload.Custom(k, v)
			}

			notification.Payload = payload
			NotiChan <- notification
			//go sendApns(token.(string))
		}
		waitTime:=rand.Intn(120)
		fmt.Println("wait:",waitTime," seconds")
		time.Sleep(time.Duration(waitTime+120)*time.Second)
		counter=counter+1
	}
}

8. In the main function:

func main() {
 //Start 10 work, process and send notification
 for i:=0;i<10;i++{
 go worker_send()
 }
 for i:=0;i<10;i++{
 go work_response()
 }
 startLoop()
}

Source address

Posted by pahikua on Sun, 29 Dec 2019 10:32:58 -0800