公共号码池redis实现方案
概述
在企业级呼叫模型中,号码资源总是有限的,企业员工在使用有限的号码资源外呼时,就会有号码冲突的问题,如何解决多人共用少量号码的选号问题?
最近有一个新的业务需求,需要解决公共号码池的选号问题,号码池中的号码也有诸多约束,本文从这个需求中的号码池功能点出发,谈一谈使用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实现方案的更多相关文章
- 3 CRM 销售与客户 我的客户,公共客户池
1.销售与客户的表结构 1.公共客户与我的客户 ---公共客户(公共资源) 1.没有报名 2.3天没有跟进 3.15天没有成单 客户分布表 龙泰 男 yuan 2018-5-1 3天未跟进 龙泰 男 ...
- Redis监控方案
Redis 监控最直接的方法当然就是使用系统提供的 info 命令来做了,你只需要执行下面一条命令,就能获得 Redis 系统的状态报告. redis-cli info 内存使用 如果 Redis 使 ...
- 监控 Redis 服务方案
RedisLive easy_install pip wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate python g ...
- Redis 监控方案
一.概述 近些天,遇到Redis监控的应用场景,从网上搜罗了一些文章,做了整理. 二.工具列表 2.1 redis-faina 见参考文章1 2.2 redis-live 见参考文章1 2.3 red ...
- (转)淘淘商城系列——Redis持久化方案
http://blog.csdn.net/yerenyuan_pku/article/details/72858975 Redis中设置key的过期时间 Redis中的expire命令用于设置key的 ...
- Redis 持久化方案
目录 持久化简介 什么是持久化? Redis 持久化方案 RDB RDB 简介 save 指令 操作与配置 工作原理 bgsave 指令 操作与配置 工作原理 bgsave 配置执行 相关配置 工作原 ...
- MongoDB仲裁节点的理解以及memcached,zookeeper,redis,故障恢复方案思考.
在进行副本集部署时我们会添加一个或多个仲裁节点,仲裁节点不用于备份数据,由于它职责的职责是负责选举主节点,所以对硬件没有太高要求,可以将它部署在单独的服务器上,这个服务器可以是监听服务器,也可以部署在 ...
- Redis持久化方案
Redis可以实现数据的持久化存储,即将数据保存到磁盘上. Redis的持久化存储提供两种方式:RDB与AOF.RDB是默认配置.AOF需要手动开启. 默认redis是会以快照的形式将数据持久化到磁盘 ...
- redis持久化方案(十)
方案分为两种方式: 1>Rdb方式 介绍:redis默认的方式,redis通过快照来将数据持久化到磁盘中 a.设置持久化快照的条件 在redis.conf中修改持久化快照的条件,如下: 比如:如 ...
- Redis缓存方案
1 Redis简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发 ...
随机推荐
- 【C#】【DateTime】获取当前时间的前一天或者后一天方法学习
假期懈怠了一阵子,因为工作环境没有网络,随即记录下当时的问题: 1.关于DateTime.Now(2023/10/07)的前一天的时间(2023/10/06),想要通过ToString转换成字符串后除 ...
- pytest框架学习-标签@pytest.mark.
标签 自定义标签(区分大小写) 可以标记测试用例,对测试用例进行分组,有利于对测试用例进行筛选. 比如:给用例打标为API,代表接口自动化的用例,打标方法为@pytest.mark.API,打标后,需 ...
- 使用dtd定义属性
- 聊一聊 C# 的线程本地存储TLS到底是什么
一:背景 1. 讲故事 有朋友在后台留言让我说一下C#的 ThreadStatic 线程本地存储是怎么玩的?这么说吧,C#的ThreadStatic是假的,因为C#完全是由CLR(C++)承载的,言外 ...
- C语言基础之因子分解
要求: 从键盘输入一个正整数,然后将该整数分解为1和各个质因子的相乘,如果输入的整数本身就是质数,则应分解为1和该数本身相乘. 输出格式: 因子分解,因子由小到大输出. 如:1* 2* 2* 3 代码 ...
- Guava常用工具类总结
=== -"我想写得更优雅,可是没人告诉我怎么写得更优雅" -"Null的含糊语义让人很不舒服.Null很少可以明确地表示某种语义,例如,Map.get(key)返回Nu ...
- Json Schema介绍 和 .net 下的实践 - 基于Lateapexearlyspeed.Json.Schema - 基础1 - type关键字和string类型
本系列旨在介绍Json Schema的常见用法,以及.net实现库Lateapexearlyspeed.Json.Schema的使用 这篇文章将介绍Json Schema中的type关键字,和stri ...
- Apache Hudi在信息服务行业构建流批一体的实践
个人介绍 李昂 高级数据研发工程师 Apache Doris & Hudi Contributor 业务背景 部门成立早期, 为了应对业务的快速增长, 数仓架构采用了最直接的Lambda架构 ...
- 14、Flutter Card卡片组件
Card是卡片组件块,内容可以由大多数类型的Widget构成,Card具有圆角和阴影,这让它看起来有立 体感. Card实现一个通讯录的卡片 class MyApp2 extends Stateles ...
- 一文带你了解Web前端发展历程
摘要:自互联网行业发展以来,web前端不断发展变化着.在前人的基础上,后人有幸能够站在前人的肩膀上行走.前端的发展变化不仅是继承式的迭代,同时也是不断的变革和创造. 一.前端到底是个什么? 简单点说, ...