FPV system setup philosophies by Team BlackSheep [转]

一月 3rd, 2011

http://www.rcgroups.com/forums/showthread.php?t=1323951

--------------------------------------------------------------------------------

I'm getting sick and tired of constant discussions filled with superstitious and non-factual contributions about issues that are plain and simple and not even worth a 2nd thought. There is so much misinformation being spread on the FPV section of this forum I feel the need to put all of the things that have been tried & tested in countless environments and situations by myself and other experienced FPV pilots into a nice little list. Follow these simple rules and regardless of what everyone else is saying, you will have success in FPV. This does not mean that not following this list means your FPV system will not work at all, but it does mean it will not work as safely, as reliably and as well.

Before we get to the list, let me just point out that while these points reduce the possibility of technical failures resulting in the loss of your aircraft, they do not account for stupidity on behalf of the pilot. If you want to fly into a tree and you consequently lose your plane, well, you shouldn't have flown into the tree.

  • Start with the bare essentials and add equipment one step at a time. after each new equipment was added do proper range- and stress tests.
  • Do not fly with a video system that is capable of outperforming your R/C system in terms of range
  • Do not fly with a R/C frequency higher than the video frequency (e.g. 2.4GHz R/C, 900MHz video).
  • Monitor the vitals of your plane (R/C and battery). Flying with a digital link without RSSI is dangerous.
  • Do not use 2.4GHz R/C unless you fly well within its range limits, in noise-free environments and always within LOS. since this is most likely never the case, it is recommended to not use 2.4GHz R/C systems.
  • Do not fly at the limits of video. if you see noise in your picture, turn around and buy a higher-gain antenna before going out further.
  • Shielded wires or twisted cables only. anything else picks up RF and can cause problems
  • When using powerful R/C transmitters, make sure your ground station equipment is properly shielded
  • Adding RTH to an unreliable system does not increase the chances of getting your plane back. Work on making your system reliable without RTH first, then add RTH as an additional safety measure if you must. At this point you will most likely realize RTH is not required.
  • Avoid powering the VTx directly. step-up or step-down the voltage and provide a constant level of power to your VTx, otherwise make sure your VTx runs reliably until the battery dies. Try to avoid 12V VTx on 3S systems.
  • Do not power your camera directly unless it works along the complete voltage range of your battery. step-up or step-down the voltage and provide a constant level of power to your camera. make sure your camera runs until your battery dies.
  • A single battery system is safer. 2 batteries in parallel to mitigate further sources of failure. reverse current protection is recommended, but usually not feasible
  • For maximum video range, use 2.4GHz video with high-gain antennas
  • When flying with R/C buddies that fly on 2.4GHz, or when flying in cities, it is perfectly possible to use 2.4GHz video provided you stick to the channels that do not lie in their band (ch5 & 8 for lawmate)
  • Do not use diversity as a replacement for pointing your antennas. diversity should be used to mitigate polarization issues
  • Improving the antenna gain on the receiver end is better than increasing the output power (except in RF-noisy areas). 500mW is plenty of power, more tx power causes more issues with RF on your plane.
  • Do not buy the cheapest equipment unless it is proven to work reliably (e.g. capacitors falling off, multitudes of bugfix firmware updates, community hacks and mods are a good indicator of poor quality and something you do NOT want to buy). Saving $50 is simply not worth losing your plane.

FAA 雷达频率

十二月 31st, 2010

The FAA airport radar transmits at 1330 and 1350 MHz. The transmitter is located east of san juan. The radar is used for traffic control around puerto rico (it is not the radar used for landing the planes).



诱导阻力

十二月 17th, 2010

机翼同一般物体相似,也有摩擦阻力和压差阻力。对于机翼而言,这二者合称“翼型阻力”。机翼上除阻力外,还有“诱导阻力”(又叫“感应阻力”)。这是机翼所独有的一种阻力。因为这种阻力是伴随着机翼上举力的产生而产生的。也许可以说它是为了产生举力而付出的一种代价。

如果有一架飞机以某一正迎角a作水平飞行,它的机翼上面的压强将降低,而下面的压强将增高,加上空气摩擦力,于是产生了举力Y。这是气流作用到机翼上的力,根据作用和反作用定律,必然有一个反作用力即负举刀力(-Y),由机翼作用到气流上,它的方向向下,所以使气流向下转折一个角度a,这一角度叫“下洗角”。随着下洗角的出现,同时出现了气流向下的速度。这一速度叫做“下洗速(w)”。下洗的存在还诱导阻力可由风洞实验观察出来。
由实验可知:当飞机飞行时,下翼面压强大、上翼面压强小。由于翼展的长度是有限的,所以上下翼面的压强差使得气流从下翼面绕过两端翼尖,向上翼面流动。当气流绕流过翼尖时,在翼尖那儿不断形成旋涡。旋涡就是旋转的空气团。随着飞机向前方飞行,旋涡就从翼尖向后方流动,并产生了向下的下洗速(w)。下洗速在两个翼尖处最大,向中心逐渐减小,在中心处减到最小。这是因为旋涡可以诱导四周的空气随之旋转,而这又是由于空气粘性所起的作用。空气在旋转时,越靠内圈,旋转得越快,越靠外圈,旋转得越慢。因此,离翼尖越远,气流垂直向下的下洗速就越小。

上单翼,下单翼

十二月 17th, 2010

如果运输机是上单翼,客机是下单翼,那么两种机型在机翼外段、驾驶舱风挡、操纵系统上可以通用,但机体完全通用不现实。现成的例子就是日本的C-X运输机和P-X反潜机,两者在开发阶段就强调的部件通用性,却也只做到机体15%通用的程度。

其实说起来都是和降落有关,大型飞机一般都采用中央翼盒,为了避免承力件横穿座舱,一般很少用中单翼。客机使用下单翼主要是为了在迫降的时候中央翼盒先着地,以保证客舱的安全。由此带来的附加效应是翼吊发动机离地较近,便于维修。而客机的起降条件比较好,发动机距地面近不会对进气条件造成影响。当然,有时候也要考虑一下,比如737的下唇口就为了保证足够的离地高度而压扁了,成了飞机的特征。
军用运输机因为有野战机场起降的要求,发动机离地高度要求较高,同时货舱地板又要低一些,因此采用了上单翼。同时由于起落架只能装在机身上,反而缩短了起落架支杆的长度,有利于在野战机场降落。由于主翼为上单翼,为了防止大迎角下水平尾翼因处于主翼尾流而失效,所以尽量提高水平尾翼的高度,形成了T字型的尾翼布局。而客机采用下单翼,倒T字型的尾翼布局就可以满足操控要求了。
由于客机和军用运输机不同的使用要求,其承力结构差别较大,在气动布局不同的情况下其机身结构设计是不可能通用的。

航模防止绕弯路Tips

十二月 17th, 2010
  • 桨的字体是要朝机头的
  • 尾冲 大侠

    试飞的时候重心尽量靠前,这样静安定性好,好飞,但是配平阻力会很大。 首飞飞起来之后再一点一点往后移动中心(每个架次挪一点),同时对应微调升降,保证飞机平飞。 这样配平阻力越来越小(平飞电流也降低),静安定性也越来越小。这样一个架次一个架次的逼近,直到最后你觉得这个静安定性你还能掌握,再放宽就把握不住了 为止(我是说不装平衡仪,装了平衡仪就没讨论的意义了,你飞机可以配制成静不安定的,跟现代战斗机一样),这个你能把握的安定性跟你的水平相关,高手可以 放很宽,新手就少放点。

    确定好中心后再挑升降微调,飞机拉高,松手,关油门让他俯冲,如果飞机很快就自己拉起来的话就松一步升降微调,一步一步的调试,直到最后飞机进入俯冲后很 直很直的斜向下俯冲,没有波动为止,这个时候升降舵微调回调一步或者两步就行了,保证飞机配平阻力小,也能自己改出俯冲状态。

摄像机CCD芯片与DSP类型(转帖)

十二月 7th, 2010

关于摄像机的分辨率,

分辨率的定义是在影像像素的基础上的,如下所示:

1)影像像素在25万像素(pixel)左右、彩色分辨率为330线、黑白分辨率400线左右的低档型。

2)影像像素在25-38万像素左右、彩色分辨率为420线、黑白分辨率500线左右的中档型。

3)影像像素在25万像素左右、彩色分辨率为480线、黑白分辨率600线左右的高档型。

摄像机参数标得越来越玄糊,决定探究竟:

CCD的结构为三层,第一层是“微型镜头”,第二层是“分色滤色片”以及第三层“感光层”。

Super HAD CCD中文就是超级HAD CCD传感器,增加了聚光镜头,比普通CCD效果大有改善。

Sony Super Had Ⅱ CCD感度提升了136%,同时噪点更低。多用于低照度枪机。

CCD 名称 CCD 型号 CCD 尺寸 CCD感度
Super HAD II ICX639AK 1/3″ 2250
Super HAD ICX409AK 1/3″ 950
Exview ICX259AK 1/3″ 1100

目前Super HAD CCD是各品牌使用最多的CCD:如405,409

SONY Super HAD II CCD芯片分别为ICX633,ICX639

DSP: 3172 3142 4103 日立93118

EXview 255 259

EX-View:是索尼公司研发用来提高其CCD感光度的一个感光度提高技术,一是两个可见光的因素,二是四倍近红外波的波长。

Vertical double-density interline CCD 213AK

采用CCD+DSP芯片的识别方法:

3142+405 420TVL 经济适用

3142+633 420TVL 刚出来的CCD,用的厂家比较少,照度很低,但是偏色的严重(我所看过的)

2163+405 480TVL(所谓假高线) 性价比高,照度低适合装红外机

2163+409 480TVL 照度高不适合装红外机,适合普通枪机半球

4103+405 480TVL(所谓假高线) 性价比高,照度低适合装红外机

4103+409 480TVL(传说的520TVL)照度高不适合装红外机,效果一般

4103+639 480TVL(传说的540TVL)照度还可以,适合装红外机.

3172+639 480TVL(传说的540TVL)

2163+255 420TVL 低线低照,用的少不做评论

2163+529 480TVL 高线低照,用的少不做评论

633是以后代替405的,他灵敏度比405高上很多,如果技术到位做出来的照度会非常低,装红外效果不错

639是以后代替409的.灵敏度大大高过409,如果技术到位,将弥补市场上高线红外机效果差这一缺陷.

在相同参数下,感觉3172+409白天的色彩比较鲜艳一点。3172+639白天的色彩有点偏兰。而3172+409在晚上没有红外灯时,噪点比较大。3172+639在晚上没有红外灯时,噪点比较小,比3172+409的照度好一点.

639与409的主要差别,在于639的赶度提高了(约等于以前的405),其余的639跟409差不多。
问题在于:
1.感度提高了,好处在低照状况下,效果比409好多了,也就是夜视效果比较好。
2.但感度提高了,相当于入光量变大了,在不改参数的情况下,颜色就会变淡。
3.前端那颗 3796(CDS/AGC,取代原来的2096),如果只是用原来的2096参数,效果也不好
所以,如果用639取代409,照度变好了,但dsp参数得修改,连色矩阵的运算都得调整过,才能有409的彩色效果,不是把CCD换掉就行了。可惜,这几年来,会调DSP参数的人越来越少了,还得等一阵子,大家抄来抄去,总会抄出个名堂来的。不管搭3172还是4103都一样。

另外,用633取代405,情况更严重,633其实感度有点太好了,夜视效果非常好,但在大太阳下很容易过曝,掉色更严重,搭3142这颗本来就不好的DSP,想搞好很难,加上这几年都在打价格战,零件都尽量省了,一般板子噪讯都很高,搭上633这高感度的CCD,噪讯问题更严重。但633如果搭3172或4103的话,就好搞多了,只是成本变高,只能当假高线卖了。

632(N制的),更惨,多了个严重色滚,无解,除非加电源同步,但那价格没几个人能接受的
真不知道索尼是怎么想的,可能是为接下来的新DSP做准备的吧。
4103是代替2163的DSP,可不知道为什么,SONY又接着推出了3172.而3172做出来的效果却并不理想.

3172方案效果确实比4103要好一些,不过芯片温度比较高,最高的时候达到近70度,做还是可以做的,第一,做双板.第二,电源要用效率高的,发热量小.第三,最好加散热片.还有一种做法,3172 3.3V上面用2.9V-3.0V去供,哈哈,很多这样搞的噢,要小心

639CCD的DSP可用日立93118的,比3172的要更好

红外一体机选购考虑因素:

1,采用先进的方案:420线一般采用的是3142+633方案,480线一般采用4103+639方案,500线以上我们采用3172+639方案。
2,采用稳定的LED红外灯:我们的红外系列采用的是台湾鼎元的LED红外灯,其寿命长,功率稳定,散热性较好,红外线发射均匀,不易因为电流瞬间过大而烧毁。并且采用“双灯技术”,宽角和窄角红外灯搭配,解决了红外灯角度与距离的互补效果。
3,采用专用IR镜头:红外机因其接受红外线反射原理在夜间成像,因此专用的IR镜头更有利接受红外线,从而夜间成像效果更佳。而我公司选用的IR镜头均为韩国进口镜头,不偏焦。
4,采用双滤光片切换:因红外彩转黑摄像机为了达到夜间接受红外线成像,需要一块特制滤光片,如果共用同一滤光片,那么白天的成像效果不佳,容易造成偏色,采用双滤光片切换可以很好的解决这一问题。
5,采用双玻设计:采用双玻设计可有效避免反光,因此摄像效果更佳。

实际评价与测试:

3142+633BK:夜视效果还可以:

3172+639BK:夜视效果听说还可以:

高线/低线、IC及电流关系:

线 数 IC及CCD配搭 电 流

420TVL 3141(1267、2096)+ 405 120MA

450TVL 2163(2006、2480)+ 405 120MA

480TVL 2163(2006、2480)+ 409 170MA

450TVL 2163(2006、2480)+ 255 170MA(低照度)

480TVL 2163(2006、2480)+ 259 170MA(高线低照度)

有420线(405+3142+2096) 480线(409+2163+2480+2006)

520线(409+3172+2096)

1/3"SONY420线彩色摄象机 D3141+405AK
1/3"SONY460线彩色摄象机 D2163+405AK
1/3"SONY480线彩色摄象机 D2163+409AK
1/3"SONY480线彩色摄象机 D2163+408AK
1/3"SONY535线彩色摄象机 D3172+409AK
1/4"SONY420线彩色半球 D3141+227AK
1/3"SONY420线彩色半球 D3141+405AK
1/3"SONY460线彩色半球 D2163+405AK
1/3"SONY480线彩色半球 D2163+409AK
1/3"SONY600线黑白半球 D2463+409AL

1/4"SONY420线彩色转黑白摄象机(软件转) D3141+227AK
1/3"SONY420线彩色转黑白摄象机(软件转) D3141+405AK
1/3"SONY460线彩色转黑白摄象机(软件转) D2163+405AK
1/3"SONY480线彩色转黑白摄象机(软件转) D2163+409AK
1/3"SONY480线彩色转黑白摄象机N制(软件转) D2163+408AK
1/3"SONY535线彩色转黑白摄象机N制(软件转) D3172+408AK
1/4"SONY420线彩色转黑白半球(软件转) D3141+227AK
1/3"SONY420线彩色转黑白半球(软件转) D3141+405AK
1/3"SONY460线彩色转黑白半球(软件转) D2163+405AK
1/3"SONY480线彩色转黑白半球(软件转) D2163+409AK
1/3"SONY535线彩色转黑白半球(软件转) D3172+409AK
1/3"SONY420线彩色低照度摄象机 D3141+255AK
1/3"SONY460线彩色低照度摄象机 D2163+255AK
1/3"SONY480线彩色低照度摄象机 D2163+259AK
1/3"SONY520线彩色低照度摄象机 D3172+259AK
1/2"SONY535线彩色摄象机 D3172+259AK

Jetta/Bora MK4 G68损坏 8年了……

十月 9th, 2010

2002年出厂的车,2010年10月7号的一个早上终于挂了。。。由于十一期间满北京连续跑路,AG4坏了,在3500转还死活不换档。

接上VAG-COM一查:

奥迪大众VAS-5051B版本: Release  311.2-N

控制模块部件号: 01M 927 733 KJ
组件和/或版本: AG4 Getriebe 01M    4952
软件代码: 00000
维修站代码: WSC 00000
2 个故障码已找到:
00281 -   车速传感器( G68 )
03-00 -  没有信号
00652 -   档位监控/换档范围控制器
27-10 -  不可靠信号 - 间歇

奥迪大众VAS-5051B版本: Release  311.2-N
控制模块部件号: 01M 927 733 KJ       组件和/或版本: AG4 Getriebe 01M    4952           软件代码: 00000         维修站代码: WSC 000002 个故障码已找到:00281 -   车速传感器( G68 )        03-00 -  没有信号00652 -   档位监控/换档范围控制器        27-10 -  不可靠信号 - 间歇

经学习AG4 01M有两个传感器,Transmission Vehicle Speed Sensor (VSS) - G38,Vehicle Speed Sensor - G38。从字面上看,一个是传动系统的车速指示,一个是真实车速指示。从逻辑上推断,这两个多是感磁的霍尔元件,在高温下突然间挂掉的多半是电子系统。试图消故障码,但G68故障码死活消不掉,进一步怀疑是探头失效。

于是连续打了N个电话,由于过节期间人们都疯狂跑路,车统一在十一之后坏掉了,到处排队。于是开始找自动变速箱专修店,下午3点赶到,试车,G68位置很缺德,比较难拆……

损坏的传感器:

这家店--洪宇修理铺,还不错,很麻利。这小店能看出来缺乏正规的培训,小工略显毛躁,但是依靠自己的能力也创出了特长:

Memcached深度分析(转载)

九月 1st, 2010

原作网站已经木有了。
------------------------------------------------------------------------------------------

Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库 负载,提升性能。关于这个东西,相信很多人都用过,本文意在通过对memcached的实现及代码分析,获得对这个出色的开源软件更深入的了解,并可以根 据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析,加深对memcached的使用方式理解。

本文的部分内容可能需要比较好的数学基础作为辅助。

◎Memcached是什么

在阐述这个问题之前,我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用,虽然memcached 使用了同样的“Key=>Value”方式组织数据,但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的,也就是说 它不是本地的。它基于网络连接(当然它也可以使用localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon方式)。

Memcached使用libevent库实现网络连接服务,理论上可以处理无限多的连接,但是它和Apache不同,它更多的时候是面向稳定的持 续连接的,所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200,这和Linux线程能力有关系,这个数值是可以调 整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的,memcachd有自己的内存分配算法和管理方式,它和共享内存没有 关系,也没有共享内存的限制,通常情况下,每个memcached进程可以管理2GB的内存空间,如果需要更多的空间,可以增加进程数。

◎Memcached适合什么场合

在很多时候,memcached都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有人发贴,类似于“如何提高效率”,回复是“用 memcached”,至于怎么用,用在哪里,用来干什么一句没有。memcached不是万能的,它也不是适用在所有场合。

Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应 用,memcached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源,即使是UNIX本地连接也一样。 在我之前的测试数据中显示,memcached本地读写速度要比直接PHP内存数组慢几十倍,而APC、共享内存方式都和直接数组差不多。可见,如果只是 本地级缓存,使用memcached是非常不划算的。

Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销,而且它是使用内存来管理数 据的,所以它可以提供比直接读取数据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached可以大大降低数据库压力,使系统执行效率 提升。另外,memcached也经常作为服务器之间数据共享的存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached 中,被多个应用共享。

需要注意的是,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会丢失,所以 memcached不能用来持久保存数据。很多人的错误理解,memcached的性能非常好,好到了内存和硬盘的对比程度,其实memcached使用 内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接 的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了,memcached进程本身并不占用多少CPU资源的情况。

◎Memcached的工作方式

以下的部分中,读者最好能准备一份memcached的源代码。

Memcached是传统的网络服务程序,如果启动的时候使用了-d参数,它会以守护进程的方式执行。创建守护进程由daemon.c完成,这个程 序只有一个daemon函数,这个函数很简单(如无特殊说明,代码以1.2.1为准):

CODE:
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>int
daemon(nochdir, noclose)
int nochdir, noclose;
{
int fd;

switch (fork()) {
case -1:
return (-1);
case 0:
break;
default:
_exit(0);
}

if (setsid() == -1)
return (-1);

if (!nochdir)
(void)chdir(”/”);

if (!noclose && (fd = open(”/dev/null”, O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
(void)close(fd);
}
return (0);
}

这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备, daemon 就建立成功了。

Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下:

1 、调用 settings_init() 设定初始化参数
2 、从启动命令中读取参数来设置 setting 值
3 、设定 LIMIT 参数
4 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式)
5 、检查用户身份( Memcached 不允许 root 身份启动)
6 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道)
7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数)
8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab
9 、如设置中 managed 生效,创建 bucket 数组
10 、检查是否需要锁定内存页
11 、初始化信号、连接、删除队列
12 、如果 daemon 方式,处理进程 ID
13 、event 开始,启动过程结束, main 函数进入循环。

在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。

memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根据 state 成员的状态来决定动作。

Memcached 使用一套自定义的协议完成数据交换,它的 protocol 文档可以参考: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

在API中,换行符号统一为\r\n

◎Memcached的内存管理方式

Memcached有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式管理内存空间,而并不是每次需要写入数据的时候去 malloc,删除数据的时候free一个指针。Memcached使用slab->chunk的组织方式管理内存。

1.1和1.2的slabs.c中的slab空间划分算法有一些不同,后面会分别介绍。

Slab可以理解为一个内存块,一个slab是memcached一次申请内存的最小单位,在memcached中,一个slab的大小默认为 1048576字节(1MB),所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk,每个chunk里保存一个 item,每个item同时包含了item结构体、key和value(注意在memcached中的value是只有字符串的)。slab按照自己的 id分别组成链表,这些链表又按id挂在一个slabclass数组上,整个结构看起来有点像二维数组。slabclass的长度在1.1中是21,在 1.2中是200。

slab有一个初始chunk大小,1.1中是1字节,1.2中是80字节,1.2中有一个factor值,默认为1.25

在1.1中,chunk大小表示为初始大小*2^n,n为classid,即:id为0的slab,每chunk大小1字节,id为1的slab, 每chunk大小2字节,id为2的slab,每chunk大小4字节……id为20的slab,每chunk大小为1MB,就是说id为20的slab 里只有一个chunk:

CODE:
void slabs_init(size_t limit) {
int i;
int size=1;mem_limit = limit;
for(i=0; i<=POWER_LARGEST; i++, size*=2) {
slabclass[i].size = size;
slabclass[i].perslab = POWER_BLOCK / size;
slabclass[i].slots = 0;
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
slabclass[i].end_page_ptr = 0;
slabclass[i].end_page_free = 0;
slabclass[i].slab_list = 0;
slabclass[i].list_size = 0;
slabclass[i].killing = 0;
}

/* for the test suite:  faking of how much we’ve already malloc’d */
{
char *t_initial_malloc = getenv(”T_MEMD_INITIAL_MALLOC”);
if (t_initial_malloc) {
mem_malloced = atol(getenv(”T_MEMD_INITIAL_MALLOC”));
}
}

/* pre-allocate slabs by default, unless the environment variable
for testing is set to something non-zero */
{
char *pre_alloc = getenv(”T_MEMD_SLABS_ALLOC”);
if (!pre_alloc || atoi(pre_alloc)) {
slabs_preallocate(limit / POWER_BLOCK);
}
}
}

在1.2中,chunk大小表示为初始大小*f^n,f为factor,在memcached.c中定义,n为classid,同时,201个头不 是全部都要初始化的,因为factor可变,初始化只循环到计算出的大小达到slab大小的一半为止,而且它是从id1开始的,即:id为1的slab, 每chunk大小80字节,id为2的slab,每chunk大小80*f,id为3的slab,每chunk大小80*f^2,初始化大小有一个修正值 CHUNK_ALIGN_BYTES,用来保证n-byte排列 (保证结果是CHUNK_ALIGN_BYTES的整倍数)。这样,在标准情况下,memcached1.2会初始化到id40,这个slab中每个 chunk大小为504692,每个slab中有两个chunk。最后,slab_init函数会在最后补足一个id41,它是整块的,也就是这个 slab中只有一个1MB大的chunk:

CODE:
void slabs_init(size_t limit, double factor) {
int i = POWER_SMALLEST – 1;
unsigned int size = sizeof(item) + settings.chunk_size;/* Factor of 2.0 means use the default memcached behavior */
if (factor == 2.0 && size < 128)
size = 128;

mem_limit = limit;
memset(slabclass, 0, sizeof(slabclass));

while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) {
/* Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES – (size % CHUNK_ALIGN_BYTES);

slabclass[i].size = size;
slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;
size *= factor;
if (settings.verbose > 1) {
fprintf(stderr, “slab class %3d: chunk size %6d perslab %5d\n”,
i, slabclass[i].size, slabclass[i].perslab);
}
}

power_largest = i;
slabclass[power_largest].size = POWER_BLOCK;
slabclass[power_largest].perslab = 1;

/* for the test suite:  faking of how much we’ve already malloc’d */
{
char *t_initial_malloc = getenv(”T_MEMD_INITIAL_MALLOC”);
if (t_initial_malloc) {
mem_malloced = atol(getenv(”T_MEMD_INITIAL_MALLOC”));
}

}

#ifndef DONT_PREALLOC_SLABS
{
char *pre_alloc = getenv(”T_MEMD_SLABS_ALLOC”);
if (!pre_alloc || atoi(pre_alloc)) {
slabs_preallocate(limit / POWER_BLOCK);
}
}
#endif
}

由上可以看出,memcached的内存分配是有冗余的,当一个slab不能被它所拥有的chunk大小整除时,slab尾部剩余的空间就被丢弃 了,如id40中,两个chunk占用了1009384字节,这个slab一共有1MB,那么就有39192字节被浪费了。

Memcached使用这种方式来分配内存,是为了可以快速的通过item长度定位出slab的classid,有一点类似hash,因为item 的长度是可以计算的,比如一个item的长度是300字节,在1.2中就可以得到它应该保存在id7的slab中,因为按照上面的计算方法,id6的 chunk大小是252字节,id7的chunk大小是316字节,id8的chunk大小是396字节,表示所有252到316字节的item都应该保 存在id7中。同理,在1.1中,也可以计算得到它出于256和512之间,应该放在chunk_size为512的id9中(32位系统)。

Memcached初始化的时候,会初始化slab(前面可以看到,在main函数中调用了slabs_init())。它会在 slabs_init()中检查一个常量DONT_PREALLOC_SLABS,如果这个没有被定义,说明使用预分配内存方式初始化slab,这样在所 有已经定义过的slabclass中,每一个id创建一个slab。这样就表示,1.2在默认的环境中启动进程后要分配41MB的slab空间,在这个过 程里,memcached的第二个内存冗余发生了,因为有可能一个id根本没有被使用过,但是它也默认申请了一个slab,每个slab会用掉1MB内存

当一个slab用光后,又有新的item要插入这个id,那么它就会重新申请新的slab,申请新的slab时,对应id的slab链表就要增长, 这个链表是成倍增长的,在函数grow_slab_list函数中,这个链的长度从1变成2,从2变成4,从4变成8……:

CODE:
static int grow_slab_list (unsigned int id) {
slabclass_t *p = &slabclass[id];
if (p->slabs == p->list_size) {
size_t new_size =  p->list_size ? p->list_size * 2 : 16;
void *new_list = realloc(p->slab_list, new_size*sizeof(void*));
if (new_list == 0) return 0;
p->list_size = new_size;
p->slab_list = new_list;
}
return 1;
}

在定位item时,都是使用slabs_clsid函数,传入参数为item大小,返回值为classid,由这个过程可以看 出,memcached的第三个内存冗余发生在保存item的过程中,item总是小于或等于chunk大小的,当item小于chunk大小时,就又发 生了空间浪费。

◎Memcached的NewHash算法

Memcached的item保存基于一个大的hash表,它的实际地址就是slab中的chunk偏移,但是它的定位是依靠对key做hash的 结果,在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。

Memcached使用了一个叫做NewHash的算法,它的效果很好,效率也很高。1.1和1.2的NewHash有一些不同,主要的实现方式还 是一样的,1.2的hash函数是经过整理优化的,适应性更好一些。

NewHash的原型参考:http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪, 呵呵~

为了变换方便,定义了u4和u1两种数据类型,u4就是无符号的长整形,u1就是无符号char(0-255)。

具体代码可以参考1.1和1.2源码包。

注意这里的hashtable长度,1.1和1.2也是有区别的,1.1中定义了HASHPOWER常量为20,hashtable表长为 hashsize(HASHPOWER),就是4MB(hashsize是一个宏,表示1右移n位),1.2中是变量16,即hashtable表长 65536:

CODE:
typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)

在assoc_init()中,会对primary_hashtable做初始化,对应的hash操作包括:assoc_find()、 assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete(),对应 于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数(注意在C中,很多时候都是同时直接传入字符串 和字符串长度,而不是在函数内部做strlen),返回的是item结构指针,它的数据地址在slab中的某个chunk上。

items.c是数据项的操作程序,每一个完整的item包括几个部分,在item_make_header()中定义为:

key:键
nkey:键长
flags:用户定义的flag(其实这个flag在memcached中没有启用)
nbytes:值长(包括换行符号\r\n)
suffix:后缀Buffer
nsuffix:后缀长

一个完整的item长度是键长+值长+后缀长+item结构大小(32字节),item操作就是根据这个长度来计算slab的classid的。

hashtable中的每一个桶上挂着一个双链表,item_init()的时候已经初始化了heads、tails、sizes三个数组为0,这 三个数组的大小都为常量LARGEST_ID(默认为255,这个值需要配合factor来修改),在每次item_assoc()的时候,它会首先尝试 从slab中获取一块空闲的chunk,如果没有可用的chunk,会在链表中扫描50次,以得到一个被LRU踢掉的item,将它unlink,然后将 需要插入的item插入链表中。

注意item的refcount成员。item被unlink之后只是从链表上摘掉,不是立刻就被free的,只是将它放到删除队列中 (item_unlink_q()函数)。

item对应一些读写操作,包括remove、update、replace,当然最重要的就是alloc操作。

item还有一个特性就是它有过期时间,这是memcached的一个很有用的特性,很多应用都是依赖于memcached的item过期,比如 session存储、操作锁等。item_flush_expired()函数就是扫描表中的item,对过期的item执行unlink操作,当然这只 是一个回收动作,实际上在get的时候还要进行时间判断:

CODE:
/* expires items that are more recent than the oldest_live setting. */
void item_flush_expired() {
int i;
item *iter, *next;
if (! settings.oldest_live)
return;
for (i = 0; i < LARGEST_ID; i++) {
/* The LRU is sorted in decreasing time order, and an item’s timestamp
* is never newer than its last access time, so we only need to walk
* back until we hit an item older than the oldest_live time.
* The oldest_live checking will auto-expire the remaining items.
*/
for (iter = heads[i]; iter != NULL; iter = next) {
if (iter->time >= settings.oldest_live) {
next = iter->next;
if ((iter->it_flags & ITEM_SLABBED) == 0) {
item_unlink(iter);
}
} else {
/* We’ve hit the first old item. Continue to the next queue. */
break;
}
}
}
}
CODE:
/* wrapper around assoc_find which does the lazy expiration/deletion logic */
item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) {
item *it = assoc_find(key, nkey);
if (delete_locked) *delete_locked = 0;
if (it && (it->it_flags & ITEM_DELETED)) {
/* it’s flagged as delete-locked.  let’s see if that condition
is past due, and the 5-second delete_timer just hasn’t
gotten to it yet… */
if (! item_delete_lock_over(it)) {
if (delete_locked) *delete_locked = 1;
it = 0;
}
}
if (it && settings.oldest_live && settings.oldest_live <= current_time &&
it->time <= settings.oldest_live) {
item_unlink(it);
it = 0;
}
if (it && it->exptime && it->exptime <= current_time) {
item_unlink(it);
it = 0;
}
return it;
}

Memcached的内存管理方式是非常精巧和高效的,它很大程度上减少了直接alloc系统内存的次数,降低函数开销和内存碎片产生几率,虽然这 种方式会造成一些冗余浪费,但是这种浪费在大型系统应用中是微不足道的。

◎Memcached的理论参数计算方式

影响 memcached 工作的几个参数有:

常量REALTIME_MAXDELTA 60*60*24*30
最大30天的过期时间

conn_init()中的freetotal(=200)
最大同时连接数

常量KEY_MAX_LENGTH 250
最大键长

settings.factor(=1.25)
factor将影响chunk的步进大小

settings.maxconns(=1024)
最大软连接

settings.chunk_size(=48)
一个保守估计的key+value长度,用来生成id1中的chunk长度(1.2)。id1的chunk长度等于这个数值加上item结构体的长度 (32),即默认的80字节。

常量POWER_SMALLEST 1
最小classid(1.2)

常量POWER_LARGEST 200
最大classid(1.2)

常量POWER_BLOCK 1048576
默认slab大小

常量CHUNK_ALIGN_BYTES (sizeof(void *))
保证chunk大小是这个数值的整数倍,防止越界(void *的长度在不同系统上不一样,在标准32位系统上是4)

常量ITEM_UPDATE_INTERVAL 60
队列刷新间隔

常量LARGEST_ID 255
最大item链表数(这个值不能比最大的classid小)

变量hashpower(在1.1中是常量HASHPOWER)
决定hashtable的大小

根据上面介绍的内容及参数设定,可以计算出的一些结果:

1、在memcached中可以保存的item个数是没有软件上限的,之前我的100万的说法是错误的。
2、假设NewHash算法碰撞均匀,查找item的循环次数是item总数除以hashtable大小(由hashpower决定),是线性的。
3、Memcached限制了可以接受的最大item是1MB,大于1MB的数据不予理会。
4、Memcached的空间利用率和数据特性有很大的关系,又与DONT_PREALLOC_SLABS常量有关。 在最差情况下,有198个slab会被浪费(所有item都集中在一个slab中,199个id全部分配满)。

◎Memcached的定长优化

根据上面几节的描述,多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。

Memcached本身是为变长数据设计的,根据数据特性,可以说它是“面向大众”的设计,但是很多时候,我们的数据并不是这样的“普遍”,典型的 情况中,一种是非均匀分布,即数据长度集中在几个区域内(如保存用户 Session);另一种更极端的状态是等长数据(如定长键值,定长数据,多见于访问、在线统计或执行锁)。

这里主要研究一下定长数据的优化方案(1.2),集中分布的变长数据仅供参考,实现起来也很容易。

解决定长数据,首先需要解决的是slab的分配问题,第一个需要确认的是我们不需要那么多不同chunk长度的slab,为了最大限度地利用资源, 最好chunk和item等长,所以首先要计算item长度。

在之前已经有了计算item长度的算法,需要注意的是,除了字符串长度外,还要加上item结构的长度32字节。

假设我们已经计算出需要保存200字节的等长数据。

接下来是要修改slab的classid和chunk长度的关系。在原始版本中,chunk长度和classid是有对应关系的,现在如果把所有的 chunk都定为200个字节,那么这个关系就不存在了,我们需要重新确定这二者的关系。一种方法是,整个存储结构只使用一个固定的id,即只使用199 个槽中的1个,在这种条件下,就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系,来从 item确定classid,不能使用长度来做键,可以使用key的NewHash结果等不定数据,或者直接根据key来做hash(定长数据的key也 一定等长)。这里简单起见,选择第一种方法,这种方法的不足之处在于只使用一个id,在数据量非常大的情况下,slab链会很长(因为所有数据都挤在一条 链上了),遍历起来的代价比较高。

前面介绍了三种空间冗余,设置chunk长度等于item长度,解决了第一种空间浪费问题,不预申请空间解决了第二种空间浪费问题,那么对于第一种 问题(slab内剩余)如何解决呢,这就需要修改POWER_BLOCK常量,使得每一个slab大小正好等于chunk长度的整数倍,这样一个slab 就可以正好划分成n个chunk。这个数值应该比较接近1MB,过大的话同样会造成冗余,过小的话会造成次数过多的alloc,根据chunk长度为 200,选择1000000作为POWER_BLOCK的值,这样一个slab就是100万字节,不是1048576。三个冗余问题都解决了,空间利用率 会大大提升。

修改 slabs_clsid 函数,让它直接返回一个定值(比如 1 ):

CODE:
unsigned int slabs_clsid(size_t size) {
return 1;
}

修改slabs_init函数,去掉循环创建所有classid属性的部分,直接添加slabclass[1]:

CODE:
slabclass[1].size = 200;                //每chunk200字节
slabclass[1].perslab = 5000;        //1000000/200

◎Memcached客户端

Memcached是一个服务程序,使用的时候可以根据它的协议,连接到memcached服务器上,发送命令给服务进程,就可以操作上面的数据。 为了方便使用,memcached有很多个客户端程序可以使用,对应于各种语言,有各种语言的客户端。基于C语言的有libmemcache、 APR_Memcache;基于Perl的有Cache::Memcached;另外还有Python、Ruby、Java、C#等语言的支持。PHP的 客户端是最多的,不光有mcache和PECL memcache两个扩展,还有大把的由PHP编写的封装类,下面介绍一下在PHP中使用memcached的方法:

mcache扩展是基于libmemcache再封装的。libmemcache一直没有发布stable版本,目前版本是1.4.0-rc2,可 以在这里找到。libmemcache有一个很不好的特性,就是会向stderr写很多错误信息,一般的,作为lib使用的时候,stderr一般都会被 定向到其它地方,比如Apache的错误日志,而且libmemcache会自杀,可能会导致异常,不过它的性能还是很好的。

mcache扩展最后更新到1.2.0-beta10,作者大概是离职了,不光停止更新,连网站也打不开了(~_~),只能到其它地方去获取这个不 负责的扩展了。解压后安装方法如常:phpize & configure & make & make install,一定要先安装libmemcache。使用这个扩展很简单:

CODE:
<?php
$mc = memcache();    // 创 建一个memcache连接对象,注意这里不是用new!
$mc->add_server(‘localhost’, 11211);    // 添 加一个服务进程
$mc->add_server(‘localhost’, 11212);    // 添 加第二个服务进程
$mc->set(‘key1′, ‘Hello’);    // 写 入key1 => Hello
$mc->set(‘key2′, ‘World’, 10);    // 写 入key2 => World,10秒过期
$mc->set(‘arr1′, array(‘Hello’, ‘World’));    // 写 入一个数组
$key1 = $mc->get(‘key1′);    // 获 取’key1′的值,赋给$key1
$key2 = $mc->get(‘key2′);    // 获 取’key2′的值,赋给$key2,如果超过10秒,就取不到了
$arr1 = $mc->get(‘arr1′);    // 获 取’arr1′数组
$mc->delete(‘arr1′);    // 删 除’arr1′
$mc->flush_all();    // 删 掉所有数据
$stats = $mc->stats();    // 获 取服务器信息
var_dump($stats);    // 服 务器信息是一个数组
?>

这个扩展的好处是可以很方便地实现分布式存储和负载均衡,因为它可以添加多个服务地址,数据在保存的时候是会根据hash结果定位到某台服务器上 的,这也是libmemcache的特性。libmemcache支持集中hash方式,包括CRC32、ELF和Perl hash。

PECL memcache是PECL发布的扩展,目前最新版本是2.1.0,可以在pecl网站得到。memcache扩展的使用方法可以在新一些的PHP手册中 找到,它和mcache很像,真的很像:

CODE:
<?php$memcache

= new Memcache;
$memcache->connect(‘localhost’, 11211) or die (“Could not connect”);

$version = $memcache->getVersion();
echo “Server’s version: ”.$version.“n”;

$tmp_object = new stdClass;
$tmp_object->str_attr = ‘test’;
$tmp_object->int_attr = 123;

$memcache->set(‘key’, $tmp_object, false, 10) or die (“Failed to save data at the server”);
echo “Store data in the cache (data will expire in 10 seconds)n”;

$get_result = $memcache->get(‘key’);
echo “Data from the cache:n”;

var_dump($get_result);

?>

这个扩展是使用php的stream直接连接memcached服务器并通过socket发送命令的。它不像libmemcache那样完善,也不 支持add_server这种分布操作,但是因为它不依赖其它的外界程序,兼容性要好一些,也比较稳定。至于效率,差别不是很大。

另外,有很多的PHP class可以使用,比如MemcacheClient.inc.php,phpclasses.org上可以找到很多,一般都是对perl client API的再封装,使用方式很像。

◎BSM_Memcache

从C client来说,APR_Memcache是一个很成熟很稳定的client程序,支持线程锁和原子级操作,保证运行的稳定性。不过它是基于APR的 (APR将在最后一节介绍),没有libmemcache的应用范围广,目前也没有很多基于它开发的程序,现有的多是一些Apache Module,因为它不能脱离APR环境运行。但是APR倒是可以脱离Apache单独安装的,在APR网站上可以下载APR和APR-util,不需要 有Apache,可以直接安装,而且它是跨平台的。

BSM_Memcache是我在BS.Magic项目中开发的一个基于APR_Memcache的PHP扩展,说起来有点拗口,至少它把APR扯进 了PHP扩展中。这个程序很简单,也没做太多的功能,只是一种形式的尝试,它支持服务器分组。

和mcache扩展支持多服务器分布存储不同,BSM_Memcache支持多组服务器,每一组内的服务器还是按照hash方式来分布保存数据,但 是两个组中保存的数据是一样的,也就是实现了热备,它不会因为一台服务器发生单点故障导致数据无法获取,除非所有的服务器组都损坏(例如机房停电)。当然 实现这个功能的代价就是性能上的牺牲,在每次添加删除数据的时候都要扫描所有的组,在get数据的时候会随机选择一组服务器开始轮询,一直到找到数据为 止,正常情况下一次就可以获取得到。

BSM_Memcache只支持这几个函数:

CODE:
zend_function_entry bsm_memcache_functions[] =
{
PHP_FE(mc_get,          NULL)
PHP_FE(mc_set,          NULL)
PHP_FE(mc_del,          NULL)
PHP_FE(mc_add_group,    NULL)
PHP_FE(mc_add_server,   NULL)
PHP_FE(mc_shutdown,     NULL)
{NULL, NULL, NULL}
};

mc_add_group函数返回一个整形(其实应该是一个object,我偷懒了~_~)作为组ID,mc_add_server的时候要提供两 个参数,一个是组ID,一个是服务器地址(ADDRORT)。

CODE:
/**
* Add a server group
*/
PHP_FUNCTION(mc_add_group)
{
apr_int32_t group_id;
apr_status_t rv;if (0 != ZEND_NUM_ARGS())
{
WRONG_PARAM_COUNT;
RETURN_NULL();
}

group_id = free_group_id();
if (-1 == group_id)
{
RETURN_FALSE;
}

apr_memcache_t *mc;
rv = apr_memcache_create(p, MAX_G_SERVER, 0, &mc);

add_group(group_id, mc);

RETURN_DOUBLE(group_id);
}

CODE:
/**
* Add a server into group
*/
PHP_FUNCTION(mc_add_server)
{
apr_status_t rv;
apr_int32_t group_id;
double g;
char *srv_str;
int srv_str_l;if (2 != ZEND_NUM_ARGS())
{
WRONG_PARAM_COUNT;
}

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ds”, &g, &srv_str, &srv_str_l) == FAILURE)
{
RETURN_FALSE;
}

group_id = (apr_int32_t) g;

if (-1 == is_validate_group(group_id))
{
RETURN_FALSE;
}

char *host, *scope;
apr_port_t port;

rv = apr_parse_addr_port(&host, &scope, &port, srv_str, p);
if (APR_SUCCESS == rv)
{
// Create this server object
apr_memcache_server_t *st;
rv = apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, &st);
if (APR_SUCCESS == rv)
{
if (NULL == mc_groups[group_id])
{
RETURN_FALSE;
}

// Add server
rv = apr_memcache_add_server(mc_groups[group_id], st);

if (APR_SUCCESS == rv)
{
RETURN_TRUE;
}
}
}

RETURN_FALSE;
}

在set和del数据的时候,要循环所有的组:

CODE:
/**
* Store item into all groups
*/
PHP_FUNCTION(mc_set)
{
char *key, *value;
int key_l, value_l;
double ttl = 0;
double set_ct = 0;if (2 != ZEND_NUM_ARGS())
{
WRONG_PARAM_COUNT;
}

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ss|d”, &key, &key_l, &value, &value_l, ttl) == FAILURE)
{
RETURN_FALSE;
}

// Write data into every object
apr_int32_t i = 0;
if (ttl < 0)
{
ttl = 0;
}

apr_status_t rv;

for (i = 0; i < MAX_GROUP; i++)
{
if (0 == is_validate_group(i))
{
// Write it!
rv = apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0);
if (APR_SUCCESS == rv)
{
set_ct++;
}
}
}

RETURN_DOUBLE(set_ct);
}

在mc_get中,首先要随机选择一个组,然后从这个组开始轮询:

CODE:
/**
* Fetch a item from a random group
*/
PHP_FUNCTION(mc_get)
{
char *key, *value = NULL;
int key_l;
apr_size_t value_l;if (1 != ZEND_NUM_ARGS())
{
WRONG_PARAM_COUNT;
}

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &key, &key_l) == FAILURE)
{
RETURN_MULL();
}

// I will try …
// Random read
apr_int32_t curr_group_id = random_group();
apr_int32_t i = 0;
apr_int32_t try = 0;
apr_uint32_t flag;
apr_memcache_t *oper;
apr_status_t rv;

for (i = 0; i < MAX_GROUP; i++)
{
try = i + curr_group_id;
try = try % MAX_GROUP;
if (0 == is_validate_group(try))
{
// Get a value
oper = mc_groups[try];
rv = apr_memcache_getp(mc_groups[try], p, (const char *) key, &value, &value_l, 0);
if (APR_SUCCESS == rv)
{
RETURN_STRING(value, 1);
}
}
}

RETURN_FALSE;
}

CODE:
/**
* Random group id
* For mc_get()
*/
apr_int32_t random_group()
{
struct timeval tv;
struct timezone tz;
int usec;gettimeofday(&tv, &tz);

usec = tv.tv_usec;

int curr = usec % count_group();

return (apr_int32_t) curr;
}

BSM_Memcache的使用方式和其它的client类似:

CODE:
<?php
$g1 = mc_add_group();    // 添 加第一个组
$g2 = mc_add_group();    // 添 加第二个组
mc_add_server($g1, ‘localhost:11211′);    // 在 第一个组中添加第一台服务器
mc_add_server($g1, ‘localhost:11212′);    // 在 第一个组中添加第二台服务器
mc_add_server($g2, ‘10.0.0.16:11211′);    // 在 第二个组中添加第一台服务器
mc_add_server($g2, ‘10.0.0.17:11211′);    // 在 第二个组中添加第二台服务器

mc_set(‘key’, ‘Hello’);    // 写 入数据
$key = mc_get(‘key’);    // 读 出数据
mc_del(‘key’);    // 删 除数据
mc_shutdown();    // 关闭所有组
?>

APR_Memcache的相关资料可以在这里找到,BSM_Memcache可以在本站下载。

◎APR环境介绍

APR的全称:Apache Portable Runtime。它是Apache软件基金会创建并维持的一套跨平台的C语言库。它从Apache httpd1.x中抽取出来并独立于httpd之外,Apache httpd2.x就是建立在APR上。APR提供了很多方便的API接口可供使用,包括如内存池、字符串操作、网络、数组、hash表等实用的功能。开发 Apache2 Module要接触很多APR函数,当然APR可以独立安装独立使用,可以用来写自己的应用程序,不一定是Apache httpd的相关开发。

◎后记

这是我在农历丙戌年(我的本命年)的最后一篇文章,由于Memcached的内涵很多,仓促整理一定有很多遗漏和错误。感谢新浪网提供的研究机会, 感谢部门同事的帮助。

NP博士 02-13-2007

原文发表于:http://www.54np.com/
转载请注明

XBMC 显示中文 去除方块

八月 15th, 2010

从你的windows/font目录下随便找个中文字体,Copy到 C:\Program Files\XBMC\media\Fonts。然后改名替换掉其中的一个:
arial.ttf
msyh.ttf
teletext.ttf
在外观界面设置中指定这个字体,如此中文就可以显示了。

XBMC TMDB Scraper 清除中文干扰-干净的削刮器

八月 15th, 2010

TMDB - The Movie DB 在对付中国式电影名是力不从心,比如[中文译名].English Name.mkv。

原动力论坛的诸多帖子对9.11版根本无效。究其原因,advancedsettings.xml中的clearstrings字段不知为何失效。无论怎么配置都无法起到基本作用。

于是直奔主题,探索 C:\Program Files\XBMC\system\scrapers\video\tmdb.xml。

在打开Debug探查之后发现,当原始文件名为

[信使].The.Messenger.LIMITED.720p.Bluray.x264-CBGB.mkv

经过净化clearstings之后变成

[信使] The Messenger.mkv

前面的[信使]被当作电影名发给了TMDB,在tmdb.xml中得到的是编码之后的

%5b%e4%bf%a1%e4%bd%bf%5d%20the%20messenger

接下来就简单了,去除中文干扰,红色的是修改添加的部分,灰色的是去除的原始内容。

<CreateSearchUrl dest="3">
<RegExp input="$$1" output="&lt;url&gt;http://api.themoviedb.org/2.1/Movie.search/en/xml/57983e31fb435df4df77afb854740ea9/\2&lt;/url&gt;" dest="3">
<RegExp input="$$2" output="%20(\1)" dest="4">
<expression clear="yes">(.+)</expression>
</RegExp>
<expression clear="no" noclean="1">(^%5b.*%5d)*(.+)</expression>
<!-- expression noclean="1"/ -->
</RegExp>
</CreateSearchUrl>

这个方法仅对进口电影有效,如果您手里都是土鳖电影,还是直接连中文电影库吧。