苹果消息推送Apns是每个apple app必须面对的坎。目前捣腾的小项目需要定时向所有用户推送消息。之前使用python第三方库pyapns实现,结果运行一段时间发现内存消耗很高,而且推送不稳定,经常手机收不到消息。
于是尝试用GO语言单独写个循环推送的小程序。主要流程就是:定时(每隔2~4分钟),遍历所有的用户apple token,推送消息。
这里分享下代码:
1.实现数据库查询出所有用户的apple token
查询出所有用户表中的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()
}