此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
3.2、ssmm0-cache
模块结构:
3.2.1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4 5 <modelVersion>4.0.0</modelVersion> 6 7 <!-- 指定父模块 --> 8 <parent> 9 <groupId>com.xxx</groupId> 10 <artifactId>ssmm0</artifactId> 11 <version>1.0-SNAPSHOT</version> 12 </parent> 13 14 <groupId>com.xxx.ssmm0</groupId> 15 <artifactId>ssmm0-cache</artifactId> 16 17 <name>ssmm0-cache</name> 18 <packaging>jar</packaging> 19 20 <!-- 引入实际依赖 --> 21 <dependencies> 22 <!-- memcached --> 23 <dependency> 24 <groupId>com.googlecode.xmemcached</groupId> 25 <artifactId>xmemcached</artifactId> 26 <version>1.4.3</version> 27 </dependency> 28 </dependencies> 29 </project>
说明:在该pom.xml中引入了xmemcached的jar包,注意该jar依赖于slf4j.jar,如果不是用maven的话,需要手动导入slf4j.jar,但是用maven的话,maven自己会导入相关的依赖包。
3.2.2、cache_config.properties
1 #memcached配置# 2 #memcached服务器集群 3 memcached.servers = ${memcached.servers} 4 #缓存过期时间 5 memcached.expiretime = ${memcached.expiretime} 6 #是否使用一致性hash算法 7 memcached.hash.consistent = ${memcached.hash.consistent} 8 #memcached的最大客户端数量 9 memcached.max.client = ${memcached.max.client} 10 #每个客户端池子的连接数 11 memcached.connection.poolsize = ${memcached.connection.poolsize} 12 #操作超时时间 13 memcached.op.timeout = ${memcached.op.timeout}
说明:这里需要从根pom.xml(即ssmm0的pom.xml)中读取信息,所以在根pom.xml的资源过滤部分有相关的修改
3.2.3、FileUtil.java
1 package com.xxx.cache.util; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 import org.apache.commons.lang3.math.NumberUtils; 8 9 /** 10 * 文件操作工具类 11 */ 12 public class FileUtil { 13 14 /** 15 * 加载属性文件*.properties 16 * @param fileName 不是属性全路径名称,而是相对于类路径的名称 17 */ 18 public static Properties loadProps(String fileName){ 19 Properties props = null; 20 InputStream is = null; 21 22 try { 23 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);//获取类路径下的fileName文件,并且转化为输入流 24 if(is != null){ 25 props = new Properties(); 26 props.load(is); //加载属性文件 27 } 28 } catch (Exception e) { 29 e.printStackTrace(); 30 }finally{ 31 if(is!=null){ 32 try { 33 is.close(); 34 } catch (IOException e) { 35 e.printStackTrace(); 36 } 37 } 38 } 39 40 return props; 41 } 42 43 /* 44 * 从属性文件中获取int型数据 45 */ 46 public static int getInt(Properties props, String key, int defaultValue){ 47 int value = defaultValue; 48 if(props.containsKey(key)){ //属性文件中是否包含给定键值 49 value = NumberUtils.toInt(props.getProperty(key), defaultValue);//从属性文件中取出给定键值的value,并且转换为int型 50 } 51 return value; 52 } 53 54 /* 55 * 从属性文件中获取long型数据 56 */ 57 public static long getLong(Properties props, String key, long defaultValue){ 58 long value = defaultValue; 59 if(props.containsKey(key)){ //属性文件中是否包含给定键值 60 value = NumberUtils.toLong(props.getProperty(key), defaultValue);//从属性文件中取出给定键值的value,并且转换为int型 61 } 62 return value; 63 } 64 65 /* 66 * 从属性文件中获取boolean型数据 67 */ 68 public static boolean getBoolean(Properties props, String key, boolean defaultValue){ 69 boolean value = defaultValue; 70 if(props.containsKey(key)){ //属性文件中是否包含给定键值 71 value = toBoolean(props.getProperty(key), defaultValue); 72 } 73 return value; 74 } 75 76 77 public static boolean toBoolean(String str, boolean defaultValue) { 78 if(str == null) { 79 return defaultValue; 80 } 81 return Boolean.parseBoolean(str); 82 } 83 84 /** 85 * 测试 86 */ 87 public static void main(String[] args) { 88 Properties props = FileUtil.loadProps("cache_config.properties"); 89 //System.out.println(props); 90 System.out.println(props.getProperty("memcached.servers", "123"));//从属性文件中读取string 91 System.out.println(FileUtil.getInt(props, "httpclient.max.conn.per.route2", 10));//属性文件中没有这个key 92 } 93 }
说明:对于该类的介绍与注意点,参看"Java文件相关"系列的《第一章 属性文件操作工具类》
在该类中,添加了一些方法
注意:
3.2.4、CachePrefix.java
1 package com.xxx.cache.util; 2 3 /** 4 * 在该类中定义一些缓存的前缀 5 * 方式: 6 * 1、定义一个类,在其中添加静态常量 7 * 2、使用枚举类(这是最好的方式) 8 * 作用: 9 * 1、防止缓存键值重复(通常情况下,每一种业务对应一种前缀) 10 * 2、可以起到根据前缀分类的作用 11 * 后者主要是在memcached中实现类似于redis的实现。 12 */ 13 public enum CachePrefix { 14 USER_MANAGEMENT, //人员管理业务类缓存前缀 15 HOTEL_MANAGEMENT; //酒店管理业务类缓存前缀 16 }
说明:在该类中定义一些缓存的前缀
作用:
注意:定义常量类有两种方式
关于枚举类与普通类做常量类的优缺点对比,查看《effective Java:第二版》第30条,或者参考我"Java高效使用"系列中后续的文章
3.2.5、MemcachedUtil
1 package com.xxx.cache.memcached; 2 3 import java.io.IOException; 4 import java.util.HashMap; 5 import java.util.Map; 6 import java.util.Properties; 7 import java.util.concurrent.TimeoutException; 8 9 import org.apache.commons.lang3.StringUtils; 10 11 import com.xxx.cache.util.CachePrefix; 12 import com.xxx.cache.util.FileUtil; 13 14 15 import net.rubyeye.xmemcached.MemcachedClient; 16 import net.rubyeye.xmemcached.MemcachedClientBuilder; 17 import net.rubyeye.xmemcached.XMemcachedClientBuilder; 18 import net.rubyeye.xmemcached.command.BinaryCommandFactory; 19 import net.rubyeye.xmemcached.exception.MemcachedException; 20 import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator; 21 import net.rubyeye.xmemcached.transcoders.CompressionMode; 22 import net.rubyeye.xmemcached.transcoders.SerializingTranscoder; 23 import net.rubyeye.xmemcached.utils.AddrUtil; 24 25 /** 26 * memcached工具类(基于Xmemcached实现) 27 */ 28 public class MemcachedUtil { 29 private static Map<Integer, MemcachedClient> clientMap 30 = new HashMap<Integer, MemcachedClient>();//client的集合 31 private static int maxClient = 3; 32 private static int expireTime = 900;//900s(默认的缓存过期时间) 33 private static int maxConnectionPoolSize = 1;//每个客户端池子的连接数 34 private static long op_time = 2000L;//操作超时时间 35 36 private static final String KEY_SPLIT = "-";//用于隔开缓存前缀与缓存键值 37 38 /** 39 * 构建MemcachedClient的map集合 40 */ 41 static{ 42 //读取配置文件 43 Properties props = FileUtil.loadProps("cache_config.properties"); 44 String servers = props.getProperty("memcached.servers", "127.0.0.1:11211");//获取memcached servers集合 45 maxClient = FileUtil.getInt(props, "", maxClient); 46 expireTime = FileUtil.getInt(props, "memcached.expiretime", expireTime); 47 maxConnectionPoolSize = FileUtil.getInt(props, "memcached.connection.poolsize", maxConnectionPoolSize); 48 op_time = FileUtil.getLong(props, "memcached.op.timeout", op_time); 49 50 if(StringUtils.isNotBlank(servers)){ 51 MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(servers)); 52 builder.setConnectionPoolSize(1);//这个默认也是1 53 builder.setSessionLocator(new KetamaMemcachedSessionLocator(true));//使用一致性hash算法 54 55 SerializingTranscoder transcoder = new SerializingTranscoder(1024*1024);//序列化转换器,指定最大的数据大小1M 56 transcoder.setCharset("UTF-8");//默认为UTF-8,这里可去掉 57 transcoder.setCompressionThreshold(1024*1024);//单位:字节,压缩边界值,任何一个大于该边界值(这里是:1M)的数据都要进行压缩 58 transcoder.setCompressionMode(CompressionMode.GZIP);//压缩算法 59 60 builder.setTranscoder(transcoder); 61 builder.setCommandFactory(new BinaryCommandFactory());//命令工厂 62 63 //构建10个MemcachedCient,并放入clientMap 64 for(int i=0;i<maxClient;i++){ 65 try { 66 MemcachedClient client = builder.build(); 67 client.setOpTimeout(op_time);//设置操作超时时间,默认为1s 68 clientMap.put(i, client); 69 } catch (IOException e) { 70 e.printStackTrace(); 71 } 72 } 73 } 74 } 75 76 /** 77 * 从MemcachedClient中取出一个MemcachedClient 78 */ 79 public static MemcachedClient getMemcachedClient(){ 80 /* 81 * Math.random():产生[0,1)之间的小数 82 * Math.random()*maxClient:[0~maxClient)之间的小数 83 * (int)(Math.random()*maxClient):[0~maxClient)之间的整数 84 */ 85 return clientMap.get((int)(Math.random()*maxClient)); 86 } 87 88 /** 89 * 设置缓存 90 * @param keyPrefix 缓存的键的前缀 91 * @param key 缓存的键 92 * @param value 缓存的值 93 * @param exp 缓存过期时间 94 */ 95 public static void setCacheWithNoReply(CachePrefix keyPrefix, 96 String key, 97 Object value, 98 int exp){ 99 try { 100 MemcachedClient client = getMemcachedClient(); 101 client.addWithNoReply(keyPrefix+KEY_SPLIT+key, exp, value); 102 } catch (InterruptedException e) { 103 e.printStackTrace(); 104 } catch (MemcachedException e) { 105 e.printStackTrace(); 106 } 107 } 108 109 /** 110 * 设置缓存 111 * @param exp 缓存过期时间(默认时间) 112 */ 113 public static void setCacheWithNoReply(CachePrefix keyPrefix, 114 String key, 115 Object value){ 116 setCacheWithNoReply(keyPrefix, key, value, expireTime); 117 } 118 119 /** 120 * 设置缓存,并返回缓存成功与否 121 * 注意: 122 * 1、设置已经设置过的key-value,将会返回false 123 */ 124 public static boolean setCache(CachePrefix keyPrefix, 125 String key, 126 Object value, 127 int exp){ 128 boolean setCacheSuccess = false; 129 try { 130 MemcachedClient client = getMemcachedClient(); 131 setCacheSuccess = client.add(keyPrefix+KEY_SPLIT+key, exp, value); 132 } catch (TimeoutException e) { 133 e.printStackTrace(); 134 } catch (InterruptedException e) { 135 e.printStackTrace(); 136 } catch (MemcachedException e) { 137 e.printStackTrace(); 138 } 139 return setCacheSuccess; 140 } 141 142 /** 143 * 设置缓存,并返回缓存成功与否(缓存超时时间采用默认) 144 * @param key 145 * @param value 146 */ 147 public static boolean setCache(CachePrefix keyPrefix, 148 String key, 149 Object value){ 150 return setCache(keyPrefix, key, value, expireTime); 151 } 152 153 /** 154 * 获取缓存 155 */ 156 public static Object getCache(CachePrefix keyPrefix, String key){ 157 Object value = null; 158 try { 159 MemcachedClient client = getMemcachedClient(); 160 value = client.get(keyPrefix+KEY_SPLIT+key); 161 } catch (TimeoutException e) { 162 e.printStackTrace(); 163 } catch (InterruptedException e) { 164 e.printStackTrace(); 165 } catch (MemcachedException e) { 166 e.printStackTrace(); 167 } 168 return value; 169 } 170 171 /** 172 * 删除缓存 173 */ 174 public static void removeCacheWithNoReply(CachePrefix keyPrefix, String key){ 175 try { 176 MemcachedClient client = getMemcachedClient(); 177 client.deleteWithNoReply(keyPrefix+KEY_SPLIT+key); 178 } catch (InterruptedException e) { 179 e.printStackTrace(); 180 } catch (MemcachedException e) { 181 e.printStackTrace(); 182 } 183 } 184 185 /** 186 * 删除缓存,并返回删除成功与否 187 */ 188 public static boolean removeCache(CachePrefix keyPrefix, String key){ 189 boolean removeCacheSuccess = false; 190 try { 191 MemcachedClient client = getMemcachedClient(); 192 removeCacheSuccess = client.delete(keyPrefix+KEY_SPLIT+key); 193 } catch (TimeoutException e) { 194 e.printStackTrace(); 195 } catch (InterruptedException e) { 196 e.printStackTrace(); 197 } catch (MemcachedException e) { 198 e.printStackTrace(); 199 } 200 return removeCacheSuccess; 201 } 202 203 /** 204 * 测试 205 * @param args 206 */ 207 public static void main(String[] args) { 208 /*for(int i=0;i<100;i++){ 209 System.out.println(Math.random()); 210 }*/ 211 System.out.println(MemcachedUtil.setCache(CachePrefix.USER_MANAGEMENT,"hello4", "world")); 212 System.out.println(MemcachedUtil.getCache(CachePrefix.USER_MANAGEMENT,"hello4")); 213 /*System.out.println(MemcachedUtil.getCache("hello2")); 214 System.out.println(MemcachedUtil.getCache("hello2")); 215 System.out.println(MemcachedUtil.getCache("hello2")); 216 System.out.println(MemcachedUtil.getCache("hello2")); 217 System.out.println(MemcachedUtil.getCache("hello2"));*/ 218 } 219 }
首先给出该类的一个图(不知道该怎么称呼这个图)
说明:上述的图可以根据源代码的追踪画出来;该类是基于XMemcached实现了的一个工具类,主要包含以下6个部分
注意:
在这里,需要装一个memcached服务器了。
我们就简要的装一个windows版本的,我发了一个再云盘上,链接:http://pan.baidu.com/s/1dDMlov3,下载后,解压,
两种使用方法:
A、双击解压后的"x86"(32位)或"x64"(64位)文件夹中的memcached.exe,跳出窗口即可。
B、在C:\Windows\System32\cmd.exe右击"以管理员身份运行"-->在命令窗口进入E:\memcached\x86目录中-->"memcached.exe -d install"-->之后去"本地服务"看看是不是已经有memcached server的服务了,如果已经有了,说明安装成功-->之后启动服务,两种方式:
B1、手工在"本地服务部分"启动
B2、命令窗口下"memcached.exe -p 11211 -m 32 -c 1024 -d start",该方法可以指定参数启动memcached服务,-p表示端口,-m表示分配的内存,-c表示最大的并发连接数
当然,我们在实际使用中,会装在Linux系统上,同时也需要指定一系列参数,例如分配的最大内存、最大并发数等等。
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。