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详见如下博客:

扫描二维码关注公众号,回复: 376300 查看本文章
来源 作者 标题 网址
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服务

后台模式启动
/usr/local/bin/memcached -d -m 10 -u root -l 192.168.202.131 -p 12000 -c 256 -P /tmp/memcached.pid
 
调试模式启动
[root@master networksettings]# /usr/local/bin/memcached -vv -m 10 -u root -l 192.168.202.131 -p 12000 -c 256 -P /tmp/memcached.pid
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)
参数说明 
-d 选项是启动一个守护进程;
-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中

HOWTO中写道
3.0.x released, features = 2.6.x, but you can now get it from maven central, please notice:

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 12000 -c 64 -P /tmp/memcached1.pid
/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     2.2 Ehcache使用
<?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命令

magent命令详解
-h this message
-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

工作流程:
1.magent1,magent2接受写请求,将key分别写入mecached1-mecached4中,同时也将key写入从memcached上,也就是magent3上,magent3再分别写入mecached5,mecached6中;主和从都是用的同一个分配算法
2.magent1,magent2接受读请求,将分别向主memcached中进行读取,而不想从memcached中读取;
3.一旦mecached1-mecached4中有一个memcached宕掉,此时magent1和magent2将向从memcached,也就是magent3中读取数据,达到缓存数据不丢失的效果;
4.当主中的memcache恢复后,将再次加入主memcached中,此时magent1和magent2将不会向从memcached中读数据了,但是写仍正常进行
启动脚本
#memcached1-memcached6节点服务启动
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集群服务

与之前不使用magent的区别 
程序上的主要区别就是,以前提供的是memcached服务器的地址列表,现在只需提供magent服务器的地址列表就行了

     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存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
        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,如下: 
yum -y install gcc tcl
    2)修改/opt/redis-3.2.0/redis.conf中配置文件,创建集群配置文件夹并将配置文件分发到各个配置目录 
#创建配置文件夹目录
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为7005
    3)redis中提供了通过ruby脚本文件/opt/redis-3.2.0/src/redis-trib.rb来启动集群,这里首先要安装ruby及其组件,以便后续能使用该脚本。  redis-trib.rb是redis作者开发的基于ruby的管理工具,没有这个工具照样能玩转redis集群启动,参见我的另外一个博客 redis cluster 非ruby方式启动  
#必要的linux库安装
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
     4)执行ruby脚本,使得之前启动的6台单独的redis服务实现3主3从集群服务,谁是主谁是从是ruby脚本自己决定的 
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"));
	}
}

    按照之前搭建的集群,会出现如下错误信息:

错误信息
Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException: no reachable node in cluster
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”,然后操作如下:

重新操作如下:
#停止所有redis服务方法一
 pkill redis 
#停止所有redis服务方法二
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”

 

猜你喜欢

转载自aperise.iteye.com/blog/2296219