json_decode(jsondecodeerror翻译中文)

zhangyang 2022-05-11 阅读:14
  

公共号码池redis实现方案

概述

  在企业级的呼叫模型中,号码资源总是有限的。当一个企业的员工使用有限的号码资源拨打电话时,就会出现号码冲突的问题。多人共用少量号码时如何解决选号问题?

  最近有一个新的业务需求,需要解决公众号池的选号问题。号码池中的号码也有很多限制。基于这一需求中号码池的功能点,讨论了如何利用redis实现分布式公众号码池的号码选择模型。

环境

   CentOS:CentOS 7.0版(最终版)或更高版本

  自由开关:1.8.7版

  海湾合作委员会:4.8.5

  Redis集群版本:redis-3.0.7

需求描述

  客户申请对应一个公众号池。号码池的功能描述如下:

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

  2.号码池中的号码有注册在线状态和离线状态。

  3.当客户需要呼出时,号码池中的号码应该被占用,被占用的号码不能再被占用。通话结束后,占用结束,恢复空闲状态。

  显式数字的约束:

  1.数字不能同时出现。

  2.号码在线。

  3.号码状态改变支持分布式多线程。

需求分析

  号码的状态分为通话状态的闲/忙和在线状态的上/下。

  事件数量,添加,删除,注册,注销,呼叫,挂断。

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

  在模型中,选择了redis列表和hash哈希表两种数据结构来存储号码池数据。

  list用于保存空闲起来的号码,hash用于保存号码池中所有号码的当前状态。

  多种数据结构的Redis操作,为了满足分布的要求,必须想办法把多种操作组合成原子操作。有两个可供选择的方向:事务和lua脚本。

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

数据模型

   redis的数据结构增加两个,分别保存免费在线号码列表和全号状态。

  在线号码列表,列表类型,键为{numpool} :Idle3360up,值为数字。

  总数状态,哈希类型,KEY为{numpool}:numstate,字段为数字,值为json格式字符串。

  {

  呼叫状态' 33600,//呼叫状态,0-空闲,1-忙碌

  regstate ' 33600//联机状态,0-向下,1-向上

  }

  几个注意事项:

  为什么选择LIST而不是SET?本来我想选择SET,用SPOP命令随机选择数字,但是lua脚本不能调用random命令,所以我退而求其次选择LIST。

  关键字中使用前缀{numpool}的原因。在redis集群环境中,必须保证LUA脚本和脚本中使用的所有键都在同一个槽中。因此,散列标签前缀{numpool}用于确保多个键位于redis的同一个槽中。

脚本逻辑

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

  数字_状态_表格

  {

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

  Nodata添加空闲停机增加数量状态

  Nodata del nodata返回成功。

  Nodata reg nodata返回失败。

  Nodata取消注册nodata返回失败。

  Nodata调用nodata返回失败。

  Nodata挂起nodata返回失败

  Idle up添加idle up成功返回。

  Idle up del nodata删除号码状态并删除空闲号码。

  成功返回闲置注册闲置。

  Idle up unreg idle down修改号码状态并删除空闲号码。

  空闲呼叫忙起来修改号码状态并删除空闲号码。

  闲置挂起闲置挂起返回错误

  Idle down添加idle down成功返回。

  空闲停机删除无数据删除号码状态

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+upthen  local newstate = {    callstate = 0,    regstate = 0  }endlocal 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名的问题,比如随机命令的问题。

  空空如常

  求真得真

评论(0)

二维码