企业的APP开发中,对于苹果设备有个独特的通知推送功能要解决,尤其是在做移动IM时,对APNS(Apple Push Notification Service)的要求比较高,虽然有专门的第三方提供此类服务,但出于安全的考滤,有能力的公司宁愿自建推送服务系统。本人结合工作中的开发经验,在这探讨一下其架构的演进与探索,希望能使此类系统更加完美。
IM系统自建苹果通知推送服务系统的层级关系如下:
图1 层级关系
首先说明一下在我工作中APNS的使用场景:
对于最初的解决方案是我入项目组时就已经定好的,具体的应对方案如下:
图3 原始设计方案
虽然这个方法实现了功能,但对于APP的推广后的大量的用户使用,系统的承受能力和扩展能力很明显就不足,虽然推送系统已经根据用户的设置信息,从最大程度上减少了压力,但对于IM软件来说仍然不够,而且对于IM类的APP来说还有两个特殊的要求:角标和消息顺序。
出于众多的考滤,历时两个多月,我对推送系统做了如下的改进,首先从架构上:
图 4 单个证书解决方案
我利用了RabbitMQ的路由功能做系统的负载均衡支持,因为苹果设备的设备号是固定64位长度,而且具有唯一性,所以的就用了用户的设备号来做Hash(散列),具体的算法如下:
public abstract class RouteKeyUtil { private static Properties prop = ConfigUtil.getApnsConfig(); private static Map<Integer, String> map; static { String list = prop.getProperty("queues"); String[] queues = list.split(","); map = new HashMap<Integer, String>(); int ii = 1; for (String q : queues) { if(StringUtils.isNotEmpty(q)){ map.put(Integer.valueOf(ii++), q); } } } public static String genarateRouteKey(String token) { if (token == null || token.isEmpty()) { throw new IllegalArgumentException("token is empty"); } int random = token.charAt(0) * token.charAt(1); int index = random % map.size() + 1; return map.get(Integer.valueOf(index)); } public static Map<Integer, String> getAllQueues(){ return map; } }
由于苹果的证书分为企业证书和开发者证书,而且两个证书的调用接口地址不一样,如果调错的话Apple Server会返回发送失败错误。所以对于两个证书我们在一个系统里做了区别,但两者架构相同,如下:
图 5 双证书解决方案
但是利用MQ做负载均衡有一个致使命的缺点,就是不能相互感知,一但监听服务器宕机,消息就会在MQ中积压,为了解决此问题,我利用Zookeeper提供的订阅功能做了一个监控方案,Zookeeper用于存放配置,健康检测系统用于监听服务状态和动态修改配置,这样可以简单实现失效转移(failover)功能,具体设计如下:
图 6 系统监控
此系统设计方案还有很多缺点,在此提出希望大家能给点宝贵的建议。