Go语言150行代码搞定苹果Apns高并发推送

苹果消息推送Apns是每个apple app必须面对的坎。目前捣腾的小项目需要定时向所有用户推送消息。之前使用python第三方库pyapns实现,结果运行一段时间发现内存消耗很高,而且推送不稳定,经常手机收不到消息。

于是尝试用GO语言单独写个循环推送的小程序。主要流程就是:定时(每隔2~4分钟),遍历所有的用户apple token,推送消息。

这里分享下代码:

1.实现数据库查询出所有用户的apple token

Go语言150行代码搞定苹果Apns高并发推送

查询出所有用户表中的apple token

用到的包:

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"

)

初始化数据库:

//初始化数据库
func init_db(){
	var dbConfig = map[string]interface{} {
        "Default":         "mysql_dev",// 默认数据库配置
        "SetMaxOpenConns": 0,          // (连接池)最大打开的连接数,默认值为0表示不限制
        "SetMaxIdleConns": 1,          // (连接池)闲置的连接数, 默认1
    
        "Connections":map[string]map[string]string{
            "mysql_dev": {// 定义名为 mysql_dev 的数据库配置
                "host": "127.0.0.1", // 数据库地址
                "username": "root",       // 数据库用户名
                "password": "123456",       // 数据库密码
                "port": "3306",            // 端口
                "database": "xxx",        // 链接的数据库名字
                "charset": "utf8",         // 字符集
                "protocol": "tcp",         // 链接协议
                "prefix": "",              // 表前缀
                "driver": "mysql",         // 数据库驱动(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()

	}

从数库取出用户token:

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.初始化APNs

用到的第三方包:

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

初始化:

//初始化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.定义两个channel:

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

4.遍历查询到的用户token,构建notification,传入NotiChan中

for _,item := range users{
	token:=item["apple_token"]
	fmt.Println("token",token)
	//生成job,放入Job channel, 供之后的worker去发送
	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.创建worker协程用于接受NotiChan中发来的notification,并发送到用户

//worker 从JobChan取出job, 用apns2.client发送notification
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.创建协程,处理response

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

7.主线程逻辑,定时循环

//循环定时线程
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)
			//生成job,放入Job channel, 供之后的worker去发送
			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. main函数中:

func main() {
 //起10个work,处理发送notification
 for i:=0;i<10;i++{
 go worker_send()
 }
 for i:=0;i<10;i++{
 go work_response()
 }
 startLoop()
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值