概述

在企业级呼叫模型中,号码资源总是有限的,企业员工在使用有限的号码资源外呼时,就会有号码冲突的问题,如何解决多人共用少量号码的选号问题?

最近有一个新的业务需求,需要解决公共号码池的选号问题,号码池中的号码也有诸多约束,本文从这个需求中的号码池功能点出发,谈一谈使用redis实现分布式公共号码池的选号模型。

环境

centos:CentOS  release 7.0 (Final)或以上版本

freeswitch:v1.8.7

GCC:4.8.5

redis集群版本:redis-3.0.7

需求描述

客户应用下对应一个公共号码池,号码池功能描述如下:

1, 号码池中的号码可以随时添加,删除。

2, 号码池中的号码有注册在线状态和下线状态。

3, 当客户需要外呼时,要对号码池中的号码进行占用,占用状态的号码不能再次占用。呼叫结束后,占用结束恢复空闲状态。

外显号码的约束:

1,号码不能并发。

2,号码有在线状态。

3,号码状态变更支持分布式多线程。

需求分析

号码的状态区分,呼叫状态分idle/busy,在线状态分up/down。

号码的事件区分,add,del,reg,unreg,call,hangup。

号码的事件触发需要满足分布式,即多台服务器的多线程会同时触发号码的不同事件。

数据模型选择redis的list列表和hash哈希表俩种数据结构来保存号码池数据。

其中使用list保存idle+up的号码,使用hash保存号码池中全量号码的当前状态。

多个数据结构的redis操作,又要满足分布式,就要想办法把多个操作合并为原子操作,可选的方向有事务和lua脚本俩种方案。

在这里我们使用lua脚本的实现方案,可操作性更强,redis接口操作更方便,易扩展。

数据模型

redis的数据结构增加俩个,分别保存空闲在线的号码列表和全量号码状态。

空闲在线号码列表,LIST类型,KEY为{numpool}:idle:up,value为NUMBER。

全量号码状态,HASH类型,KEY为{numpool}:numstate,FIELD为NUMBER,value为json格式字符串。

{

"callstate":0,    //呼叫状态,0-idle,1-busy

"regstate":0    //在线状态,0-down,1-up

}

几个注意的点:

为什么选择LIST,不选SET。原本想选择SET,使用SPOP命令随机选号,但是lua脚本中不能调用随机命令,退而求其次选择LIST。

为什么KEY中使用{numpool}的前缀。在redis集群环境下,LUA脚本和脚本中使用到的所有的KEY必须要保证落在同一个SLOT中。所以使用{numpool}这种hash-tag格式的前缀来保证多个keys在redis的同一个SLOT。

脚本逻辑

对于号码状态的变更定义一个状态机迁移表,所有的状态和事件都在表中可以找到对应的处理流程。

num_state_table

{

号码当前状态       事件       后续状态       处理

nodata                  add     idle+down   增加号码状态

nodata                  del     nodata      返回成功

nodata                  reg     nodata      返回失败

nodata                  unreg   nodata      返回失败

nodata                  call    nodata      返回失败

nodata                  hangup  nodata      返回失败

idle+up         add     idle+up     返回成功

idle+up         del     nodata      删除号码状态,删除空闲号码

idle+up         reg     idle+up     返回成功

idle+up         unreg   idle+down   修改号码状态,删除空闲号码

idle+up         call    busy+up     修改号码状态,删除空闲号码

idle+up         hangup  idle+up     返回错误

idle+down       add     idle+down   返回成功

idle+down       del     nodata      删除号码状态

idle+down       reg     idle+up     修改号码状态,添加空闲号码

idle+down       unreg   idle+down   返回成功

idle+down       call    idle+down   返回错误

idle+down       hangup  idle+down   返回错误

busy+up         add     busy+up     返回成功

busy+up         del     nodata      删除号码状态

busy+up         reg     busy+up     返回成功

busy+up         unreg   busy+down   修改号码状态

busy+up         call    busy+up     返回错误

busy+up         hangup  idle+up     修改号码状态,添加空闲号码

busy+down       add     busy+down   返回成功

busy+down       del     nodata      删除号码状态

busy+down       reg     busy+up     修改号码状态

busy+down       unreg   busy+down   返回成功

busy+down       call    busy+down   返回错误

busy+down       hangup  idle+down   修改号码状态

}

数据结构和状态机定义好之后,脚本的逻辑就比较简单了,下面写了一个示例流程。

local statejson = redis.call('HGET', '{numpool}:numstate', number)

local state = cjson.decode(statejson)

if (0 == state.callstate and 1 == state.regstate and 'unreg' == eventtype)  //state: idle+up

then

local newstate = {

callstate = 0,

regstate = 0

}

end

local newstatejson = cjson.encode(newstate)

redis.call('HSET', '{numpool}:numstate', number, newstatejson)

redis.call('LREM', '{numpool}:idle:up', -1, number)

return "1"

调用方法

假设lua脚本的名称为numpool.lua。

redis集群的IP为192.168.0.1:6666,192.168.0.2:6666,192.168.0.3:6666。

lua脚本需要预先load加载到redis的3个master主节点,加载命令如下

./redis-cli -h 192.168.0.1 -p 6666 SCRIPT LOAD "$(cat numpool.lua)"

"6f05c87dd30ac0882ecd4a2516eb8cbc575be07d"

加载人员需要把命令返回的sha值(6f05c87dd30ac0882ecd4a2516eb8cbc575be07d)记录并写入模块对应的配置文件中,供业务代码调用时使用。

lua脚本调用命令如下

evalsha 6f05c87dd30ac0882ecd4a2516eb8cbc575be07d 1 {numpool} $eventtype [$number]

详细的命令格式介绍这里就不再介绍,有需要可以另外查找。

命令示例:

新增号码

evalsha 6f05c87dd30ac0882ecd4a2516eb8cbc575be07d 1 {numpool} add 123456

呼叫选号

evalsha 6f05c87dd30ac0882ecd4a2516eb8cbc575be07d 1 {numpool} call

号码上线

evalsha 6f05c87dd30ac0882ecd4a2516eb8cbc575be07d 1 {numpool} reg 123456

总结

redis的lua脚本模式很强大,可以解决redis多命令执行的事务性问题。

同时,在redis的集群模式下,也有很多坑和问题需要避免,比如key名的问题,比如随机命令的问题。

空空如常

求真得真

公共号码池redis实现方案的更多相关文章

  1. 3 CRM 销售与客户 我的客户,公共客户池

    1.销售与客户的表结构 1.公共客户与我的客户 ---公共客户(公共资源) 1.没有报名 2.3天没有跟进 3.15天没有成单 客户分布表 龙泰 男 yuan 2018-5-1 3天未跟进 龙泰 男 ...

  2. Redis监控方案

    Redis 监控最直接的方法当然就是使用系统提供的 info 命令来做了,你只需要执行下面一条命令,就能获得 Redis 系统的状态报告. redis-cli info 内存使用 如果 Redis 使 ...

  3. 监控 Redis 服务方案

    RedisLive easy_install pip wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate python g ...

  4. Redis 监控方案

    一.概述 近些天,遇到Redis监控的应用场景,从网上搜罗了一些文章,做了整理. 二.工具列表 2.1 redis-faina 见参考文章1 2.2 redis-live 见参考文章1 2.3 red ...

  5. (转)淘淘商城系列——Redis持久化方案

    http://blog.csdn.net/yerenyuan_pku/article/details/72858975 Redis中设置key的过期时间 Redis中的expire命令用于设置key的 ...

  6. Redis 持久化方案

    目录 持久化简介 什么是持久化? Redis 持久化方案 RDB RDB 简介 save 指令 操作与配置 工作原理 bgsave 指令 操作与配置 工作原理 bgsave 配置执行 相关配置 工作原 ...

  7. MongoDB仲裁节点的理解以及memcached,zookeeper,redis,故障恢复方案思考.

    在进行副本集部署时我们会添加一个或多个仲裁节点,仲裁节点不用于备份数据,由于它职责的职责是负责选举主节点,所以对硬件没有太高要求,可以将它部署在单独的服务器上,这个服务器可以是监听服务器,也可以部署在 ...

  8. Redis持久化方案

    Redis可以实现数据的持久化存储,即将数据保存到磁盘上. Redis的持久化存储提供两种方式:RDB与AOF.RDB是默认配置.AOF需要手动开启. 默认redis是会以快照的形式将数据持久化到磁盘 ...

  9. redis持久化方案(十)

    方案分为两种方式: 1>Rdb方式 介绍:redis默认的方式,redis通过快照来将数据持久化到磁盘中 a.设置持久化快照的条件 在redis.conf中修改持久化快照的条件,如下: 比如:如 ...

  10. Redis缓存方案

    1 Redis简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发 ...

随机推荐

  1. C++ Qt开发:SpinBox数值微调框组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍QSpinBo ...

  2. org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException报错问题

    这个原因是 高版本SpringBoot整合swagger 造成的 我的项目是2.7.8 swagger版本是3.0.0 就会出现上面的报错 解决方式: 1.配置WebMvcConfigurer.jav ...

  3. HDFS存储原理

    冗余数据保存问题: 一个数据块默认被保存三次 好处:1.加快数据传输错误(假如要同时访问数据块1 因为他冗余存储就会有3份 所以会加快数据传输速度) 2.很容易检查数据错误 3.保证数据可靠性 数据的 ...

  4. SpringCloudAlibaba-OSS文件上传

    一.引入相关依赖 <dependencyManagement> <dependencies> <dependency> <groupId>com.ali ...

  5. 打造一个极度舒适的Chrome扩展项目开发环境

    大家好,我是 dom 哥.这是我关于 Chrome 扩展开发的系列文章,感兴趣的可以 点个小星星. Chrome 扩展能够提高浏览器的使用体验,通过自定义 UI 界面,监听浏览器事件,改变 Web 页 ...

  6. 华企盾DSC控制台无法登录常见处理方法

    1.查看服务器服务是否正常运行 2.服务器电脑的防火墙关闭了 3.telnet服务器IP和端口(服务器端口和数据库端口)是否是通的 4.如果是外网需要再控制台安装目录改setting配置文件 5.my ...

  7. 关于开放签CA数字证书设计的思考

    这几天在调研CA数字证书以及思考如何在产品中集成使用CA数字证书,主要的目的和方向有以下几点: 有法律效力的电子文件的签署需要使用权威的证书颁发机构(CA)颁发的数字证书: 我们希望找到更便宜的证书方 ...

  8. 4.elasticsearch中聚合查询

    elasticsearch聚合查询 什么是聚合,就是目的不是查询具体的文档,而是查询文档的相关性,此外还可以对聚合的文档在按照其他维度再聚合. 包含以下四种聚合 Bucket Aggregation ...

  9. C# 多线程 progressbar 界面不卡顿简单用法

    多线程进度条的简单使用,界面不卡顿.如下图: 简单源码如下: using System; using System.Collections.Generic; using System.Componen ...

  10. websocket实现实时直播

    websocket实现实时直播 这篇文章我首发于简书,拿到这里发表不过分吧?点个赞再走呗! 作为一名web开发者,我使用websocket实现实时直播(滑鸡版). 为什么是滑鸡版呢?因为他上不了生产, ...