MySQL 表占用空间排行
phpMyAdmin用多了不会手写SQL了吧
1 | select t.TABLE_NAME |
phpMyAdmin用多了不会手写SQL了吧
1 | select t.TABLE_NAME |
HBSpy: 这里我们综合了IVR, mod_dptools: IVR Menu
互动式语音应答(IVR)是一种自动化的电话系统,可以与来话交互以收集信息或按策略路由。
FreeSWITCH IVRs可以用FreeSWITCH支持的任何语言编写,包括JavaScript, Python, Perl, Lua和XML macro格式
源码中自带了一个用XML实现的示例IVR。IVR菜单加载自conf/autoload_configs/ivr.conf.xml
Dialplan调用IVR菜单的入口如下
1 | <extension name="ivr_demo"> |
IVR Macro文件位于conf/lang/en/demo/demo-ivr.xml
IVR Menu功能可以使你简单的用XML描述IVR菜单o
菜单定义在配置文件autoload_configs/ivr.conf.xml中
1 | <configuration name="ivr.conf" description="IVR menus"> |
IVR Menu支持使用phrase macro。在phrase后写phrase macro的macro名字即可。务必保证tts-engine, tts-voice和phrase-lang被正确设置。
1 | <menu name="main" |
有时需要在来话进入IVR应用之前添加如下内容,以保证之不在early media阶段被接听
1 | <action application="answer"/> |
菜单选项
每个菜单支持通过按键绑定一系列动作
1 | <entry action="menu-play-sound" digits="3" param="say: You pressed 3"/> |
这个应用可以让你在dialplan中做HTTP请求并接收返回。返回可以是plain text或json对象
为了使用mod_curl,你可以通过编辑modules.conf,去掉对应的注释来编译此模块
1 | #applications/mod_curl |
重新编译FreeSWITCH
1 | make |
HBSpy: 这里应该不需要这么麻烦,去掉注释后
make mod_curl-install
即可
编辑/usr/local/freeswitch/conf/autoload_configs/modules.conf使该模块在启动时加载
1 | <load module="mod_curl"/> |
此模块没有其它单独的配置文件
mod_curl提供了以下两个功能的API和dialplan APP
curl APP的语法是这样的
1 | <action application="curl" data="url [headers|json] [get|head|post [url_encode_data]]"/> |
curl APP会设置curl_response_data和curl_response_code两个变量。如果需要,curl_response_data也可以是headers/body或者json的
1 | <action application="curl" data="http://www.google.com"/> |
curl_sendfile有如下两种语法
1 | <action application="curl_sendfile" data="<url> <filename_post_name=/path/to/filename [nopost|foo1=bar1&foo2=bar2&...fooN=barN [event|none [uuid|identifier]]]"/> |
或者像这样使用通道变量
1 | <action application="set" data="curl_sendfile_report=event"/> |
url, filename, extrapost这些通道变量需要urlencode,data="“字段也是。如果调用此APP时,在data=”"字段里提供了全部的请求参数,你必须指定’nopost’来继续使用data line中其它的参数。如果你指定’uuid’作为identifier,此APP会自动的使用此session的uuid作为这个值。
HBSpy: 'nopost’这里的意思是说要使用[event|none [uuid|identifier]]啥的就不能让前面这儿空着,如果没有请求参数就写nopost。感觉是个常识
在CLI中调用此API,语法是这样的
1 | curl url [headers|json|content-type <mime-type>|connect-timeout <seconds>|timeout <seconds>] [get|head|post|delete|put [data]] |
1 | [api/bgapi] curl_sendfile <url> <filenameParamName=filepath> [nopost|postparam1=foo&postparam2=bar... [event|stream|both|none [identifier ]]] |
HBSpy: 原文中的这些例子就忽略了,都比较简单
如果你不喜欢ESL,或是需要与CTI交互的信息不多,那么mod_curl就是一个非常好的方式
把IVR与mod_curl相结合,就可以写出简单的自助信息查询等功能了
如果使用ESL,那么其实并不建议与ESL混用,在FS中想实现一个功能通常有许多种方式,在一个项目中使用统一的方式通常能够带来维护上的便利。
出于公司要开发呼叫中心相关业务被迫学习了FreeSWITCH orz
然而似乎软交换这东西并不怎么火热,中文的参考资料似乎只有杜金房(Seven Du)
老师的《FreeSWITCH权威指南》以下简称《权威》
官方文档写的凑合,但是一些细节交待的也不清不楚
出于为日后接手此项目的人员能在两周内快速上手,故逐步丰富该系列文档,主要形式是项目中使用或可能使用到的功能,给官方文档的做个翻译,并且加入一些个人的见解。
目前的见解大多是实践所得,并没有进行源码解析
mod_fifo 是一个呼叫中心APP,通过分配优先级可以自定义呼叫队列。
FIFO 的意思是『先入先出』。当有呼叫进入队列时,它们按序排列,以便于队列中最长等待时间的呼叫将被第一个得到应答。通常,FIFO呼叫队列被用于『先到先得』的呼叫场景,例如客户服务呼叫中心。
一种替代玩法是mod_callcenter,它更像是传统的ACD应用,由FreeSWITCH™社区的成员提供。
1 | <fifo name>[!<importance_number>] [in [<announce file>|undef] [<music file>|undef] | out [wait|nowait] [<announce file>|undef] [<music file>|undef]] |
记住如下概念将帮助你玩转mod_fifo
HBSpy: 坐席服务完客户之后并不挂机,而是在听MOH,当有新客户呼入时直接接听,省去坐席振铃时间
HBSpy: 队列中有呼入后,给注册到该队列的坐席们振铃。实践中的是会依次振铃,间隔会受agent的timeout, lag等参数的影响。
mod_fifo的全局配置参数在以下文件中
1 | $CONFDIR/autoload_configs/fifo.conf.xml |
通过以下方式注册坐席。类似Asterisk队列成员
1 | <configuration name="fifo.conf" description="FIFO Configuration"> |
timout: 在切换到下一个坐席前的振铃超时(秒)
simo: 同时振铃数,至少为1
HBSpy: 《权威》里把simo定义为『最大能服务的呼叫的数量』,好像跟官方说法有些出入?
HBSpy: wait与nowait的区别《权威》里解释的也比较清楚,不清楚的也可以联想一下前面的摘机坐席和挂机坐席
提示:如果你想在fifo呼叫坐席时,给来话一个指定的ID,可以设置origination_caller_id_name和origination_caller_id_num变量。可以通过{}在呼叫字符串上设置,或者通过set app在把来话放入fifo队列的dialplan中设置(要在’fifo in’执行之前)。
HBSpy: 这里的要在’fifo in’执行之前要特别注意,fifo相关的许多变量似乎都是这样的。从fifo执行的debug日志来看,在fifo in之后就会立即从通道变量的值中设置fifo相关的参数值,之后再怎么在执行前修改,比如在坐席接听之前修改fifo_announce,也不会生效。具体是否如此,要等分析完源码之后才能给出定论。所以你想用fifo_announce播放『工号XXX为您服务』似乎是很难的,《权威》中也并没有举这样的例子。
1 | <action application="fifo" data="myqueue in /tmp/exit-message.wav /tmp/music-on-hold.wav"/> |
这会使来话压入一个叫"myqueue"的队列,并且一直播放"/tmp/music-on-hold.wav"的等待音。在来话被出队时,播放"/tmp/exit-message.wav"
HBSpy: 于是这里的exit-message.wav就可以是通常的『为保证服务质量,您的通话将会被录音』,当然你也可以通过设置fifo_announce来实现,本质上是一样的。
1 | <action application="fifo" data="myqueue out nowait /tmp/caller-found.wav /tmp/agent-music-on-hold.wav"/> |
这会把"myqueue"队列中等待时间最久的来话取出,与坐席在当前通道连接,并给坐席播放"/tmp/caller-found.wav"
如果当前FIFO队列中没有来话,并且你设置的是"wait","/tmp/agent-music-on-hold.wav"就会一直给坐席播放直到有新呼叫到达。并且在通话结束时,坐席不必挂机就会回到FIFO队列
If you have the “nowait” parameter set and there are no calls in the FIFO queue, processing immediately continues past the FIFO queue. If there are one or more calls in the queue, only one call is retrieved and processing continues past the FIFO queue after that call ends.
如果你想指定一个来话出队,可以设置fifo_bridge_uuid为队列中来话的UDID
这里有一些方式可以用来给’fifo in’和’fifo out’设置MOH和放送音
1 | <action application="set" data="fifo_music=<sound path>"/> |
HBSpy: 我们说一些在我使用的版本上的实践结果以供参考:
fifo_music: 这个是来话在队列中等待时听的音乐,也是摘机坐席在等来话时听的音乐。
fifo_announce: 这个是当有坐席摘机时,给来话播放的,此时坐席是听不到的。fifo_music放完之前该来话也没被认为是被服务,也就是说如果在fifo_announce放完之前坐席就把电话挂了,该来话仍在队列中。反应到FIFO-Action上,应该是在consumer_start之后,caller_pop之前(待验证)
。然后就是之前说的,目前尚未想到一种好方法,通过此处设置实现播送坐席的工号等信息。
fifo_orbit_announce: 这个是来话在队列中等待超过一定时间后的放音。fifo_orbit_*这一系列都是用来控制等待超时的,fifo_orbit_exten中可以定义超时后的跳转exten和超时时长,所以fifo_orbit_announce在没有设置fifo_orbit_exten的超时时长时是不生效的。《权威》中的例子将此设置为『为保证服务质量,您的通话将会被录音』似乎是不合适的。另外还有个设置是fifo_chime_list, fifo_chime_freq在超时放音上也能实现类似的功能,但它其实更看重循环而非超时,并且不能进行exten跳转。
fifo_orbit_exten: 如上,超时后的跳转exten和超时时长
fifo_override_announce: 这里是给坐席放音的,一般可以用来给坐席提示来电号码。实践中在fifo_announce后播送,并不同时,感觉有些浪费时间,annouce和override_annouce取最长时间之后再双方进行通话不是挺好,不知道是freeSWITCH的BUG还是Feature。另外需注意,由于此设置最终影响的是坐席leg,故你在来话dialplan中设置的tts_engine和tts_voice需要在此通道重新设置。在《权威》中也提到了这个问题,但他的设置方式在我的实践中并没有生效。其它设置方式还可以是坐席的呼叫字符串,全局默认值等。PS:
参考了源码之后发现,缺乏tts_engine和tts_voice变量时,log中的提示也为Invaild Args,没有跟其它情况下的Invaild Args区分,对排错十分不友好。
(直译)
每一个FIFO队列可以有10种优先级槽位(默认优先级是5)。优先级1要高于优先级10。你可以通过如下设置把一个来话放入这10种优先级槽位中
1 | <action application="set" data="fifo_priority=1" /> |
消费者(坐席)可以通过设置fifo_pop_order来影响选择来话服务的优先级
1 | <action application="set" data="fifo_pop_order=1,2" /> |
可以通过逗号分隔符同时设置多个优先级顺序
这个场景有两个号码:7010是坐席用,会一直听MOH直到有来话;7011是来话用,会一直听MOH直到有空闲坐席
1 | <extension name="Agent_Wait"> |
坐席通过拨打7010后等待。来话通过被路由或转移到此号码上时就会在队列中等待,直到有空闲坐席为之服务
这个例子中,如果来话停泊时间超过了20秒就会被转移到1004这个号码上
1 | extension name="park" continue="true"> |
1 | fifo list|list_verbose [ fifo_name ] |
'fifo list’和’fifo list_verbose’都能给出许多关于当前定义过的队列的信息,包括正在队列中等待或是被服务的客户(来话),坐席等。如果提供了队列名,就只会列出指定名称队列的信息
HBSpy: list_verbose比list给出的信息多的多,包含了许多非fifo自身的信息,大多是通道变量
1 | fifo count [ fifo_name ] |
示例输出
1 | fifo_1:0:0:1:0:0 |
结果是返回每一个队列的一些统计值,用冒号分隔
1 | 队列名:消费者数:来话数:成员数:正在振铃的坐席数:空闲坐席数 |
1 | fifo importance <fifo_name> |
输出指定队列的重要权值
1 | fifo reparse [del_all] |
重新载入mod_fifo的配置;如果你使用的是mod_xml_curl来生成的配置,那么这个操作也会重新请求这个xml
del_all参数会引发内存清理(当队列为空时,并不会自动被删除)。内存在大多数情况下并不是什么问题,每个fifo队列的开销非常少,但在多租户的场景下,可能会是个事。
1 | add <fifo_name> <originate_string> [<simo_count>] [<timeout>] [<lag>] [expires] [taking-calls] |
给指定队列添加成员,使用的参数跟在Agent Callback章节里的一致。如果指定名称的FIFO队列不存在,就会自动创建一个。
1 | del <fifo_name> <originate_string> |
从指定队列中删除成员。当最后一个成员从队列中移除时,队列并不会被自动删除。
这些通道变量是mod_fifo根据该consumer的相关配置设置的。
如果该consumer是静态配置成member的,这些变量的值可以通过在consumer的呼叫字符串前用{}设置。使用{}设置多个变量时,以逗号分隔的 变量=值 形式。
如果consumer是通过执行一个dialplan来从fifo中接听电话的,这些变量也可以在consumer所执行的dialplan中用set应用设置。
HBSpy: 总结一下就是提醒你,Caller Leg和Consumer Leg不是一个Leg,一些在Caller Leg中设置的变量并不一定会自动的设置在Consumer Leg上,想想之前的tts_engine和tts_voice
It is generally best to do most telephony processing in the dialplan, not in your script. The FS team has put a great deal of effort into making sofia handle many edge cases of SIP processing, so trying to do all that in your script would prove hopeless. ↩︎
Investigation of regular high load on unused machines every 7 hours
原文如下:
https://blog.avast.com/investigation-of-regular-high-load-on-unused-machines-every-7-hours
这里贴一下结论:
#define LOAD_FREQ (5HZ+1) / 5 sec intervals */
So, contrary to the comment there, the load is not measured every 5s (HZ=1000 in CentOS6) but every 5.001 seconds!
Now, let’s do the math. If we add one ms every five s, how long does it take to add another full five s? In other words, what is the interference period? We need to add one ms 5000x. If we add it every five s, it takes us 25000s. Which, converted to hours, is: six hours, 56 minutes and 40 seconds. So we were slightly wrong, the spikes did not occur every seven hours.
The regular high load was really caused by launching a bunch of monitoring scripts every minute and by the fact that the load measurements slowly and slightly moves with a period of almost 7 hours.
Well, yeah. It turned out that it was really happening everywhere. But to our defense, on a much, much smaller scale. Why? First, most of our machines are much more powerful, so the spike in load is not that big and it does not trigger the threshold for alerts (it is based on load_per_core value). Second, most of the machines actually do something, so you won’t notice a small spike in load occurring every ~seven hours in the plot, as the load curve is not stable anyway. And third, the majority of the hosts only have a few collectd exec plugins configured, so the number of processes executed at one moment is significantly smaller.
附上一篇阿里云上的文章:
https://yq.aliyun.com/articles/484253
从这个函数中可以看到,内核计算load采用的是一种平滑移动的算法,Linux的系统负载指运行队列的平均长度,需要注意的是:可运行的进程是指处于运行队列的进程,不是指正在运行的进程。即进程的状态是TASK_RUNNING或者TASK_UNINTERRUPTIBLE。
Linux内核定义一个长度为3的双字数组avenrun,双字的低11位用于存放负载的小数部分,高21位用于存放整数部分。当进程所耗的 CPU时间片数超过CPU在5秒内能够提供的时间片数时,内核计算上述的三个负载,负载初始化为0。
假设最近1、5、15分钟内的平均负载分别为 load1、load5和load15,那么下一个计算时刻到来时,内核通过下面的算式计算负载:
load1 -= load1 - exp(-5 / 60) -+ n (1 - exp(-5 / 60 ))
load5 -= load5 - exp(-5 / 300) + n (1 - exp(-5 / 300))
load15 = load15 exp(-5 / 900) + n (1 - exp(-5 / 900))
其中,exp(x)为e的x次幂,n为当前运行队列的长度。
有兴趣的同学还可以更加深♂入的了解一下linux的Load Average算法
引起这问题的原因有很多,在这儿先贴一个StackOverflow上的讨论
http://stackoverflow.com/questions/6784463/error-trustanchors-parameter-must-be-non-empty
我们的场景是,在连接一个开启了security的WAS时产生的
使用的是WAS默认的key.p12和trust.p12
/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/nullNode01Cell/nodes/nullNode01/key.p12
/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/nullNode01Cell/nodes/nullNode01/trust.p12
先贴一份前期知识
文中说
在创建概要文件期间,WebSphere Application Server 将创建缺省密钥库文件 key.p12 和缺省信任库文件 trust.p12。另外,还将在 key.p12 文件中创建缺省的链证书。并且会从 key.p12 文件中抽取链式证书的根签署者或公用密钥并将其添加到 trust.p12 文件中。如果在进程启动期间这些文件不存在,那么会在启动期间进行重新创建。
缺省密钥库和缺省信任库的后缀分别为 DefaultKeyStore 和 DefaultTrustStore,您可以凭此确定这两个文件。另外,在 SSL 配置中,必须将 fileBased 属性设置为 true,以便运行时环境只使用缺省密钥库和缺省信任库。
在基本应用程序服务器上,缺省密钥和信任密钥库存储在配置库的节点目录中。例如,缺省 key.p12 和 trust.p12 库是使用 AppSrv01 概要文件名称、myhostNode01Cell 名称和 myhostNode01 节点名创建的。密钥库和信任库分别位于下列目录中:
[AIX Solaris HP-UX Linux Windows] C:\WebSphere\AppServer\profiles\AppSrv01\config\cells\myhostNode01Cell \nodes\myhostNode01\key.p12
[AIX Solaris HP-UX Linux Windows] C:\WebSphere\AppServer\profiles\AppSrv01\config\cells\myhostNode01Cell \nodes\myhostNode01\trust.p12
WebSphere Application Server 生成的所有缺省密钥库的缺省密码都是 WebAS。在进行初始配置之后,您应更改缺省密码,以便获得一个更安全的环境。
然而,并没有!
我们使用keytool查看这两个库
1 | [root@was nullNode01]# keytool -list -v -keystore key.p12 -storetype PKCS12 -storepass WebAS |
正常
1 |
|
说好的放进去呢
解决方案有两种,一种我附个别人的
http://www.wenhq.com/article/view_711.html
用http进行连接的,客户端代码比较简单,直接使用URL类进行连接并获取输入流即可。https不需要客户端证书,并且服务器端的证书是已经受信任的也同http一样容易。当验证的证书每年的费用得好几千元,为了省钱,这里主要是说java访问未验证证书的https的访问。
通常有两种方案:1、将证书导入到TrustStore文件中;2、修改X509证书信任管理器类的实现。推荐使用第一种方案,我们可以尽量让服务器的证书稳定,不在使用中修改就可以了。
第一步、导出服务器端证书。用ie连接地址,然后出现了证书确认的提示框,点击查看证书-详细信息,点击复制到文件,选择base64编码,导出保存文件为test.cert。
第二步、把证书从其它文件导入到TrustStore文件中。
keytool -import -file test.cer -keystore test_store
第三步、设置java的javax.net.ssl.trustStore的系统属性
System.setProperty(“javax.net.ssl.trustStore”, “D: \test_store”);//注意是绝对路径
这样通过HttpClient应用接口就可以访问我们自己的https服务了。
常见错误:javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: Java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty 异常,有两种情况:
1、没有设置或者文件路径设置错误;
2、证书就采用keytool的默认jks类型就可以,否则也会报错,参考内容
An implementation of PKCS12 as JCA keystore type “pkcs12″. Storing trusted anchors in PKCS12 is not supported. Users should store trust anchors in JKS format and save private keys in PKCS12 format.
来自http://www.cs.nyu.edu/artg/internet/Spring2006/readings/JSSERefGuide.html
No related posts.
第二种可以在WAS里进行配置,直接把key.p12里的证书导入trust.p12
Security > SSL certificate and key management > Key stores and certificates > NodeDefaultTrustStore > Personal certificates
Manages personal certificates.
Import certificates from a key file or key store
生成新trust.p12之后再次查看
1 | [root@was ~]# keytool -list -v -keystore trust.p12 -storetype PKCS12 -storepass WebAS |
这时候应该是跟key.p12内容差不多的,别问我为什么,不是很懂Java系
使用新的trust.p12作为trustStore去连接,然后就正常了
理论上看起来好像有一些问题,但他的确正常了,有需要的可以参考
这是一次我认为并不常见的Cannot import pyOpenSSL的问题的处理
1 | [root@NMS winrm]# python test.py |
尝试手动执行
1 | [root@NMS OpenSSL]# python /usr/local/lib/python2.7/site-packages/OpenSSL/__init__.py |
EC_GROUP_new_curve_GF2这东西应该是openssl提供的
1 | [root@NMS OpenSSL]# ldd /usr/local/lib/python2.7/site-packages/cryptography/hazmat/bindings/_openssl.so |
libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007f179af33000)
1 | [root@NMS OpenSSL]# nm /usr/lib64/libcrypto.so.10 |
问题应该出在libcrypto.so.10上,由于这系统不是我亲自一点点编译的,所以暂时不知道为什么
但重新编译openssl是少不了的
而且要注意加-fPIC shared
1 | ./config -fPIC shared |
再nm一下
1 | [root@NMS openssl-1.0.1e]# nm libcrypto.so | grep EC_GROUP_new_curve_GF2m |
XXX: 需要注意,openssl相关的太底层,与许多基础功能相关,尤其是你SSH到这台服务器上进行操作,一不小心sshd就会跪,就再也SSH不上了,处理的时候一定要小心再小心
1 | Active Directory ? ????????'EXCHANGE\administrator'???????? |
登录到本机的PowerShell执行一些命令是没问题的,当需要加载Exchange的PowerShell时,就会遇到Double Hop或者叫Second Hop问题
目前PyWinRM提供的支持Double Hop的方式是起用WinRM CredSSP
https://github.com/diyan/pywinrm#enabling-winrm-on-remote-host
Enable WinRM CredSSP authentication. This allows double hop support so you can authenticate with a network service when running command son the remote host. This command is run in Powershell.
1 | Enable-WSManCredSSP -Role Server -Force |
同时需要pip install requests-credssp和pip install pywinrm[credssp]
但是目前pip库中的pywinrm是有问题的,是不提credssp组件的。所以需要从git源码python setup.py install
然后在winrm.Session的时候添加transport=“credssp”
credssp: Will use CredSSP authentication for both domain and local accounts. Allows double hop authentication. This only works over a HTTPS endpoint and not HTTP.