推送

移动推送实现方式

  • 轮询(POLL)

  • 短信推送(SMS PUSH):短信发送推送消息,在客户端植入短信拦截模块(主要针对Android),可实现对短信拦截转发交给APP处理

  • 长连接(TCP长连)

解决方案

  • iOS(Apple Push Notification Service)
  • Android(Android cloud to device messaging):中国基本不可用

自建推送服务难点

  • 海量长连接管理
  • Push Service常驻
  • 通信协议制定
  • 弱网环境

消息状态机

graph TD
A(消息入库) --> B(待发送)
B --> C(发送中)
C --> D(发送成功<br>客户端收到)
E(发送失败) --> C
C --> E
C --> F(N次重发未收到)
F --> G(( ))
D --> H(( ))
C --> I(发送成功<br>客户端未收到)
I --> C

微信架构

微信异步队列 MQ 2.0

框架

graph TD
subgraph MQ
Binlog
Queue
sch[调度器]
end
subgraph Worker
pro[处理器xn]
end
  • MQ:任务持久化和调度框架
  • Worker:处理框架(1.0任务只能本机消费)
  • 优化:任务调度/处理、过载保护

多IDC分布特点

graph LR
A(跨机消费) --> B(拉/推任务?)
A --> C(Worker如何感知和消除积压)
  • 广播模式:MQ积压量广播给多个Worker

流式任务处理框架

在任务处理结束时,除了返回结果,还可以返回一系列新的任务

流量控制能力

MQ 1.0 以下只能通过配置队列任务出队速度来实现流量控制:

  • 配置人工调整难以估算对后端的实际访问
  • 后端处于过载状态时无法自适应调整

过载保护:

  • 前向限速:其直接观察到的数据
    • CPU使用率
    • 任务成功率

-后向限速:业务反馈,如后端RPC访问量


红包系统

过程

A给B发十元红包:

  1. A账号中读出余额
  2. A账号余额-10
  3. 结果写回A账号
  4. B账号中读出余额
  5. 拆开红包,读出数值10
  6. B账号余额+10
  7. 结果写入到B账号中

为保证数据一致性,1-3、4-7必须全部完成或回滚

  • 7中拆开A的红包最后入账操作时由于并发压力常常出现入账失败状况
  • 入账失败后请求全部转入CMQ,反复尝试直到成功,此时手机显示等待状态
  • CMQ保证这条消息不丢失

协程调度

策略

  1. 服务启动,初始化一个调度器,一个任务队列
  2. 每次来一个请求会创建一个Task,将Task加入到任务队列里
  3. 调度器开始执行调度函数,调度函数会将task出队列,执行task函数
  4. phpneighbor的中断的上下文信息通过迭代器来保存。Task会遍历迭代器,将所有的中断递归入栈,然后从最后一个节点信息开始判断信息类型
  5. 异步IO类型,task会执行发包逻辑。并注册异步回调函数。然后当前task被调度器从执行任务队列中移除
  6. 申请资源类型,task会执行申请资源逻辑
graph TB
A(申请) -->|正常|B(通过迭代器<br>将信息写回<br>协程中断)
B --> C(协程唤起<br>执行中断<br>后的程序)
A -->|失败|D(注册回调函数)
D --> E(等待空闲资源)
E --> F(task被移出队列)
C --> F
  1. 回调函数被触发或普通数据需要回写到协程中断,task会继续将该迭代器信息入栈,继续回到流程3

优化设计

程序优化

提高对固定服务器的TCP服务请求速度

  • 程序启动阶段预先建立N个连接
  • 有数据要发送就找一个空闲的连接
  • 定时器:检测连接池里的可用连接数,保持足够不

程序扩展性

多进程服务容灾

  • 前端用nginx等做负载均衡

防止单点失效

  • 多个服务器进程
  • crontab心跳包,挂掉后迅速拉起

Memcached

有限资源解决超大访问量

  • IO大,做好限速准备
  • 注意命中率与失效率

杂碎

Software Defined Network

  • 新型网络创新架构,网络虚拟化实现方式
  • 核心技术OpenFlow通过将网络设备控制面数据面分离开了,从而实现网络流量的灵活控制
    • OpenFlow常见术语:流表、控制平面、转发平面

可重入

函数

可以被中断的函数,可以在执行的任何时刻中断它,转入os调度执行另一段代码

public class UnReentrant{
	Lock lock = new Lock();
	public void outer(){
		lock.lock();
		inner();
		lock.unlock();
	}
	public void inner(){
		lock.lock();//卡死
		//do something lock.unlock();
	}
}
  • 线程可以进入任何一个它已经拥有的锁所同步着的代码块
  • 最大好处是可以避免死锁
  • 在java 中,synchronized关键字和ReentrantLock类是属于可重入锁,这里单纯的Lock就不是

Written with StackEdit.