APP消息推送(APP Push)解决方案-服务端工作逻辑和实现

APP消息推送(APP Push)解决方案-服务端工作逻辑和实现,第1张

App推送消息是我们常见的一种app消息提醒方式。

我们的实现需要第三方的支持,实现方式是后台通过接口将Push请求发送至第三方,第三方实现在App所在设备上的推送。

在与推送平台交互时,后台需要向第三方发送两部分信息,推送目标终端标示+推送内容

APP推送需要定位目标终端,也就是说要给那台设备进行推送,

简单的情况下,单设备推送,我们需要拿到一个终端ID的概念,用于定位目标设备,

注:不同渠道中使用的单设备ID方式也不尽相同,以下用TokenID来表示这个终端ID的概念。

而实际推送渠道中往往还有自定义的功能,比如通过打标签的方式将TokenID进行划分,达到批量差异化的效果。

即指通过API接口参数的定义终端上收到的Push消息的内容和格式。

其中IOS的推送消息在展示上区别于安卓的一点是没有title,title的部分只能是默认的APP名称,而安卓的部分虽然默认值也是APP名称,但是也支持自定义title。

通过上述的处理逻辑可得知,后端首先需要登记客户端的TokenId,然后保持TokenID的有效性更新,然后在需要发送APP推送时拿到用户的有效TokenID,

然后使用TokenID和已有的内容信息通过API与三方Push服务交互,完成推送。

即后端的实现分为两部分:

1、TokenID的登记

2、App Push API的调用

注:以下示例中有两个元素为本项目的特殊情况:

其中product_id是因为当前项目中客户端同时有多个版本,不同版本需要推送独立处理,但在同一张表内统一记登记;

而login_id跟member_id同时存在是因为当前系统中存在共享账户的情况,一般账号账户一对一的情况login_id和member_id是绑定的,不需要同时重复登记。

<pre>

/ ============================================================== /

/* Table: sys_app_push_token*/

/ ============================================================== /

create table sys_app_push_token

(

record_idint(11) not null auto_increment,

login_id int(11),

member_idint(11),

push_token varchar(200),

visit_device int(4) comment '3:Android4:IOS',

product_id varchar(20) default '0' comment '',

push_channel int(4) default 1 comment '1:IOS信鸽,2:华为,3:小米,4:极光',

nstatus int(4) not null default 0 comment '状态:0:申请中;1:生效;2:失效;3:删除;4:历史记录',

create_useridint(11) not null default 0,

create_time varchar(20) character set utf8 not null default "",

edit_userid int(11) not null default 0,

edit_timevarchar(20) character set utf8 not null default "",

this_remark text,

description text,

create_ordernum varchar(30) character set utf8 comment '记录创建时的流水号',

last_ordernumvarchar(30) character set utf8 comment '记录最后一次编辑时的流水号',

primary key (record_id)

)

ENGINE=InnoDB

DEFAULT CHARACTERSET=utf8

COLLATE=utf8_general_ci

auto_increment=10000

row_format=COMPACT

alter table sys_app_push_token comment 'app推送token表'

/ ============================================================== /

/* Index: Index_1 */

/ ============================================================== /

create index Index_1 on sys_app_push_token

(

record_id

)

</pre>

注:其中,推送渠道绝对在做Push时使用哪家API,参数的判定交由客户端进行处理,后端直接登记判定结果。

<pre>

@Transactional(readOnly=false)

publicbooleanuploadMemberPushTokenEnt(TrainVansContext trainVansContext) {

try{

//check already data

trainVansContext.getTrainVansRequest().put("login_id", TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(),"login_login_id"))

// get All memberRelation

trainVansContext.getTrainVansRequest().put("relation_type", TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(),"visit_role"))

List>memberRelationList = SpringContextHandler.getBean(MemberService.class).getRelateMemberListByLoginId(trainVansContext)

for(Map memberRelateMap : memberRelationList){

//

trainVansContext.getTrainVansRequest().put("member_id", TrainVansUtils.getMV(memberRelateMap,"member_id"))

Map tokenMap = SpringContextHandler.getBean(AppPushService.class).getPushTokenMapByLoginMap(trainVansContext.getTrainVansRequest())

//disable already data

if(tokenMap !=null){

if(!TrainVansUtils.getMV(tokenMap,"push_token").equals(TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(),"push_token"))){

//

trainVansContext.getTrainVansRequest().put("record_id", TrainVansUtils.getMV(tokenMap,"record_id"))

if(!SpringContextHandler.getBean(AppPushService.class).updateDiabledThePushToken(trainVansContext)){

thrownewRuntimeException("TranVans_Operate_Exception")

}

//insert new data

if(!SpringContextHandler.getBean(AppPushService.class).insertPushTokenRecord(trainVansContext)){

thrownewRuntimeException("TranVans_Operate_Exception")

}

}

}else{

//insert new data

if(!SpringContextHandler.getBean(AppPushService.class).insertPushTokenRecord(trainVansContext)){

thrownewRuntimeException("TranVans_Operate_Exception")

}

}

}

returntrue

}catch(Exception e) {

TrainVansUtils.setRetInfo(trainVansContext,"10005001","Register TokenID Error")

e.printStackTrace()

thrownewRuntimeException("TranVans_Operate_Exception")

}

}

</pre>

注:方法外部有一个关于对应本账号的对账户列表的遍历,遍历中的处理部分为TokenID的登记处理操作。

推送渠道:

APP推送不仅仅要求在APP打开状态时或者后台运行时进行消息推送,更多的场景是在移动终端关闭APP的场景下进行消息推送,

渠道的优劣无非在于两个维度,送达率和送达效率。

其中安卓推送的渠道较为杂乱,其中华为和小米提供的PUSH服务对于自平台的移动终端支持的较为完善,而没有厂商提供PUSH服务的终端只能通过

第三方服务来进行对接。

对于现有的这些渠道进行如下总结:

1、IOS:信鸽推送,这个推送在我门公司中经历了三个项目,推送效果稳定。API接入也方便,是IOS端的不二选择。

2、Android-华为:华为自平台。

3、Android-小米:小米自平台。

4、Android-其他:目前使用的是“极光推送”。在理想状态下送达率和送达效率表现很好,但并不如以上三家渠道稳定。

在进行调用时可根据之前定义的push_channel分发给各自的渠道,各渠道的具体对接请各自查看官网,API都很完善。

可以将客户端的逻辑设计成:

1.将username,password  封装进buffer

2.连接服务端

3.发送buffer

4.接收二进制的系统当前时间

5.显示时间

代码如下:

服务端地址设置部分:

[cpp] view plain copy print?

addr_server.sin_family = AF_INET  

addr_server.sin_port = htons( port )  

addr_server.sin_addr.s_addr = inet_addr( ip )

创建连接:

sock_client = socket( AF_INET, SOCK_STREAM, 0 )

连接服务端代码:

flag = connect( sock_client, ( struct sockaddr* ) &addr_server, sizeof( addr_server ) )

设置buffer填充username/password代码:

sprintf(buffer, "%s", "username")  

sprintf(buffer + 32, "%s", "password" )  

  

buffer[31]=buffer[63] = 0

接着是发送

flag = send( sock_client, buffer, 64, 0 )  

if( flag == 64 )  

{     

      printf( "send ok\n")   

}

接收部分代码:

flag = recv( sock_client, buffer, 64, 0 )  

if( flag != sizeof( time_t ) )  

{  

       printf( "recv does not follow protocal\n")  

       close( sock_client )  

       continue  

}

将接收到的二进制数据转成时间

memcpy( curtime, buffer, sizeof( time_t ) )  

struct tm *ptm = localtime( curtime )

显示时间:

printf( "system time:%04d-%02d-%02d-%02d:%02d:%02d\n", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,  

        ptm->tm_hour, ptm->tm_min, ptm->tm_sec )

关闭连接:

printf( "ok,now we close connection\n" )  

close( sock_client )

实际开发中,为了追求并发效率和提升搞压效果,客户端需要有一个循环,另外可以多进程同时操作。

“后端系统”从宽泛的角度上讲是指向用户提供数据的服务器、超级服务器、群集系统、中程系统以及大型机。这些服务所在的位置通常称为服务器场或数据中心。 客户机/服务器中的服务器称为后端系统。服务器配备大容量存储器并安装数据库系统,用于数据的存放和数据检索;客户端安装专用的软件,负责数据的输入、运算和输出。客户机/服务器计算将处理分为前端应用程序(在客户端的工作站中运行)和后端服务。典型的后端服务包括数据库管理系统(DBMS)、消息传送系统(如Lotus Notes和Microsoft Exchange)和旧式系统(如IBM主机)的网关以及网络管理系统。 用户在前端系统中与应用程序进行交互从而在后端系统上生成请求。然后,后端系统对这些请求进行处理,并搜索和排序数据、提供文件以及其他各种服务。后端系统在物理上距离数据存储系统较近,因此这一布局使网络的利用率很高。 三层系统由于添加了一个中间系统(该系统执行某些通常由客户机或服务器完成的处理),从而扩展了客户机/服务器系统。最重要的是,中间层在使命关键的商业环境中用来存放所有应用程序共享的业务逻辑(规则、过程和/操作顺序)。 在三层模型中,命令先是被发送到服务的"中间层",然后由它将命令发送给数据源。数据库对 SQL 语句进行处理并将结果送回到中间层,中间层再将结果送回给用户。这种三层模型提供了对于各种更新的更大控制,而且它还简化了应用程序的部署。在许多情况下,这种三层模型还可以提供性能上的优势。 当使用因特网/企业网技术时,可将Web服务器置于中间层中。它接受来自客户端的请求,在屏幕上显示请求,将这些请求传递到后端系统,接受响应,将该响应格式化到Web页中,然后将该Web页发送给用户。该系统是可伸缩的。如果通信量增加,Web服务器可将其工作负载的一部分分配给未处于繁忙状态的同层服务器。 例如,根据Microsoft技术构建的联机注册系统可采用Internet Explorer前端界面和Microsoft SQL Server后端数据库。而中间层由运行Microsoft Internet Information Server(IIS)(使用ActiveX技术和动态服务器网页(ASP))的WindowsNT/Windows 2000服务器组成。当用户访问Web服务器时,ActiveX组件下载到客户端以便为访问后端数据库信息提供客户端支持。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/711659.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-08-06
下一篇2023-08-06

发表评论

登录后才能评论

评论列表(0条)

    保存