手机话费充值平台之架构设计
该平台面向三类用户:下家(合作商),上家(代理商),平台管理员。该平台提供给下家的功能包括:合作商入驻、充值价格查询、充值接口,充值结果查询、对账接口等。平台可以接入多个上家,根据每个上家充值接口的服务质量,充值时动态切换上家,目前暂时只接入一个上家。平台还提供了手机号码归属地查询接口,可以自动切换该接口的多个实现,以实现该接口的高可用。手机话费空中充值的流程如下:用户在下家网站上输入手机号码和充值面值,下家调用平台的充值价格查询接口,用户确认价格并支付成功之后,调用平台的充值接口。平台提供了充值结果异步通知接口。下家与平台之间的通讯协议是使用https,平台与上家之间的通讯协议是使用TCP。使用netty封装了平台与上家之间的通讯,可以自定义协议和很好的解决TCP的拆包,粘包等问题,因为使用l了TCP的keepalive。所以会使用心跳维持这个连接。每次调用上家充值接口,会传递一个不重复的有规则的序列号,序列号的生成使用了MySQLMaxValueIncrementer,如下配置所示:
<bean id="messageSequence" class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer">
<property name="incrementerName" value="t_message_sequence"/> <!-- 设置维护主键的表名 -->
<property name="columnName" value="sequence_val"/><!-- 用于生成主键值的列名 -->
<!--<property name="cacheSize" value="10"/>缓存大小 -->
<property name="dataSource" ref="dataSource"/>
</bean>
平台与该上家进行合作时,平台需支付预存款。所以调用上家的充值接口时,需保证账户余额大于0,而每次调用上家的充值接口时,会实时更新该余额,为了高性能的进行该操作,使用了乐观锁。
同时为了实现调用上家接口的代码的复用和可扩展性,大量使用了泛型:
public abstract class Message<T1 extends Head,T2 extends Body> { /**报文起始标识**/ public final static byte startFlag[] ={0x55,0x55,0x55,0x55}; /**报文结束标识**/ public final static short endFlag[] ={0xFE,0xFE,0xFE,0xFE}; /**报文头**/ protected T1 head; /**报文体**/ protected T2 body; public T1 getHead() { return head; } public T2 getBody(){ return body; } void setBody(T2 body) { this.body = body; } /**获取报文消息内容**/ public String getMessageContent(){ return head.getHeadContent()+body.getBodyContent(); } public String getMessageContentJson(){ return head.getHeadContentJson()+body.getBodyContentJson(); } }
性能方面可以优化的地方:上面的序列号生成和账户余额的更新功能,可以使用redis来做。不过对于当前的情况,上面两种方案已经足够满足了 。
因为下家是使用同步方式调用平台的充值接口,而平台调用上家的充值接口是使用异步的方式,这样就存在将异步转同步的问题。有多种实现方案,这里就不多说了,留给大家思考。