• 如何给30W的APP用户推送消息?


    给30W用户推送消息并不难,使用第三方的推送可以说相当简单,基本上都支持针对app级别的推送,服务端只需要调用一次推送接口,剩下的交给第三方推送即可。但有的场景下,完全交给第三方是不可行的。比如下面的场景

    1.用户收到消息后会打开app,打开app就会请求服务器去加载数据,这样并发请求会非常高,而因为某些原因服务器承受不住的情况。

    2.推送消息是有针对性的用户的,但针对的是部分用户。

    3.要推送的消息量比较大,需要数据库表先存储,再用其他软件去推送。

    4.数据库做了主从同步,如果大量的写入要推送的消息,会导致同步性能极差,其他数据的同步变得极慢,直接影响用户体验。

    归纳这个场景起来就两个问题

    1.数据库主从同步,大量数据会导致同步性能问题,绝对要避免大量数据的写入和删除和修改。

    2.同时推送消息给大量用户,用户请求并发高,服务器无法承受,需要分压,削减波峰。

    为了解决第一个问题,采用的方案为将待推送消息的表拆分为单独的数据库中,并且该数据库不需要做主从同步。(或者采用其他方案,例如mongoDB等)

    怎样生成待推送消息到推送数据库表呢?

    这里需要先说一下推送机制,因为待推送信息写入到待推送表中后,每条消息有个推送时间(主要满足运营需要,提前准备好内容,指定时间进行推送),真正推送的程序是按照推送时间进行过滤数据进行推送的,当指定的推送时间小于当前时间时,就拿去数据进行推送操作。因此这里的推送时间就成为了削减波峰,缓解服务器并非压力的关键

    方案一:传统方案

    1.查询出推送的目标对象集合(数据量有点大,查询比较慢,网络IO问题也严重,开发环境需要20秒)

    2.根据目标对象集合插入对应的带推送消息到带推送表中。(values()(),限制999,超过容易出bug,开发环境需要20秒)

    方案二:既然网络IO有问题,那就不查询出来,使用insert into select 来做吧。

    1 insert into [ak50Push]..ak_push select top 10000 newid(),'00000000-0000-0000-0000-000000000000',1,isnull(sp.tokenId,''),sp.guid,'推送信息',0,isnull(sp.gtClientId,''),isnull(sp.iskeeper,0),getdate() from ak_space sp with(nolock)
    2 join (select guid from ak_student t with(nolock) where t.akstustate=1 union all select guid from ak_teacher t with(nolock) where t.akteastate=1) tt on sp.guid=tt.guid where sp.isClientInstall=1 and sp.iskeeper=0 

    这个方案并未真正解决问题,因为最终的推送时间都一致了,数据写入了,但并非未解决。

    方案三:既然这样,那就用临时变量+循环来做吧。

     1 declare @targetUser table(tokenId nvarchar(50),gtClientId nvarchar(50),iskeeper tinyint,guid uniqueidentifier PRIMARY KEY)
     2 insert into @targetUser select sp.tokenId,sp.gtClientId,sp.iskeeper,sp.guid from ak_space sp with(nolock) join (select guid from ak_student t with(nolock) where t.akstustate=1 union all select guid from ak_teacher t with(nolock) where t.akteastate=1) tt on sp.guid=tt.guid  where sp.isClientInstall=1 and sp.iskeeper=0 --and ((sp.tokenId is not null and sp.tokenId !='') or (sp.gtClientId is not null and sp.gtClientId!=''))
     3 declare @total int,@perTime int,@perSize int,@index int
     4 declare @pushTime datetime
     5 set @total=(select count(*) from @targetUser)
     6 set @index=1
     7 set @perTime=15
     8 set @perSize=500
     9 set @pushTime='2018-10-10 14:16'
    10 while @index<=(@total/@perSize+1)
    11 begin
    12     insert into [ak50Push]..ak_push select top 500 newid(),'00000000-0000-0000-0000-000000000000',1,isnull(sp.tokenId,''),sp.guid,'推送信息',0,isnull(sp.gtClientId,''),isnull(sp.iskeeper,0),@pushTime from @targetUser sp
    13     where not exists (select 1 from [ak50Push]..ak_push p with(nolock) where p.recGuid=sp.guid and p.headGuid='00000000-0000-0000-0000-000000000000')
    14     set @index+=1
    15     set @pushTime=DATEADD(second,@perTime,@pushTime)
    16 end

    此方案实现了目标,但执行时间为40多秒,太慢了。并且发现待推送表的读取次数非常高。

    方案四:既然待推送表的读取高,那就不读该表就是了。

     1 declare @targetUser table(tokenId nvarchar(50),gtClientId nvarchar(50),iskeeper tinyint,guid uniqueidentifier PRIMARY KEY)
     2 declare @tempUser  table(tokenId nvarchar(50),gtClientId nvarchar(50),iskeeper tinyint,guid uniqueidentifier PRIMARY KEY)
     3 insert into @targetUser select sp.tokenId,sp.gtClientId,sp.iskeeper,sp.guid from ak_space sp with(nolock) join (select guid from ak_student t with(nolock) where t.akstustate=1 union all select guid from ak_teacher t with(nolock) where t.akteastate=1) tt on sp.guid=tt.guid  where sp.isClientInstall=1 and sp.iskeeper=0 --and ((sp.tokenId is not null and sp.tokenId !='') or (sp.gtClientId is not null and sp.gtClientId!=''))
     4 declare @total int,@perTime int,@perSize int,@index int
     5 declare @pushTime datetime
     6 set @total=(select count(*) from @targetUser)
     7 set @index=1
     8 set @perTime=15
     9 set @perSize=500
    10 set @pushTime='2018-10-10 14:16'
    11 while @index<=(@total/@perSize+1)
    12 begin
    13     insert into @tempUser select top 500 * from @targetUser
    14     insert into [ak50Push]..ak_push select top 500 newid(),'00000000-0000-0000-0000-000000000000',1,isnull(sp.tokenId,''),sp.guid,'推送信息',0,isnull(sp.gtClientId,''),isnull(sp.iskeeper,0),@pushTime from @tempUser sp
    15     delete p1 from @targetUser p1 where exists (select 1 from @tempUser p2 where p1.guid=p2.guid)
    16     set @index+=1
    17     set @pushTime=DATEADD(second,@perTime,@pushTime)
    18 end

    存在问题:比方案三的性能还差,这个等了2分多种没有出结果,直接停掉了。

    方案五:最终方案

    1 insert into [ak50Push]..ak_push select newid(),'00000000-0000-0000-0000-000000000000',1,isnull(sp.tokenId,''),sp.guid,'推送信息',0,isnull(sp.gtClientId,''),isnull(sp.iskeeper,0),DATEADD(second,sp.timediff,getdate()) from (
    2 select sp.tokenId,sp.gtClientId,sp.iskeeper,sp.guid,(ROW_NUMBER() over (order by sp.guid))/500*15 as timediff from ak_space sp with(nolock) join (select guid from ak_student t with(nolock) where t.akstustate=1 union all select guid from ak_teacher t with(nolock) where t.akteastate=1) tt on sp.guid=tt.guid  where sp.isClientInstall=1 and sp.iskeeper=0 --and ((sp.tokenId is not null and sp.tokenId !='') or (sp.gtClientId is not null and sp.gtClientId!=''))
    3 )sp

    时间上有递增,并且在开发环境只需要1秒就完成了。

    本文禁止转载,请勿复制,所有代码仅供参考,根据自己的实际情况进行修改。

  • 相关阅读:
    ReactNative--Flexbox布局
    ReactNative--资源,文章,等等
    ReactNative--坑--no bundle URL present
    ReactNative--StyleSheet样式表
    ReactNative--项目创建及结构分析
    ReactNative--ReactNative简介
    10-4路径文字排版 这一节完全不明白
    10-3区域文字排版
    10-2使用字符调板
    10-1使用文字工具
  • 原文地址:https://www.cnblogs.com/maomao999/p/9766744.html
Copyright © 2020-2023  润新知