提升APNS消息推送质量的一些想法和验证程序
发布时间:2020-12-16 18:37:27 所属栏目:大数据 来源:网络整理
导读:今天在想用什么样的方式,才能改善提高大级别时APNS的推送质量.有了个初步的想法。 首先简单列一下,APNS常见的一些限制和要注意的地方: 1.频繁建立和断开连接,被当成受到攻击,直接把链接给断了。 2.开发一堆并发,有个消息发生异常推送失败了,apns ack要等
今天在想用什么样的方式,才能改善提高大级别时APNS的推送质量.有了个初步的想法。
首先简单列一下,APNS常见的一些限制和要注意的地方: 1.频繁建立和断开连接,被当成受到攻击,直接把链接给断了。 2.开发一堆并发,有个消息发生异常推送失败了,apns ack要等一段时间(可能有1sec左右的延迟)才返回,而 这期间,后面发的消息也会被认为有问题,直接被其扔了。 这时麻烦了,要依返回的Identifier,找到出错的消息,跳过它,把后面的消息重发。 3.还有一些如扩展的JSON太长了,超过了长度限制。 4.莫名其妙的连接断开错误,需要时刻做好重连准备。 5. ..... 极光推送有篇Blog<< APNs 推送原理及问题>>可以看看. 这其中,有些可以通过发送前先进行效验,提前过滤掉一些问题,毕竟APNS的ACK相对有点慢。 比如: DeviceToken 是否合法? 尽量防止Invalid token (Status code = 8)之类出现 参数是否正确,如推送内容是否为空之类。 包是否太大? ...... 另外一些东西就比较麻烦,比如上面的第2条。这时就需要想些别的方法来处理. 我想出来的办法如下图: 简单说来是分别建立多个长连接,通过Ring比较平均的分配给长连接去发送推送, /* Author: XCL(XiongChuanLiang) Date: 2015-11-11 | RingNode RingNode | | ConnRing | P...| RingNode RingNode(ID|List|...) | C... | Notification | | Notification | | ...... | */ package main import ( "container/list" "container/ring" "fmt" "log" "net" "runtime" "strings" "sync" "time" ) var ( dialNetwork string = "tcp" dialAddress string = "localhost:6666" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) test() } //////////////////////////////////////////// func test() { /////////////////////////////////////////// //初始化 cr := NewConnRing(3) cr.InitConn() cr.ListConnRing() //得到通知环 var rn *RingNode var ok bool // for _ = range time.Tick(time.Second * 1) { for i := 0; i < 5; i++ { v := cr.GetRingNode().Next().Value if rn,ok = v.(*RingNode); ok { rn.Add(NewNotification(fmt.Sprintf("%d-%s",rn.NodeID,"PushNotification"))) rn.ListNotification() log.Println(strings.Repeat("--",50)) } } /////////////////////////////////////////// cr.Close() } //////////////////////////////////////////// // Ring // //////////////////////////////////////////// type ConnRing struct { r *ring.Ring } func NewConnRing(n int) *ConnRing { cr := &ConnRing{} cr.r = ring.New(n) return cr } func (cr *ConnRing) InitConn() { for i := 1; i <= cr.r.Len(); i++ { cr.r.Value = NewRingNode(dialNetwork,dialAddress,i) cr.r = cr.r.Next() //log.Println("[InitConn] i:",i," conn ok") } } func (cr *ConnRing) GetRingNode() *ring.Ring { cr.r = cr.r.Next() return cr.r } func (cr *ConnRing) ListConnRing() { cr.r.Do(func(p interface{}) { if v,ok := p.(*RingNode); ok { log.Println("[ListConnRing] NodeID:",v.NodeID) } }) } func (cr *ConnRing) Close() { for i := 1; i <= cr.r.Len(); i++ { v := cr.r.Value // v := cr.GetRingNode().Next().Value if rn,ok := v.(*RingNode); ok { (*rn).Close() } } } /////////////////////////////////////////////////////// // 环的节点 /////////////////////////////////////////////////////// type RingNode struct { NodeID int Status int conn NList *list.List //通知列表 } func NewRingNode(dialNetwork,dialAddress string,id int) *RingNode { x := &RingNode{} x.NodeID = id x.NList = list.New() x.Dial(dialNetwork,dialAddress) return x } func (rn *RingNode) Add(v *Notification) { rn.NList.PushBack(v) } func (rn *RingNode) Remove() { //...... } func (rn *RingNode) Get() *Notification { x := rn.NList.Front() if v,ok := x.Value.(*Notification); ok { return v } else { return nil } } func (rn *RingNode) ListNotification() { log.Println("节点(",")共有(",rn.NList.Len(),")笔数据,明细如下:") for e := rn.NList.Front(); e != nil; e = e.Next() { if c,ok := e.Value.(*Notification); ok { log.Println("ID:"," Content:",c.Content) } else { log.Println("ID:"," Value:",e.Value) } } } /////////////////////////////////////////////////////// // 通知 /////////////////////////////////////////////////////// type Notification struct { Content string Ct time.Time } func NewNotification(v string) *Notification { return &Notification{Content: v,Ct: time.Now()} } /////////////////////////////////////////////////////// // 基本的服务器连接处理 /////////////////////////////////////////////////////// type conn struct { mu sync.Mutex conn net.Conn err error } func (c *conn) Dial(network,address string) { con,err := net.Dial(network,address) if err != nil { log.Fatal(err) } c.conn = con log.Println(address,"连接成功!") return } func (c *conn) Close() error { c.mu.Lock() addr := c.conn.RemoteAddr() if c.conn != nil { c.conn.Close() } c.mu.Unlock() log.Println(addr,"断开连接!") return nil } /////////////////////////////////////////////////////// /* 2015/11/12 00:25:07 localhost:6666 连接成功! 2015/11/12 00:25:07 localhost:6666 连接成功! 2015/11/12 00:25:07 localhost:6666 连接成功! 2015/11/12 00:25:07 [ListConnRing] NodeID: 1 2015/11/12 00:25:07 [ListConnRing] NodeID: 2 2015/11/12 00:25:07 [ListConnRing] NodeID: 3 2015/11/12 00:25:07 节点( 3 )共有( 1 )笔数据,明细如下: 2015/11/12 00:25:07 ID: 3 Content: 3-PushNotification 2015/11/12 00:25:07 ------------------------------------ 2015/11/12 00:25:07 节点( 1 )共有( 1 )笔数据,明细如下: 2015/11/12 00:25:07 ID: 1 Content: 1-PushNotification 2015/11/12 00:25:07 ------------------------------------ 2015/11/12 00:25:07 节点( 2 )共有( 1 )笔数据,明细如下: 2015/11/12 00:25:07 ID: 2 Content: 2-PushNotification 2015/11/12 00:25:07 ------------------------------------ 2015/11/12 00:25:07 节点( 3 )共有( 2 )笔数据,明细如下: 2015/11/12 00:25:07 ID: 3 Content: 3-PushNotification 2015/11/12 00:25:07 ID: 3 Content: 3-PushNotification 2015/11/12 00:25:07 ------------------------------------ 2015/11/12 00:25:07 节点( 1 )共有( 2 )笔数据,明细如下: 2015/11/12 00:25:07 ID: 1 Content: 1-PushNotification 2015/11/12 00:25:07 ID: 1 Content: 1-PushNotification 2015/11/12 00:25:07 ------------------------------------ 2015/11/12 00:25:07 127.0.0.1:6666 断开连接! 2015/11/12 00:25:07 127.0.0.1:6666 断开连接! 2015/11/12 00:25:07 127.0.0.1:6666 断开连接! */模型是跑起来了,具体效果要实际测才知道。 不过就算发成功了,也只是初步,后面还有一堆 事情要做,比如,要区分,app是否已被用户删了。消息是否被合并了,发送成功与失败的结果返回与统计等等。
BLOG: http://blog.csdn.net/xcl168 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |