没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Memcached 使用点滴总结分享
我对于 Memcached 的接触,还是在去年看了 CSDN 的一系列国外大型网站架构设计而开始的。最初
的时候只是简单的封装了 Memcached Java 版的客户端,主要是对于配置的简化以及 Memcached 多
点备份作了一些工作,然后就作为 ASF 的组件一部分提供给其他 Team 使用。其实看过 Memcached
Java 客户端代码的人就会了解其实客户端的事情很简单,就是要有一套高性能的 Socket 通信框架以
及对 Memcached 的私有协议实现的接口,自己去做这些事情也是很简单的,不过既然有可以满足自
己需求的开源部分,那么就去实现自己需要的但没有实现的。这里我用的是 Whalin 的客户端版本,
这里为什么还要提出来讲这个,后面会提到。
在对 Java 客户端作了简单封装和扩展以后,由于其他 Team 使用的没有什么特殊需求,也就没
有再去做太多的修改,直到最近自己的服务集成平台需要做服务访问控制,才重新丰富了 Cache 组件,
也就是这个过程中对于 Memcached 的一些特性和小的细节有了一些新的认识。
作为服务集成平台需要对服务有所监控,包括访问频率控制以及访问次数控制。频率控制其实
很类似于硬件方面的频率控制,例如硬件可以对 IP 的高频率访问视为攻击,列入黑名单。而作为服
务的访问,对于服务访问者的控制其实涉及到了业务参数,那么硬件就不是很适合去做这方面的控制,
为此我也考虑了很久,最开始打算在 Apache 上做一个模块控制,但是最后觉得还是放在后面的业务
框架上做这件事情。当然后面我说说的方案可能并不好,但是也算是一种想法。要把频繁的访问数据
记录下来同时分析,那么数据库肯定是不行的,最简单的方式就是采用 Cache,又因为是集群范围内
的控制,那么集中式 Cache 就非 Memcached 莫数了(分布式的 Cache 传播本身损耗太大,集中式 Cache
本来的最大缺点就是单点,但作简单的备份操作就可以基本解决此类问题)。
作为解决这个问题的方法来说只需要实现两部分工作:访问计数器,定时任务。定时任务在我
做日志分析框架的时候都是采用了 Jdk5 的 Concurrent 包里面的 ScheduledExecutorService,这个作
简单的循环任务足够用了,同时也是有很好的多线程异步支持,复杂一点么用 Quartz。计数器就要靠
Memcached 来实现了,本来一般的 Cache 最大的问题就是高并发下的事务保证,如果采用 Get+Set
来完成计数的话,那么高并发下计数器就会出现读写不一致性的问题,幸好 Memcached 提供了计数
累加功能,让这种累加动作能够在服务端一次做好,服务端控制并发写入,保证数据的一致性。
下面就看看以下几个方法:
boolean storeCounter(String key, long count):存储 key 的计数器,值为 count。
long getCounter(String key):获取 key 的计数器,如果不存在返回-1。
long addOrDecr(String key, long decr):计数器值减去 decr,如果计数器不存在,保存 decr 作
为计数器值
long addOrIncr(String key, long inc):计数器值增加 inc,如果计数器不存在,保存 inc 作为计
数器值
long decr(String key, long decr):与 addOrDecr 不同的是在计数器不存在的时候不保存任何值,
返回-1
long incr(String key, long inc) :与 addOrIncr 不同的是在计数器不存在的时候不保存任何值,
返回-1
这里需要说明几点:
storeCounter 和普通的 set 方法不同,如果通过 set 方式置入 key:value 的话,getCounter 等其
他四个方法都认为技术器不存在。所以 Counter 的存储方式是和普通内容存储不同的。
在不同的场景要慎用 addOrXXXX 和 XXXX 的方法,两者还是有比较大的区别的。
计数器没有提供移除特殊方法,使用 delete 方法可以移除计数器,但是频繁的 delete 和
addOrXXXX 有时候会出现一些奇怪的问题(例如同名的计数器就没有办法再次被创建,不过这个还
需要进一步的去研究一下看看)。一般情况下如果计数器的 key 不是很多,同时也会被复用,那么可
以通过置为 0 或者减去已经分析过的数量来复位。
有上面的一套计数器机制就可以很方便的实现 Memcached 的计数功能,但是又一个问题出现了,
如何让定时任务去遍历计数器,分析计数器是否到了阀值,触发创建黑名单记录的工作。早先我同事
希望我能够提供封装好的 keySet 接口,但是我自己觉得其实作为 Cache 来说简单就是最重要的,
Cache 不需要去遍历。首先使用 Cache 的角色就应该知道 Key,然后去 Cache 里面找,找不到就去后
台例如 DB 里面去搜索,然后将搜索的结果在考虑更新到 Cache 里面,这样才是最高效并且最可靠的,
Cache 靠不住阿,随时都可能会丢失或者崩溃,因此作为类似于一级缓存或者这类数据完整性要求不
高,性能要求很高的场景使用最合适。当时就没有提供这样的接口,直到今天自己需要了,才考虑如
何去做这件事情。
开始考虑是否能够将 key 都记录在另外的 Cache 中或者是 Memcached 中,首先在高并发下更
新操作就是一大问题,再者 Memcached 的内存分配回收机制以及 Value 的大小限制都不能满足这样
的需求,如果使用数据库,那么频繁更新操作势必不可行,采用异步缓存刷新又有一个时间间隔期,
同时更新也不是很方便。最后考虑如果能够让 Memcached 实现 Keyset 那么就是最好的解决方案,网
上搜索了一下,找到一种策略,然后自己优化了一下,优化后的代码如下:
@SuppressWarnings("unchecked")
public Set keySet(int limit,boolean fast)
{
Set<String> keys = new HashSet<String>();
Map<String,Integer> dumps = new HashMap<String,Integer>();
Map slabs = getCacheClient().statsItems();
if (slabs != null && slabs.keySet() != null)
{
Iterator itemsItr = slabs.keySet().iterator();
while(itemsItr.hasNext())
{
String server = itemsItr.next().toString();
Map itemNames = (Map) slabs.get(server);
Iterator itemNameItr = itemNames.keySet().iterator();
while(itemNameItr.hasNext())
{
String itemName = itemNameItr.next().toString();
// itemAtt[0] = itemname
// itemAtt[1] = number
// itemAtt[2] = field
String[] itemAtt = itemName.split(":");
if (itemAtt[2].startsWith("number"))
dumps.put(itemAtt[1], Integer.parseInt(itemAtt[1]));
}
}
剩余15页未读,继续阅读
资源评论
小小哭包
- 粉丝: 1899
- 资源: 3854
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功