memcached完全剖析ehcache memcached redis 缓存技术总结
redis 学习问题总结 |
http://aperise.iteye.com/blog/2310639 |
ehcache memcached redis 缓存技术总结 |
http://aperise.iteye.com/blog/2296219 |
redis-stat 离线安装 |
http://aperise.iteye.com/blog/2310254 |
redis cluster 非ruby方式启动 |
http://aperise.iteye.com/blog/2310254 |
redis-sentinel安装部署 |
http://aperise.iteye.com/blog/2342693 |
spring-data-redis使用 |
http://aperise.iteye.com/blog/2342615 |
redis客户端redisson实战 |
http://blog.csdn.net/zilong_zilong/article/details/78252037 |
redisson-2.10.4源代码分析 |
http://blog.csdn.net/zilong_zilong/article/details/78609423 |
tcmalloc jemalloc libc选择 |
http://blog.csdn.net/u010994304/article/details/49906819 |
1.前言ehcache memcached redis 缓存技术总结
1.1 工作中最初的缓存实现
工作中第一次接触缓存技术,是在一个java web程序中做权限管理功能。当时权限框架选择的是springsecurity,springsecurity提供了需要实现的接口调取用户关于URL相关权限数据,当时权限数据全部存放于数据库中,为了降低对数据库的访问,在web启动时候,就将存放于数据库中所有权限数据加载到内存中,做法如下:
//用静态变量Map存储用户权限数据 //Map中key为用户账号,value为用户权限对象 static Map<String,Object> rightsMap = new HashMap<String,Object>(); //web中配置一个servlet listener,web启动后马上加载数据到静态变量rightsMap //权限管理功能中增加一个通知接口,方便在修改权限后,更新静态变量rightsMap //springsecurity接口访问静态变量rightsMap拿取权限数据
上面可能是当时对一些缓存框架不太了解情况下很多开发者的普遍思路了。
1.2 hibernate中接触到的ehcache
EhCache是工作中接触到的第二个缓存技术,接触到它主要是因为工作中使用到了Hibernate,而EhCache是Hibernate中默认的CacheProvider,EhCache 是一个纯Java的进程内缓存框架。
1.3 后来接触的memcached和redis
后来接触的项目越来越多,陆续接触到了memcached和redis,这里将这些用到过的缓存自己做个总结,以便温故知新。
2.Ehcache
2.1 Ehcache初识
1)EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
2)Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
3)Ehcache最初是由Greg Luck于2003年开始开发。2009年,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如Enterprise EHCache and BigMemory。,维基媒体Foundationannounced目前使用的就是Ehcache技术。
4)Ehcache详见如下博客:
来源 | 作者 | 标题 | 网址 |
iteye | RayChase | Ehcache详细解读 | http://raychase.iteye.com/blog/1545906 |
cnblogs | hoojo | 整合Spring 使用页面、对象缓存 | http://www.cnblogs.com/hoojo/archive/2012/07/12/2587556.html |
官网 | http://www.ehcache.org/ |
2.2 Ehcache使用
这里以官网http://www.ehcache.org/documentation/3.0/getting-started.html 例子进行讲解
1)eclipse中新建maven普通工程,pom.xml配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>ehcachetest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ehcachetest</name> <packaging>jar</packaging> <properties> <!-- 这里定义所有的版本信息,方便版本统一管理 --> <ehcache.version>3.0.1</ehcache.version> </properties> <dependencies> <!-- 這裡添加所有的依賴包 --> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> </dependencies> <build> <plugins> <!-- 這裡添加所有插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.4.3</version> <configuration> <!-- specify UTF-8, ISO-8859-1 or any other file encoding --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>2)eclipse中windows-Preferences-Maven- Download Artifact Sources勾选上方便后续查看依赖包源代码
3)直接调用java API管理缓存cache
- 通过
CacheManagerBuilder
的静态方法newCacheManagerBuilder
实例化CacheManager对象 - 通过
CacheConfigurationBuilder
的静态方法newCacheManagerBuilder
来创建缓存Cache
的配置CacheConfiguration
,然后通过CacheManager的静态方法withCache
注册配置 - 通过CacheManager的静态方法getCache获取
Cache
对象 - 拿到
Cache
对象后,可调用put设置缓存值key-value,可调用get通过可以获取value - 通过CacheManager的静态方法removeCache获取删除Cache对象
- 最后必须调用CacheManager的静态方法close关闭CacheManager,以便释放资源
package com.ehcache.test; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; public class ManagedCacheTest { public static void main(String[] args) { CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() // 通过CacheManagerBuilder 的静态方法newCacheManagerBuilder 实例化CacheManager对象 .withCache( "preConfigured", //通过CacheConfigurationBuilder 的静态方法newCacheManagerBuilder 来创建缓存Cache 的配置CacheConfiguration , //然后通过CacheManager的静态方法withCache 注册配置 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))).build(); cacheManager.init(); //通过CacheManager的静态方法getCache获取Cache 对象 Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class); //通过CacheManager的方法createCache创建另一个不同配置Cache对象 Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder .newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)).build()); //拿到Cache 对象后,可调用put设置缓存值key-value,可调用get通过可以获取value //通过CacheManager的静态方法removeCache获取删除Cache对象 myCache.put(1L, "da one!"); String value = myCache.get(1L); System.out.println("value=" + value); cacheManager.removeCache("preConfigured"); //最后可通过CacheManager的静态方法close关闭CacheManager cacheManager.close(); } }4)Ehcache3中引入的用户管理cache
- UserManagedCache是EhCACHE3中的新特性,UserManagedCache不受
CacheManager
管理 - 可以在build时候传入false先不初始化UserManagedCache
- 最后必须调用UserManagedCache.close()来释放资源
package com.ehcache.test; import org.ehcache.UserManagedCache; import org.ehcache.config.builders.UserManagedCacheBuilder; public class UserManagedCacheTest { public static void main(String[] args) { //UserManagedCache是EhCACHE3中的新特性,UserManagedCache不受CacheManager 管理 UserManagedCache<Long, String> userManagedCache = UserManagedCacheBuilder.newUserManagedCacheBuilder( Long.class, String.class).build(false);//可以在build时候传入false先不初始化UserManagedCache userManagedCache.init(); userManagedCache.put(1L, "da one!"); //最后必须调用UserManagedCache.close()来释放资源 userManagedCache.close(); } }
5)EhCache中的存储分层
EhCache中数据默认是存储在内存(堆存储)中的,但内存(堆存储)这种资源是非常宝贵的,为了高效利用资源,尽量使得频繁使用的数据放置于内存(堆存储)中,那些使用频率低的数据可以放置在非堆存储中,甚至持久化到资源丰富的磁盘上。
非堆存储Off-heap
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("tieredCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(10, MemoryUnit.MB)) ) .build(true); cacheManager.close();注意:上面的例子分配了一个只允许10个缓存大小的非堆内存;
使用非堆内存时候,你必须自己序列化对象和反序列化对象,这样速度肯定比堆内存慢;
使用非堆内存时候,一个优势是不必担心有类似堆内存存储时候的垃圾回收GC影响;
使用非堆内存时候,一定不要忘记了要去设置-XX:MaxDirectMemorySize
磁盘存储Disk persistence
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(getStoragePath() + File.separator + "myData")) .withCache("persistent-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .disk(10, MemoryUnit.MB, true)) ) .build(true); persistentCacheManager.close();注意:使用磁盘存储,需要通过CacheManagerBuilder.persistence(String)设置存储位置;
使用磁盘存储,你必须自己去序列化和反序列化存储对象,速度上肯定是慢与非堆存储,更慢于堆存储;
使用磁盘存储,最大的优势是,在Ehcache关闭后,缓存数据是可以恢复的
三种存储混合使用Three tiers
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(getStoragePath() + File.separator + "myData")) .withCache("threeTieredCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB) ) ).build(true); persistentCacheManager.close();注意:上面例子中混合使用三种存储;
其中内存heap中单位是10个,允许最多10个key-value值,单位也可以是KB MB等;
其中offheap容量为1MB ;
其中disk容量为20MB,注意这里仍然要通过CacheManagerBuilder.persistence(String)设置存储位置
关于资源池更新Update ResourcePools
ResourcePools pools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(20L, EntryUnit.ENTRIES).build(); cache.getRuntimeConfiguration().updateResourcePools(pools); assertThat(cache.getRuntimeConfiguration().getResourcePools() .getPoolForResource(ResourceType.Core.HEAP).getSize(), is(20L));注意:这里意思是可以在线更新线上的已经存在的EhCache缓存配置信息
关于缓存数据有效时间设置Data freshness
CacheConfiguration<Long, String> cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(100)) .withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.SECONDS))) .build();
注意:这里意思是可以设置缓存数据有效时间
6)使用XML类配置EhCache
<?xml version="1.0" encoding="UTF-8"?> <ehcache name="myehcache"> <!--定义一个cache,别名为foo--> <cache alias="foo"> <!--cache的可以为String类型,value这里没定义,默认为Object类型--> <key-type>java.lang.String</key-type> <resources> <!--heap存储且只允许2000个key-value对象--> <heap unit="entries">2000</heap> <!--offheap存储且其大小最大为500MB--> <offheap unit="MB">500</offheap> </resources> </cache> <!--这里定义一个基本的cache配置模板,其他cache可以继承此配置进行扩展--> <cache-template name="myDefaults"> <key-type>java.lang.Long</key-type> <value-type>java.lang.String</value-type> <heap unit="entries">200</heap> </cache-template> <!--这里定义一个cache名字为bar,其key覆盖模板配置,value和模板配置保持一致--> <cache alias="bar" uses-template="myDefaults"> <key-type>java.lang.Number</key-type> </cache> <!--这里定义一个cache名字为simpleCache,全部采用模板配置--> <cache alias="simpleCache" uses-template="myDefaults" /> </ehcache>
上面的XML定义好后,可以通过如下java API进行调用
final URL myUrl = this.getClass().getResource("/ecache.xml"); Configuration xmlConfig = new XmlConfiguration(myUrl); CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
2.3 EhCache与spring集成
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache/ehcache.xml"/> </bean> <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcacheManager"/> <property name="transactionAware" value="true"/> </bean> </beans>
在controller中注入ehCacheCacheManager后,就可以拿到ehCacheCacheManager对象以获得你配置的cache对象进行操作。
2.4 EhCache在hibernate中的使用
参见http://blog.csdn.net/lpz283929516/article/details/8084664
2.5 EhCache三种缓存算法
- LRU 最近最少使用
- LFU 较少频率使用
- FIFO 先进先出
3.Memcached
3.1memcach和memcached的区别
这里引用杨鑫奇在cnblogs的一篇关于“小白谈memcache和memcached的区别”的博客,这两个是啥呢,博主说的很清楚了,memcache和memcached在php关于memcached客户端中有两个版本:
- memcache是pecl扩展库版本
- memcached是libmemcached版本
3.2memcached完全剖析
来源 | 作者 | 标题 | 网址 |
cnblogs |
作者:长野雅广 (Masahiro Nagano) 翻译:charlee |
memcached的基础 | http://kb.cnblogs.com/page/42731/ |
cnblogs |
作者:长野雅广 (Masahiro Nagano) 翻译:charlee |
理解memcached的内存存储 | http://kb.cnblogs.com/page/42732/ |
cnblogs |
作者:长野雅广 (Masahiro Nagano) 翻译:charlee |
memcached的删除机制和发展方向 | http://kb.cnblogs.com/page/42733/ |
cnblogs |
作者:长野雅广 (Masahiro Nagano) 翻译:charlee |
memcached的分布式算法 | http://kb.cnblogs.com/page/42734/ |
cnblogs |
作者:长野雅广 (Masahiro Nagano) 翻译:charlee |
memcached的应用和兼容程序 | http://kb.cnblogs.com/page/42735/ |
3.2.1 memcached的基础
1) memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。memcached是高性能的分布式内存缓存服务器。 一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、 提高可扩展性。
2) memcached的特征
- 协议简单:memcached的服务器客户端通信并不使用复杂的XML等格式, 而使用简单的基于文本行的协议。因此,通过telnet 也能在memcached上保存数据、取得数据。
- 基于libevent的事件处理:libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能 封装成统一的接口。
- 内置内存存储方式:为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。 由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。
- memcached不互相通信的分布式:memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。 各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢? 这完全取决于客户端的实现。
方法 | 说明 |
add | 仅当存储空间中不存在键相同的数据时才保存 |
replace | 仅当存储空间中存在键相同的数据时才保存 |
set | 与add和replace不同,无论何时都保存(set函数忽视该delete阻塞,照常保存数据) |
get | 获取数据 |
get_multi | 一次取得多条数据 |
delete | 删除数据,它有个独特的功能delete('键', '阻塞时间(秒)')删除第一个参数指定的键的数据。第二个参数指定一个时间值,可以禁止使用同样的键保存新数据。 |
3.2.2 理解memcached的内存存储
1) Slab Allocation机制:整理内存以便重复使用
Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块, 以完全解决内存碎片问题。Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合),如下图:
2) Slab Allocation的主要术语
- Page:分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。
- Chunk:用于缓存记录的内存空间。
- Slab Class:特定大小的chunk的组。
3) 使用Growth Factor进行调优
memcached在启动时指定 Growth Factor因子(通过-f选项), 就可以在某种程度上控制slab之间的差异。默认值为1.25。 但是,在该选项出现之前,这个因子曾经固定为2,称为“powers of 2”策略。
$ memcached -f 2 -vv 下面是启动后的verbose输出: slab class 1: chunk size 128 perslab 8192 slab class 2: chunk size 256 perslab 4096 slab class 3: chunk size 512 perslab 2048 slab class 4: chunk size 1024 perslab 1024 slab class 5: chunk size 2048 perslab 512 slab class 6: chunk size 4096 perslab 256 slab class 7: chunk size 8192 perslab 128 slab class 8: chunk size 16384 perslab 64 slab class 9: chunk size 32768 perslab 32 slab class 10: chunk size 65536 perslab 16 slab class 11: chunk size 131072 perslab 8 slab class 12: chunk size 262144 perslab 4 slab class 13: chunk size 524288 perslab 2 可见,从128字节的组开始,组的大小依次增大为原来的2倍。 这样设置的问题是,slab之间的差别比较大,有些情况下就相当浪费内存。 因此,为尽量减少内存浪费,两年前追加了growth factor这个选项
默认设置(f=1.25)时的输出(篇幅所限,这里只写到第10组): slab class 1: chunk size 88 perslab 11915 slab class 2: chunk size 112 perslab 9362 slab class 3: chunk size 144 perslab 7281 slab class 4: chunk size 184 perslab 5698 slab class 5: chunk size 232 perslab 4519 slab class 6: chunk size 296 perslab 3542 slab class 7: chunk size 376 perslab 2788 slab class 8: chunk size 472 perslab 2221 slab class 9: chunk size 592 perslab 1771 slab class 10: chunk size 744 perslab 1409 可见,组间差距比因子为2时小得多,更适合缓存几百字节的记录。 从上面的输出结果来看,可能会觉得有些计算误差, 这些误差是为了保持字节数的对齐而故意设置的
3.2.3 memcached的删除机制和发展方向
1) 数据不会真正从memcached中消失
memcached不会释放已分配的内存。记录超时后,客户端就无法再看见该记录(invisible,透明), 其存储空间即可重复使用。
memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期。 这种技术被称为lazy(惰性)expiration。因此,memcached不会在过期监视上耗费CPU时间。
2) LRU:从缓存中有效删除数据的原理
memcached会优先使用已超时的记录的空间,但即使如此,也会发生追加新记录时空间不足的情况, 此时就要使用名为 Least Recently Used(LRU)机制来分配空间。 顾名思义,这是删除“最近最少使用”的记录的机制。 因此,当memcached的内存空间不足时(无法从slab class 获取到新的空间时),就从最近未被使用的记录中搜索,并将其空间分配给新的记录。
#有些情况下LRU机制反倒会造成麻烦。memcached启动时通过“-M”参数可以禁止LRU,如下所示: #小写的“-m”选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。 #指定“-M”参数启动后,内存用尽时memcached会返回错误。 $ memcached -M -m 1024
3) memcached的最新发展方向
- 二进制协议的策划和实现:使用二进制协议的理由是它不需要文本协议的解析处理,使得原本高速的memcached的性能更上一层楼, 还能减少文本协议的漏洞
- 外部引擎支持:世界上有许多memcached的派生软件,其理由是希望永久保存数据、实现数据冗余等, 即使牺牲一些性能也在所不惜。我在开发memcached之前,在mixi的研发部也曾经 考虑过重新发明memcached。外部引擎的加载机制能封装memcached的网络功能、事件处理等复杂的处理。 因此,现阶段通过强制手段或重新设计等方式使memcached和存储引擎合作的困难 就会烟消云散,尝试各种引擎就会变得轻而易举了。
3.2.4 memcached的分布式算法
1) memcached的分布式
memcached虽然称为“分布式”缓存服务器,但服务器端并没有“分布式”功能。 服务器端仅包括 第2次、 第3次 前坂介绍的内存存储功能,其实现非常简单。 memcached的分布式,则是完全由客户端程序库实现的。 这种分布式是memcached的最大特点。
2)分布式算法
- 根据服务器台数的余数进行分散:求得键的整数哈希值,再除以服务器台数,根据其余数来选择服务器。余数计算的方法简单,数据的分散性也相当优秀,但也有其缺点。 那就是当添加或移除服务器时,缓存重组的代价相当巨大。 添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器, 从而影响缓存的命中率。
-
- Consistent Hashing:首先求出memcached服务器(节点)的哈希值, 并将其配置到0~2的32次方的圆(continuum)上。 然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。 如果超过2的32次方仍然找不到服务器,就会保存到第一台memcached服务器上。只有在continuum上增加服务器的地点逆时针方向的 第一台服务器上的键会受到影响。
-
3.3 memcached的安装
1)memcached依赖libevent,首先检查libevent是否已经安装
ls -al /usr/local/lib|grep libevent
2)在http://libevent.org/ 下载libevent-2.0.21-stable.tar.gz放置于/opt下
wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
3)解压libevent-2.0.21-stable.tar.gz
cd /opt tar -zxvf libevent-2.0.21-stable.tar.gz
4)安装libevent,这里都是默认安装在目录/usr/local/lib
cd /opt/libevent-2.0.21-stable ./configure make make install
5)再次检查libevent是否已经安装
[root@master libevent-2.0.21-stable]# ls -al /usr/local/lib|grep libevent lrwxrwxrwx 1 root root 21 May 14 17:47 libevent-2.0.so.5 -> libevent-2.0.so.5.1.9 -rwxr-xr-x 1 root root 968442 May 14 17:47 libevent-2.0.so.5.1.9 -rw-r--r-- 1 root root 1571130 May 14 17:47 libevent.a lrwxrwxrwx 1 root root 26 May 14 17:47 libevent_core-2.0.so.5 -> libevent_core-2.0.so.5.1.9 -rwxr-xr-x 1 root root 585057 May 14 17:47 libevent_core-2.0.so.5.1.9 -rw-r--r-- 1 root root 977914 May 14 17:47 libevent_core.a -rwxr-xr-x 1 root root 976 May 14 17:47 libevent_core.la lrwxrwxrwx 1 root root 26 May 14 17:47 libevent_core.so -> libevent_core-2.0.so.5.1.9 lrwxrwxrwx 1 root root 27 May 14 17:47 libevent_extra-2.0.so.5 -> libevent_extra-2.0.so.5.1.9 -rwxr-xr-x 1 root root 404772 May 14 17:47 libevent_extra-2.0.so.5.1.9 -rw-r--r-- 1 root root 593288 May 14 17:47 libevent_extra.a -rwxr-xr-x 1 root root 983 May 14 17:47 libevent_extra.la lrwxrwxrwx 1 root root 27 May 14 17:47 libevent_extra.so -> libevent_extra-2.0.so.5.1.9 -rwxr-xr-x 1 root root 941 May 14 17:47 libevent.la lrwxrwxrwx 1 root root 29 May 14 17:47 libevent_openssl-2.0.so.5 -> libevent_openssl-2.0.so.5.1.9 -rwxr-xr-x 1 root root 94209 May 14 17:47 libevent_openssl-2.0.so.5.1.9 -rw-r--r-- 1 root root 131836 May 14 17:47 libevent_openssl.a -rwxr-xr-x 1 root root 1012 May 14 17:47 libevent_openssl.la lrwxrwxrwx 1 root root 29 May 14 17:47 libevent_openssl.so -> libevent_openssl-2.0.so.5.1.9 lrwxrwxrwx 1 root root 30 May 14 17:47 libevent_pthreads-2.0.so.5 -> libevent_pthreads-2.0.so.5.1.9 -rwxr-xr-x 1 root root 18462 May 14 17:47 libevent_pthreads-2.0.so.5.1.9 -rw-r--r-- 1 root root 18702 May 14 17:47 libevent_pthreads.a -rwxr-xr-x 1 root root 1004 May 14 17:47 libevent_pthreads.la lrwxrwxrwx 1 root root 30 May 14 17:47 libevent_pthreads.so -> libevent_pthreads-2.0.so.5.1.9 lrwxrwxrwx 1 root root 21 May 14 17:47 libevent.so -> libevent-2.0.so.5.1.9 [root@master libevent-2.0.21-stable]#
6)下载memcached-1.4.25.tar.gz并放置于目录/opt下
wget http://memcached.org/files/memcached-1.4.25.tar.gz
7)解压memcached-1.4.25.tar.gz
cd /opt tar -zxvf memcached-1.4.25.tar.gz
8)安装memcached-1.4.25.tar.gz,默认安装在/usr/local/bin/memcached
cd /opt/memcached-1.4.25 ./configure make make test make install
遇到错误:
./sizes ./sizes: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory make: *** [test] Error 127
需要制定libevent安装目录
cd /opt/memcached-1.4.25 ./configure -with-libevent=/usr/local/lib make make test make install
[root@master memcached-1.4.25]# make install make install-recursive make[1]: Entering directory `/opt/memcached-1.4.25' Making install in doc make[2]: Entering directory `/opt/memcached-1.4.25/doc' make install-am make[3]: Entering directory `/opt/memcached-1.4.25/doc' make[4]: Entering directory `/opt/memcached-1.4.25/doc' make[4]: Nothing to be done for `install-exec-am'. /bin/mkdir -p '/usr/local/share/man/man1' /usr/bin/install -c -m 644 memcached.1 '/usr/local/share/man/man1' make[4]: Leaving directory `/opt/memcached-1.4.25/doc' make[3]: Leaving directory `/opt/memcached-1.4.25/doc' make[2]: Leaving directory `/opt/memcached-1.4.25/doc' make[2]: Entering directory `/opt/memcached-1.4.25' make[3]: Entering directory `/opt/memcached-1.4.25' /bin/mkdir -p '/usr/local/bin' /usr/bin/install -c memcached '/usr/local/bin' /bin/mkdir -p '/usr/local/include/memcached' /usr/bin/install -c -m 644 protocol_binary.h '/usr/local/include/memcached' make[3]: Leaving directory `/opt/memcached-1.4.25' make[2]: Leaving directory `/opt/memcached-1.4.25' make[1]: Leaving directory `/opt/memcached-1.4.25' [root@master memcached-1.4.25]#
9)检测memcached是否安装成功
[root@master bin]# ls -al /usr/local/bin/mem* -rwxr-xr-x 1 root root 360338 May 14 18:04 /usr/local/bin/memcached [root@master bin]#
10)启动Memcached服务
slab class 1: chunk size 96 perslab 10922
slab class 2: chunk size 120 perslab 8738
slab class 3: chunk size 152 perslab 6898
slab class 4: chunk size 192 perslab 5461
slab class 5: chunk size 240 perslab 4369
slab class 6: chunk size 304 perslab 3449
slab class 7: chunk size 384 perslab 2730
slab class 8: chunk size 480 perslab 2184
slab class 9: chunk size 600 perslab 1747
slab class 10: chunk size 752 perslab 1394
slab class 11: chunk size 944 perslab 1110
slab class 12: chunk size 1184 perslab 885
slab class 13: chunk size 1480 perslab 708
slab class 14: chunk size 1856 perslab 564
slab class 15: chunk size 2320 perslab 451
slab class 16: chunk size 2904 perslab 361
slab class 17: chunk size 3632 perslab 288
slab class 18: chunk size 4544 perslab 230
slab class 19: chunk size 5680 perslab 184
slab class 20: chunk size 7104 perslab 147
slab class 21: chunk size 8880 perslab 118
slab class 22: chunk size 11104 perslab 94
slab class 23: chunk size 13880 perslab 75
slab class 24: chunk size 17352 perslab 60
slab class 25: chunk size 21696 perslab 48
slab class 26: chunk size 27120 perslab 38
slab class 27: chunk size 33904 perslab 30
slab class 28: chunk size 42384 perslab 24
slab class 29: chunk size 52984 perslab 19
slab class 30: chunk size 66232 perslab 15
slab class 31: chunk size 82792 perslab 12
slab class 32: chunk size 103496 perslab 10
slab class 33: chunk size 129376 perslab 8
slab class 34: chunk size 161720 perslab 6
slab class 35: chunk size 202152 perslab 5
slab class 36: chunk size 252696 perslab 4
slab class 37: chunk size 315872 perslab 3
slab class 38: chunk size 394840 perslab 2
slab class 39: chunk size 493552 perslab 2
slab class 40: chunk size 616944 perslab 1
slab class 41: chunk size 771184 perslab 1
slab class 42: chunk size 1048576 perslab 1
<26 server listening (auto-negotiate)
<27 send buffer was 229376, now 268435456
<27 server listening (udp)
<29 server listening (udp)
<30 server listening (udp)
<28 server listening (udp)
-m 是分配给Memcache使用的内存数量,单位是MB,我这里是10MB;
-u 是运行Memcache的用户,我这里是root;
-l 是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200;
-p 是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口;
-c 选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定;
-P 是设置保存Memcache的pid文件,我这里是保存在 /tmp/memcached.pid
-vv 用very vrebose模式启动,调试信息和错误输出到控制台
启动时如果遇到如下错误:
[root@master bin]# /usr/local/bin/memcached -d -m 10 -u root -l 192.168.202.131 -p 12000 -c 256 -P /tmp/memcached.pid /usr/local/bin/memcached: error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such file or directory [root@master bin]#
解决办法如下:
#首先查下memcached的加载库位置 LD_DEBUG=libs memcached -v #针对缺失的库在加载目录建立个软连接 mkdir -p /usr/local/lib/lib/tls/x86_64 cd /usr/local/lib/lib/tls/x86_64 ln -s /usr/local/lib/libevent-2.0.so.5 /usr/local/lib/lib/tls/x86_64/libevent-2.0.so.5
再次启动就可以了
11)关闭memcached
方法一:
cat /tmp/memcached.pid|xargs kill -9
方法二:
#先查进程号 ps -ef|grep memcached #然后杀掉进程 kill -9 进程号
12)测试memcached
telnet在centos下的安装http://blog.csdn.net/jiguang0455/article/details/8670142
telnet 192.168.202.131 12000 Trying 192.168.202.131... Connected to 192.168.202.131 (192.168.202.131). Escape character is '^]'. set key1 0 60 4 zhou STORED get key1 VALUE key1 0 4 zhou END
3.4 memcached java API使用
3.4.1 通过Memcached-Java-Client客户端连接memcached
Memcached-Java-Client源代码地址:https://github.com/gwhalin/Memcached-Java-Client
Memcached-Java-Client维基文档地址: https://github.com/gwhalin/Memcached-Java-Client/wiki
1)Memcached-Java-Client相关maven地址,在https://github.com/gwhalin/Memcached-Java-Client/wiki中
since the domain danga.com is no more under our control(according to maven’s policy, the domain should be under authors’ control), the package name was replaced with “whalin.com”. If you don’t intend to rebuild your app, please use 2.6.×.
search “com.whalin” or “memcached java client” in search.maven.org, and you will find 3.0.×.
在maven*仓库http://search.maven.org/查询结果如下:
2)eclipse中新建maven工程,然后在pom.xml中配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>ehcachetest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ehcachetest</name> <packaging>jar</packaging> <properties> <!-- 这里定义所有的版本信息,方便版本统一管理 --> <ehcache.version>3.0.1</ehcache.version> <memcached.version>3.0.2</memcached.version> </properties> <dependencies> <!-- 這裡添加所有的依賴包 --> <!-- ehcache --> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> <!-- Memcached-Java-Client --> <dependency> <groupId>com.whalin</groupId> <artifactId>Memcached-Java-Client</artifactId> <version>${memcached.version}</version> </dependency> </dependencies> <build> <plugins> <!-- 這裡添加所有插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.4.3</version> <configuration> <!-- specify UTF-8, ISO-8859-1 or any other file encoding --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
这里我需要现在我的虚拟机中启动三个memcached节点
/usr/local/bin/memcached -d -m 10 -u root -l 192.168.202.131 -p 12001 -c 64 -P /tmp/memcached2.pid
/usr/local/bin/memcached -d -m 10 -u root -l 192.168.202.131 -p 12002 -c 64 -P /tmp/memcached3.pid
package com.memcached.test; import com.whalin.MemCached.MemCachedClient; import com.whalin.MemCached.SockIOPool; public class MyClass { //创建单例 protected static MemCachedClient mcc = new MemCachedClient(); //初始化连接池 static { //memcached服务器列表和各服务器权重 String[] servers = { "192.168.202.131:12000", "192.168.202.131:12001", "192.168.202.131:12002" }; Integer[] weights = { 3, 3, 2 }; //获取连接池 SockIOPool pool = SockIOPool.getInstance(); //设置服务列表和权重 pool.setServers( servers ); pool.setWeights( weights ); // 设置连接池配置信息 // 初始化5连接,最小5连接,最大250连接 // 设置最大超时时间为6小时 pool.setInitConn( 5 ); pool.setMinConn( 5 ); pool.setMaxConn( 250 ); pool.setMaxIdle( 1000 * 60 * 60 * 6 ); // 设置连接池主线程休眠时间为30秒 pool.setMaintSleep( 30 ); // 设置TCP连接信息set some TCP settings // 禁用nagle // 设置读超时时间为3秒 // 禁用连接超时 pool.setNagle( false ); pool.setSocketTO( 3000 ); pool.setSocketConnectTO( 0 ); // 这里开始初始化连接池 pool.initialize(); // 这里是一些压缩方面设置,不过在2.0.2版本中已经抛弃不再使用 // compress anything larger than 64k // mcc.setCompressEnable( true ); // mcc.setCompressThreshold( 64 * 1024 ); } // 只要上面设置OK后,就能根据MemCachedClient调用memcached API public static void main(String[] args) { mcc.set( "foo", "This is a test String" ); String bar = (String) mcc.get( "foo" ); System.out.println(bar); } }
如果你需要让你的memcached服务器对Java, PHP, Perl等等客户端提供服务,你需要配置如下设置:
// 设置HASH算法,默认是SockIOPool.NATIVE_HASH pool.setHashingAlg( SockIOPool.NEW_COMPAT_HASH ); // 是否将基本类型Boolean,Byte,String,Character,StringBuffer,StringBuilder,Short,Long,Double,Float,Date,Integer转换为String存储,默认是不转换的,设置转换的目的是节省空间和资源 // 对于非基本类型,这个参数设置是不起作用的 mcc.setPrimitiveAsString( true ); // 设置是否对key进行URL ENCODE,默认java客户端是进行URL ENCODE的 // 其他客户端是默认不进行URL ENCODE的 mcc.setSanitizeKeys( false );
关于memcached集群时候的故障转移参数设置
//集群中设置池的故障转移的标志 //当一个memcached服务器失效的时候客户端默认会failover另一个服务去 //如果失效的服务器恢复运行,客户端会failback到原来连接的服务器 //一般不要使用该功能 pool.setFailover( false ); pool.setFailback( false );
3.4.2 通过alisoft-xplatform-asf-cache客户端连接memcached
alisoft-xplatform-asf-cache是阿里软件的架构师岑文初进行封装的里面的注释都是中文的。目前没找到阿里正式发布该开源项目,也没在maven*仓库找到相关信息,只在网上找到关于alisoft-xplatform-asf-cache的源代码,这里自己建了个maven工程,maven工程源代码详见附件alisoft-xplatform-asf-cache-src.zip(注意文件是GBK编码的,eclipse中自己设置下,无意冒犯阿里,这里实在是找不到其相关URL连接)
1)先自己下载alisoft-xplatform-asf-cache-src.zip将其install到本地maven仓库,然后pom.xml配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>ehcachetest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ehcachetest</name> <packaging>jar</packaging> <properties> <!-- 这里定义所有的版本信息,方便版本统一管理 --> <ehcache.version>3.0.1</ehcache.version> <memcached.version>3.0.2</memcached.version> <alisoft.memcached.version>2.5.1</alisoft.memcached.version> </properties> <dependencies> <!-- 這裡添加所有的依賴包 --> <!-- ehcache --> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> <!-- Memcached-Java-Client --> <dependency> <groupId>com.whalin</groupId> <artifactId>Memcached-Java-Client</artifactId> <version>${memcached.version}</version> </dependency> <dependency> <groupId>com.alisoft</groupId> <artifactId>alisoft-xplatform-asf-cache</artifactId> <version>${alisoft.memcached.version}</version> </dependency> </dependencies> <build> <plugins> <!-- 這裡添加所有插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.4.3</version> <configuration> <!-- specify UTF-8, ISO-8859-1 or any other file encoding --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>2)配置alisoft-xplatform-asf-cache需要的XML文件alisoft-memcached.xml
<?xml version="1.0" encoding="UTF-8"?> <memcached> <!-- name 属性是程序中使用Cache的唯一标识 socketpool 属性将会关联到后面的socketpool配置; --> <client name="client_test_name" compressEnable="true" defaultEncoding="UTF-8" socketpool="poo_test_name"> <!-- 可选,用来处理出错情况 --> <errorHandler>com.alisoft.xplatform.asf.cache.memcached.MemcachedErrorHandler </errorHandler> </client> <!-- name 属性和client 配置中的socketpool 属性相关联 maintSleep 属性是后台线程管理SocketIO池的检查间隔时间,如果设置为0,则表明不需要后台线程维护SocketIO线程池,默认需要管理 socketTO 属性是Socket操作超时配置,单位ms。 aliveCheck 属性表示在使用Socket以前是否先检查Socket状态 --> <socketpool name="poo_test_name" maintSleep="5000" socketTO="3000" failover="true" aliveCheck="true" initConn="5" minConn="5" maxConn="250" nagle="false"> <!-- 设置memcache服务端实例地址.多个地址用","隔开 --> <servers>192.168.202.131:12000,192.168.202.131:12001,192.168.202.131:12002</servers> <!-- 可选配置。表明了上面设置的服务器实例的Load权重. 例如 <weights>3,3,4</weights> 表示30% load 在 192.168.202.131:12000 , 30% load 在 192.168.202.131:12001 , 40% load 在 192.168.202.131:12002 --> <weights>3,3,4</weights> </socketpool> </memcached>
3)测试java代码如下:
package com.memcached.test; import com.alisoft.xplatform.asf.cache.ICacheManager; import com.alisoft.xplatform.asf.cache.IMemcachedCache; import com.alisoft.xplatform.asf.cache.memcached.CacheUtil; import com.alisoft.xplatform.asf.cache.memcached.MemcachedCacheManager; public class AlisoftMemcachedTest { public static void main(String[] args) { ICacheManager<IMemcachedCache> manager; manager = CacheUtil.getCacheManager(IMemcachedCache.class, MemcachedCacheManager.class.getName()); manager.setConfigFile("alisoft-memcached.xml"); manager.start(); try { IMemcachedCache cache = manager.getCache("client_test_name"); cache.put("key", "value"); System.out.println(cache.get("key")); } finally { manager.stop(); } } }
3.5 利用magent搭建memcached集群
之前例子中使用memcached时缓存数据分布图如下:
上面的问题是如果中间一台机器宕机,那么缓存的部分数据就会丢失了,如下
magent的出现正是为了解决这个问题,
3.5.1 magent介绍
magent是一款开源的Memcached代理服务器软件。
magent项目网址为:http://code.google.com/p/memagent/
magent下载地址:https://code.google.com/archive/p/memagent/downloads
magent代理memcached实现主从备份来保证缓存数据完好无损,而且magent还可以作为从继续使用。
3.5.2 magent安装
这里完全参考http://www.php-note.com/article/detail/820,比较详细,这里说下自己真实安装遇到的问题。
1)主要安装步骤
#这里安装版本为magent-0.6,先创建个目录放置下载的安装文件 mkdir -p /opt/magent-0.6 cd /opt/magent-0.6 #下载 wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/memagent/magent-0.6.tar.gz #解压 tar -zxvf magent-0.6.tar.gz #安装 /sbin/ldconfig sed -i "s#LIBS = -levent#LIBS = -levent -lm#g" Makefile cd /opt/magent-0.6 make #安装完后拷贝 cp /opt/magent-0.6/magent /usr/bin/magent
2)执行make是报错1
gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o magent.o magent.c magent.c: In function 'writev_list': magent.c:729: error: 'SSIZE_MAX' undeclared (first use in this function) magent.c:729: error: (Each undeclared identifier is reported only once magent.c:729: error: for each function it appears in.) make: *** [magent.o] Error 1
解决办法:编辑文件/opt/magent-0.6/ketama.h,在开头添加如下内容:
#ifndef SSIZE_MAX #define SSIZE_MAX 32767 #endif
3)执行make遇到的错误2
gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o magent.o magent.c gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o ketama.o ketama.c gcc -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a gcc: /usr/lib64/libevent.a: No such file or directory gcc: /usr/lib64/libm.a: No such file or directory
解决办法:
因为我之前libevent是安装在/usr/local/lib,所以缺文件的话就执行下面命令建立了个软连接
ln -s /usr/local/lib/libevent* /usr/lib64/
4)执行make报错3
gcc -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a gcc: /usr/lib64/libm.a: No such file or directory make: *** [magent] Error 1
文章中说是要重新安装glibc glibc-devel,需要执行yum install -y glibc glibc-devel ,但我的不是这个问题
,最后是通过如下方式解决的
cp /usr/lib64/libm.so /usr/lib64/libm.a
5)执行make报错4
gcc -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a /usr/lib64/libevent.a(event.o): In function `detect_monotonic': event.c:(.text+0xc79): undefined reference to `clock_gettime' /usr/lib64/libevent.a(event.o): In function `gettime': event.c:(.text+0xd60): undefined reference to `clock_gettime' collect2: ld returned 1 exit status make: *** [magent] Error 1
解决办法,编辑文件/opt/magent-0.6/Makefile,修改前内容如下:
ARCH := $(shell uname -m) X64 = x86_64 CC = gcc PROGS = magent ifeq ($(ARCH), $(X64)) M64 = -m64 LIBS = /usr/lib64/libevent.a /usr/lib64/libm.a else LIBS = -levent -lm -lm -L/usr/local/lib endif CFLAGS = -Wall -g -O2 -I/usr/local/include $(M64) all: $(PROGS) STPROG = magent.o ketama.o ketama.o: ketama.c ketama.h $(CC) $(CFLAGS) -c -o $@ ketama.c magent.o: magent.c ketama.h $(CC) $(CFLAGS) -c -o $@ magent.c magent: $(STPROG) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) clean: rm -f *.o *~ $(PROGS)
编辑文件/opt/magent-0.6/Makefile,修改后内容如下:
ARCH := $(shell uname -m) X64 = x86_64 CC = gcc PROGS = magent ifeq ($(ARCH), $(X64)) M64 = -m64 LIBS = /usr/lib64/libevent.a /usr/lib64/libm.a else LIBS = -levent -lm -lm -L/usr/local/lib endif CFLAGS = -lrt -Wall -g -O2 -I/usr/local/include $(M64) all: $(PROGS) STPROG = magent.o ketama.o ketama.o: ketama.c ketama.h $(CC) $(CFLAGS) -c -o $@ ketama.c magent.o: magent.c ketama.h $(CC) $(CFLAGS) -c -o $@ magent.c magent: $(STPROG) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) clean: rm -f *.o *~ $(PROGS)
6)最后执行make成功,记录如下:
[root@master magent-0.6]# make gcc -lrt -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a [root@master magent-0.6]# pwd /opt/magent-0.6 [root@master magent-0.6]# ll total 624 -rw-rw-r-- 1 hadoop mysql 12822 Apr 10 2010 ketama.c -rw-rw-r-- 1 hadoop mysql 393 May 15 21:08 ketama.h -rw-r--r-- 1 root root 23616 May 15 21:08 ketama.o -rwxr-xr-x 1 root root 394126 May 15 21:24 magent -rw-r--r-- 1 root root 17257 May 15 20:52 magent-0.6.tar.gz -rw-rw-r-- 1 hadoop mysql 54813 Apr 15 2010 magent.c -rw-r--r-- 1 root root 112200 May 15 21:08 magent.o -rw-rw-r-- 1 hadoop mysql 510 May 15 21:24 Makefile [root@master magent-0.6]#
7)magent命令
-u uid
-g gid
-p port, default is 11211. (0 to disable tcp support)
-s ip:port, set memcached server ip and port
-b ip:port, set backup memcached server ip and port
-l ip, local bind ip address, default is 0.0.0.0
-n number, set max connections, default is 4096
-D do not go to background
-k use ketama key allocation algorithm
-f file, unix socket path to listen on. default is off
-i number, max keep alive connections for one memcached server, default is 20
-v verbose
3.5.3 magent搭建的memcached集群实例
这里参见的文章为:http://www.2cto.com/os/201506/406932.html
2.magent1,magent2接受读请求,将分别向主memcached中进行读取,而不想从memcached中读取;
3.一旦mecached1-mecached4中有一个memcached宕掉,此时magent1和magent2将向从memcached,也就是magent3中读取数据,达到缓存数据不丢失的效果;
4.当主中的memcache恢复后,将再次加入主memcached中,此时magent1和magent2将不会向从memcached中读数据了,但是写仍正常进行
memcached -d -m 10 -u root -l 192.168.202.131 -p 12001 -c 102400 -P /tmp/memcached1.pid
memcached -d -m 10 -u root -l 192.168.202.131 -p 12002 -c 102400 -P /tmp/memcached2.pid
memcached -d -m 10 -u root -l 192.168.202.131 -p 12003 -c 102400 -P /tmp/memcached3.pid
memcached -d -m 10 -u root -l 192.168.202.131 -p 12004 -c 102400 -P /tmp/memcached4.pid
memcached -d -m 10 -u root -l 192.168.202.131 -p 12005 -c 102400 -P /tmp/memcached5.pid
memcached -d -m 10 -u root -l 192.168.202.131 -p 12006 -c 102400 -P /tmp/memcached6.pid
#magent3代理服务启动
/usr/bin/magent -u root -n 102400 -l 192.168.202.131 -p 13003 -s 192.168.202.131:12005 -s 192.168.202.131:12006
#magent1代理服务启动
/usr/bin/magent -u root -n 102400 -l 192.168.202.131 -p 13001 -s 192.168.202.131:12001 -s 192.168.202.131:12002 -s 192.168.202.131:12003 -s 192.168.202.131:12004 -b 192.168.202.131:13003
#magent2代理服务启动
/usr/bin/magent -u root -n 102400 -l 192.168.202.131 -p 13002 -s 192.168.202.131:12001 -s 192.168.202.131:12002 -s 192.168.202.131:12003 -s 192.168.202.131:12004 -b 192.168.202.131:13003
启动后查看memcached服务如下:
启动后查看magent服务如下:
3.5.4 java API调用magent搭建的memcached集群服务
java代码如下:
package com.memcached.test; import com.whalin.MemCached.MemCachedClient; import com.whalin.MemCached.SockIOPool; public class MagentMemCachedClientTest { //创建单例 protected static MemCachedClient mcc = new MemCachedClient(); //初始化连接池 static { //memcached服务器列表和各服务器权重 ,这里现在配置magent代理服务器地址列表 String[] servers = { "192.168.202.131:13001", "192.168.202.131:13002" }; Integer[] weights = { 5, 5}; //获取连接池 SockIOPool pool = SockIOPool.getInstance(); //设置服务列表和权重 pool.setServers( servers ); pool.setWeights( weights ); // 设置连接池配置信息 // 初始化5连接,最小5连接,最大250连接 // 设置最大超时时间为6小时 pool.setInitConn( 5 ); pool.setMinConn( 5 ); pool.setMaxConn( 250 ); pool.setMaxIdle( 1000 * 60 * 60 * 6 ); // 设置连接池主线程休眠时间为30秒 pool.setMaintSleep( 30 ); // 设置TCP连接信息set some TCP settings // 禁用nagle // 设置读超时时间为3秒 // 禁用连接超时 pool.setNagle( false ); pool.setSocketTO( 3000 ); pool.setSocketConnectTO( 0 ); // 这里开始初始化连接池 pool.initialize(); /* alg=0,SockIOPool.NATIVE_HASH 使用String.hashCode()获得hash code,该方法依赖JDK,可能和其他客户端不兼容,建议不使用 alg=1,SockIOPool.OLD_COMPAT_HASH 使用original 兼容hash算法,兼容其他客户端 alg=2,SockIOPool.NEW_COMPAT_HASH 使用CRC32兼容hash算法,兼容其他客户端,性能优于original算法 alg=3,SockIOPool.CONSISTENT_HASH 使用MD5 hash算法 采用前三种hash算法的时候,查找cache服务器使用余数方法。采用最后一种hash算法查找cache服务时使用consistent方法。 */ pool.setHashingAlg( SockIOPool.NEW_COMPAT_HASH ); // 这里是一些压缩方面设置,不过在2.0.2版本中已经抛弃不再使用 // compress anything larger than 64k // mcc.setCompressEnable( true ); // mcc.setCompressThreshold( 64 * 1024 ); } // 只要上面设置OK后,就能根据MemCachedClient调用memcached API public static void main(String[] args) { mcc.set( "foo", "This is a test String" ); String bar = (String) mcc.get( "foo" ); System.out.println(bar); } }
4.redis
4.1 redis介绍
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。[1]
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)
目前,Vmware在资助着redis项目的开发和维护。
4.2 redis集群安装
1)下载、解压并编译redis
cd /opt wget http://download.redis.io/releases/redis-3.2.0.tar.gz tar -zxvf redis-3.2.0.tar.gz cd /opt/redis-3.2.0 make make install在centos 7下遇到无法正常编译问题,主要是gcc和tcl未安装导致,报错是“/bin/sh: cc: command not found”和“You need tcl 8.5 or newer in order to run the Redis test”,解决方法是安装一下gcc和tcl,如下:
#创建配置文件夹目录 mkdir -p /home/hadoopmanage/rediscluster/conf/7000 mkdir -p /home/hadoopmanage/rediscluster/conf/7001 mkdir -p /home/hadoopmanage/rediscluster/conf/7002 mkdir -p /home/hadoopmanage/rediscluster/conf/7003 mkdir -p /home/hadoopmanage/rediscluster/conf/7004 mkdir -p /home/hadoopmanage/rediscluster/conf/7005 #修改配置文件中的下面选项 vi /opt/redis-3.2.0/redis.conf port 7000 daemonize yes cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes #复制配置文件到个配置目录 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7000 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7001 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7002 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7003 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7004 cp /opt/redis-3.2.0/redis.conf /home/hadoopmanage/rediscluster/conf/7005 #修改文件/home/hadoopmanage/rediscluster/conf/7000/redis.conf中端口port为7000 #修改文件/home/hadoopmanage/rediscluster/conf/7001/redis.conf中端口port为7001 #修改文件/home/hadoopmanage/rediscluster/conf/7002/redis.conf中端口port为7002 #修改文件/home/hadoopmanage/rediscluster/conf/7003/redis.conf中端口port为7003 #修改文件/home/hadoopmanage/rediscluster/conf/7004/redis.conf中端口port为7004 #修改文件/home/hadoopmanage/rediscluster/conf/7005/redis.conf中端口port为70053)redis中提供了通过ruby脚本文件/opt/redis-3.2.0/src/redis-trib.rb来启动集群,这里首先要安装ruby及其组件,以便后续能使用该脚本。 redis-trib.rb是redis作者开发的基于ruby的管理工具,没有这个工具照样能玩转redis集群启动,参见我的另外一个博客redis cluster 非ruby方式启动
yum -y install zlib-devel openssl-devel cmake build-essential autoconf automake libtool zlib1g-dev pkg-config libssl-dev
#安装ruby
cd /opt
wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
tar -zxvf ruby-2.3.1.tar.gz
cd /opt/ruby-2.3.1
./configure
make
make install
#ruby的扩展库zlib安装
cd /opt/ruby-2.3.1/ext/zlib
ruby ./extconf.rb
make
make install
#ruby的扩展库openssl安装
cd /opt/ruby-2.3.1/ext/openssl
ruby ./extconf.rb
#如果遇到报错make: *** No rule to make target `/include/ruby.h', needed by `ossl_ssl_session.o'. Stop.
#在文件/opt/ruby-2.3.1/ext/openssl/Makefile里添加如下变量top_srcdir,值为ruby源代码的根路径/opt/ruby-2.3.1
#top_srcdir=/opt/ruby-2.3.1
make
make install
#如果遇到报错make: *** No rule to make target `/include/ruby.h', needed by `ossl_ssl_session.o'. Stop.
#在文件/opt/ruby-2.3.1/ext/openssl/Makefile里添加如下变量top_srcdir,值为ruby源代码的根路径/opt/ruby-2.3.1
#top_srcdir=/opt/ruby-2.3.1
https://rubygems.org/
#检测ruby环境
ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
#检测rubygems
3RubyGems(简称 gems)是一个用于对 Ruby组件进行打包的 Ruby 打包系统。
#它提供一个分发 Ruby 程序和库的标准格式,还提供一个管理程序包安装的工具。
#Ruby1.9.2之后版本版本默认自带RubyGems,如果低于这个版本的话,需要自行安装RubyGems,
#参照这里安装http://storysky.blog.51cto.com/628458/1155353/
#因为我是ruby2.3.1所以不需要安装此组件,已经自带了
#检查RubyGems是否安装好的方法如下:
gem -v
2.5.1
cd /opt/redis-3.2.0/src/ ./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
4.3 redis客户端使用
[hadoopmanage@master 7005]$ cd /opt/redis-3.2.0/src/ [hadoopmanage@master src]$ redis-cli -c -p 7000 127.0.0.1:7000> set key1 value1 -> Redirected to slot [9189] located at 127.0.0.1:7001 OK 127.0.0.1:7001> get key1 "value1" 127.0.0.1:7001> set key2 value2 -> Redirected to slot [4998] located at 127.0.0.1:7000 OK 127.0.0.1:7000> get key2 "value2" 127.0.0.1:7000> set key1 value3 -> Redirected to slot [9189] located at 127.0.0.1:7001 OK 127.0.0.1:7001> get key1 "value3" 127.0.0.1:7001> quit [hadoopmanage@master src]$
4.4 JAVA REDIS API调用
在http://redis.io/clients#java有所有关于redis所支持的语言的客户端API,这里我选择的是jedis(https://github.com/xetorthio/jedis)
1)在maven项目的pom.xml添加如下依赖:
<!-- jedis,client for redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> <type>jar</type> <scope>compile</scope> </dependency>
测试代码如下:
package com.redis.test; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; public class RedisTest { public static void main(String[] args) { Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); // Jedis Cluster will attempt to discover cluster nodes automatically List<HostAndPort> serverList = new ArrayList<HostAndPort>(); serverList.add(new HostAndPort("192.168.202.131", 7000)); serverList.add(new HostAndPort("192.168.202.131", 7001)); serverList.add(new HostAndPort("192.168.202.131", 7002)); serverList.add(new HostAndPort("192.168.202.131", 7003)); serverList.add(new HostAndPort("192.168.202.131", 7004)); serverList.add(new HostAndPort("192.168.202.131", 7005)); jedisClusterNodes.addAll(serverList); JedisCluster jc = new JedisCluster(jedisClusterNodes); jc.set("foo", "bar"); System.out.println(jc.get("foo")); } }
按照之前搭建的集群,会出现如下错误信息:
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnection(JedisSlotBasedConnectionHandler.java:54)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:113)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:131)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:30)
at redis.clients.jedis.JedisCluster.set(JedisCluster.java:60)
at com.redis.test.RedisTest.main(RedisTest.java:25)
原因是之前我们在所有redis.conf文件中配置了“bind 127.0.0.1” ,先更正为“192.168.202.131”,然后操作如下:
ps -efww|grep redis|grep -v grep|cut -c 9-15|xargs kill -9
#删除之前缓存数据,因为是写到磁盘了,所以这里需要删除数据文件
rm -rf /home/hadoopmanage/rediscluster/conf/*/nodes.conf
rm -rf /home/hadoopmanage/rediscluster/conf/*/dump.rdb
rm -rf /home/hadoopmanage/rediscluster/conf/*/appendonly.aof
#修改“bind 127.0.0.1”为“bind 192.168.202.131”,然后重启服务
cd /home/hadoopmanage/rediscluster/conf/7000/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /home/hadoopmanage/rediscluster/conf/7001/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /home/hadoopmanage/rediscluster/conf/7002/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /home/hadoopmanage/rediscluster/conf/7003/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /home/hadoopmanage/rediscluster/conf/7004/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /home/hadoopmanage/rediscluster/conf/7005/
/opt/redis-3.2.0/src/redis-server redis.conf
cd /opt/redis-3.2.0/src/
./redis-trib.rb create --replicas 1 192.168.202.131:7000 192.168.202.131:7001 192.168.202.131:7002 192.168.202.131:7003 192.168.202.131:7004 192.168.202.131:7005
再次执行测试代码,不会出现错误。
以上所有测试代码见附件“缓存技术总结所有测试代码.zip”