提升APNS消息推送质量的一些想法和验证程序

前端之家收集整理的这篇文章主要介绍了提升APNS消息推送质量的一些想法和验证程序前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
今天在想用什么样的方式,才能改善提高大级别时APNS的推送质量.有了个初步的想法。
首先简单列一下,APNS常见的一些限制和要注意的地方:
1.频繁建立和断开连接,被当成受到攻击,直接把链接给断了。
2.开发一堆并发,有个消息发生异常推送失败了,apns ack要等一段时间(可能有1sec左右的延迟)才返回,而
这期间,后面发的消息也会被认为有问题,直接被其扔了。
这时麻烦了,要依返回的Identifier,找到出错的消息,跳过它,把后面的消息重发。
3.还有一些如扩展的JSON太长了,超过了长度限制。
4.莫名其妙的连接断开错误,需要时刻做好重连准备。
5. .....

极光推送有篇Blog<< APNs 推送原理及问题>>可以看看.

这其中,有些可以通过发送前先进行效验,提前过滤掉一些问题,毕竟APNS的ACK相对有点慢。
比如: DeviceToken 是否合法? 尽量防止Invalid token (Status code = 8)之类出现
参数是否正确,如推送内容是否为空之类。
包是否太大?
......

另外一些东西就比较麻烦,比如上面的第2条。这时就需要想些别的方法来处理.

我想出来的办法如下图:

简单说来是分别建立多个长连接,通过Ring比较平均的分配给长连接去发送推送,
当其中如果有长连接出问题时,也只会影响这个RingNode的List中部分消息,不会影响其它链接,这么做重发消息范围也缩小了。
自已写了个tcp server模拟服务端,用上面的想法,模拟客户端,稍稍验证了下模型:

/*

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

猜你在找的Go相关文章