企业项目开发--分布式缓存memcached(1)

叁叁肆2018-12-18 10:50

此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


注意:本节代码基于《第七章 企业项目开发--本地缓存guava cache

1、本地缓存的问题

  • 本地缓存速度一开始高于分布式缓存,但是随着其缓存数量的增加,所占内存越来越大,系统运行内存越来越小,最后系统会被拖慢(这一点与第二点联系起来)
  • 本地缓存存于本机,其缓存数量与大小受本机内存大小限制
  • 本地缓存存于本机,其他机器的访问不到这样的缓存

解决方案:分布式缓存

  • Jboss cache:缓存还存于本机,但是会同步更新到其他机器(解决了第三个问题,解决不了第一和第二个问题),如果缓存机器数量很多,同步更新很耗时
  • memcached:缓存存于其他机器,理论上缓存数量与大小无限(因为集群可伸缩),且不需要同步,所以即使缓存机器数量很多,也无所谓,但是这样就会造成单点故障问题,最简单易行的解决方案是缓存备份,即缓存至少存两份。

 

2、memcached Java客户端的选用

当下常用的三种memcached Java客户端:

  • Memcached Client for Java:memcached官方提供,基于Java BIO实现
  • SpyMemcached:基于Java NIO
  • XMemcached:基于Java NIO,并发性能优于XMemcached,实际上SpyMemcached性能也很高

三者的实验比较结果:

http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html

所以,我们选用XMemcached来实现客户端的编写。

 

3、代码

在原来的代码结构上,我增加了一个ssmm0-cache模块,专门用于放置分布式缓存相关(memcached、redis、spring cache)的代码。

项目整体结构:


说明:怎样新建maven项目,并加入原来项目,最后引入eclipse,见第一章《第一章 企业项目开发--maven+springmvc+spring+mybatis+velocity整合

 

3.1、ssmm0

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     <groupId>com.xxx</groupId>
  8     <artifactId>ssmm0</artifactId>
  9     <version>1.0-SNAPSHOT</version>
 10 
 11     <name>ssmm0</name>
 12     <packaging>pom</packaging><!-- 父模块 -->
 13 
 14     <!-- 管理子模块 -->
 15     <modules>
 16         <module>userManagement</module><!-- 具体业务1-人员管理系统 -->
 17         <module>data</module><!-- 封装数据操作 -->
 18         <module>cache</module><!-- 缓存模块 -->
 19     </modules>
 20 
 21     <properties>
 22         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 23         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 24     </properties>
 25 
 26     <!-- dependencyManagement不会引入实际的依赖,只是作为一个依赖池,供其和其子类使用 -->
 27     <dependencyManagement>
 28         <dependencies>
 29             <!-- json -->
 30             <dependency>
 31                 <groupId>com.alibaba</groupId>
 32                 <artifactId>fastjson</artifactId>
 33                 <version>1.1.39</version>
 34             </dependency>
 35             <!-- servlet -->
 36             <dependency>
 37                 <groupId>javax.servlet</groupId>
 38                 <artifactId>javax.servlet-api</artifactId>
 39                 <version>3.0.1</version>
 40                 <scope>provided</scope>
 41             </dependency>
 42             <!-- spring -->
 43             <dependency>
 44                 <groupId>org.springframework</groupId>
 45                 <artifactId>spring-core</artifactId>
 46                 <version>3.2.6.RELEASE</version>
 47             </dependency>
 48             <dependency>
 49                 <groupId>org.springframework</groupId>
 50                 <artifactId>spring-beans</artifactId>
 51                 <version>3.2.6.RELEASE</version>
 52             </dependency>
 53             <dependency>
 54                 <groupId>org.springframework</groupId>
 55                 <artifactId>spring-context</artifactId>
 56                 <version>3.2.6.RELEASE</version>
 57             </dependency>
 58             <dependency>
 59                 <groupId>org.springframework</groupId>
 60                 <artifactId>spring-web</artifactId>
 61                 <version>3.2.6.RELEASE</version>
 62             </dependency>
 63             <dependency>
 64                 <groupId>org.springframework</groupId>
 65                 <artifactId>spring-webmvc</artifactId>
 66                 <version>3.2.6.RELEASE</version>
 67             </dependency>
 68             <!-- 这个是使用velocity的必备包 -->
 69             <dependency>
 70                 <groupId>org.springframework</groupId>
 71                 <artifactId>spring-context-support</artifactId>
 72                 <version>3.2.6.RELEASE</version>
 73             </dependency>
 74             <!-- mysql -->
 75             <dependency>
 76                 <groupId>mysql</groupId>
 77                 <artifactId>mysql-connector-java</artifactId>
 78                 <version>5.1.27</version>
 79                 <scope>runtime</scope>
 80             </dependency>
 81             <!-- 数据源 -->
 82             <dependency>
 83                 <groupId>org.apache.tomcat</groupId>
 84                 <artifactId>tomcat-jdbc</artifactId>
 85                 <version>7.0.47</version>
 86             </dependency>
 87             <!-- mybatis -->
 88             <dependency>
 89                 <groupId>org.mybatis</groupId>
 90                 <artifactId>mybatis</artifactId>
 91                 <version>3.1.1</version>
 92             </dependency>
 93             <dependency>
 94                 <groupId>org.mybatis</groupId>
 95                 <artifactId>mybatis-spring</artifactId>
 96                 <version>1.1.1</version>
 97             </dependency>
 98             <!-- velocity -->
 99             <dependency>
100                 <groupId>org.apache.velocity</groupId>
101                 <artifactId>velocity</artifactId>
102                 <version>1.5</version>
103             </dependency>
104             <dependency>
105                 <groupId>velocity-tools</groupId>
106                 <artifactId>velocity-tools-generic</artifactId>
107                 <version>1.2</version>
108             </dependency>
109             <!-- 用于加解密 -->
110             <dependency>
111                 <groupId>commons-codec</groupId>
112                 <artifactId>commons-codec</artifactId>
113                 <version>1.7</version>
114             </dependency>
115             <dependency>
116                 <groupId>org.bouncycastle</groupId>
117                 <artifactId>bcprov-jdk15on</artifactId>
118                 <version>1.47</version>
119             </dependency>
120             <!-- 集合工具类 -->
121             <dependency>
122                 <groupId>org.apache.commons</groupId>
123                 <artifactId>commons-collections4</artifactId>
124                 <version>4.0</version>
125             </dependency>
126             <!-- 字符串处理类 -->
127             <dependency>
128                 <groupId>org.apache.commons</groupId>
129                 <artifactId>commons-lang3</artifactId>
130                 <version>3.4</version>
131             </dependency>
132             <!-- http -->
133             <dependency>
134                 <groupId>org.apache.httpcomponents</groupId>
135                 <artifactId>httpclient</artifactId>
136                 <version>4.2.6</version>
137             </dependency>
138         </dependencies>
139     </dependencyManagement>
140 
141     <!-- 引入实际依赖 -->
142     <dependencies>
143         <!-- json -->
144         <dependency>
145             <groupId>com.alibaba</groupId>
146             <artifactId>fastjson</artifactId>
147         </dependency>
148         <!-- spring -->
149         <dependency>
150             <groupId>org.springframework</groupId>
151             <artifactId>spring-core</artifactId>
152         </dependency>
153         <dependency>
154             <groupId>org.springframework</groupId>
155             <artifactId>spring-beans</artifactId>
156         </dependency>
157         <dependency>
158             <groupId>org.springframework</groupId>
159             <artifactId>spring-context</artifactId>
160         </dependency>
161         <!-- 集合工具类 -->
162         <dependency>
163             <groupId>org.apache.commons</groupId>
164             <artifactId>commons-collections4</artifactId>
165         </dependency>
166         <!-- 字符串处理类 -->
167         <dependency>
168             <groupId>org.apache.commons</groupId>
169             <artifactId>commons-lang3</artifactId>
170         </dependency>
171     </dependencies>
172 
173     <build>
174         <resources>
175             <!-- 这里配置了这一块儿true,才可以让指定文件(这里是src/main/resources/spring-data.xml)读到pom.xml中的配置信息 
176                 , 值得注意的是,如果src/main/resources下还有其他文件,而你不想让其读pom.xml, 你还必须得把src/main/resources下的其余文件再配置一遍,配置为false(不可读pom.xml), 
177                 如下边的注释那样,否则,会报这些文件找不到的错误 
178             -->
179             <resource>
180                 <directory>src/main/resources</directory>
181                 <filtering>true</filtering>
182                 <includes>
183                     <include>*.xml</include>
184                     <include>*.properties</include>
185                 </includes>
186             </resource>
187             <!-- 
188             <resource> 
189                 <directory>src/main/resources</directory> 
190                 <filtering>false</filtering>   
191                 <includes> 
192                     <include>*.properties</include> 
193                 </includes> 
194             </resource> 
195             -->
196             <resource> 
197                 <directory>src/main/resources</directory> 
198                 <filtering>false</filtering>   
199                 <includes> 
200                 <!-- 这里如果不加这一条,那么在spring-data.xml中配置的xml将找不到classpath:mapper/admin/AdminMapper.xml -->
201                     <include>mapper/**/*.xml</include> 
202                 </includes> 
203             </resource> 
204         </resources>
205     </build>
206 
207     <!-- 
208         profiles可以定义多个profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果 
209         注意两点: 
210         1)<activeByDefault>true</activeByDefault>这种情况表示服务器启动的时候就采用这一套env(在这里,就是prod) 
211         2)当我们启动服务器后,想采用开发模式,需切换maven的env为dev,如果env的配置本身就是dev,需要将env换成rc或prod,点击apply,然后再将env切换成dev,点击apply才行 
212     -->
213     <profiles>
214         <!-- 开发env -->
215         <profile>
216             <id>dev</id>
217             <activation>
218                 <!-- 这里为了测试方便,改为了true,在上线的时候一定要改成false,否则线上使用的就是这一套dev的环境了 -->
219                 <activeByDefault>true</activeByDefault>
220                 <property>
221                     <name>env</name>
222                     <value>dev</value>
223                 </property>
224             </activation>
225             <properties>
226                 <env>dev</env>
227 
228                 <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
229                 <!--
230                      对于jdbc.url中内容的配置,如果需要配置 &amp;时,有两种方法:
231                     1)如下边这样,使用<![CDATA[XXX]]>包起来 
232                     2)使用jdbc.properties文件来读取此pom.xml,然后spring.xml再读取jdbc.properties文件 显然,前者更方便,而且还省了一个jdbc.properties的文件,但是,有的时候,还是会用后者的; 
233                     在使用后者的时候,注意三点:
234                     1)需要修改上边的build中的内容 
235                     2)需要在spring.xml中配置<context:property-placeholder location="classpath:jdbc.properties"/> 
236                     3)将jdbc.properties放在ssmm0-data项目中,之后需要将ssmm0-data项目的env配置为dev 
237                 -->
238                 <jdbc.url><![CDATA[jdbc:mysql://127.0.0.1:3306/blog?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;characterEncoding=utf-8]]></jdbc.url>
239                 <jdbc.username>root</jdbc.username>
240                 <jdbc.password>123456</jdbc.password>
241                 
242                 <!-- memcache,多台服务器之间需要使用空格隔开,而不要使用英文逗号隔开,因为Xmemcached的AddrUtil源码是根据空格隔开的 -->
243                 <memcached.servers><![CDATA[127.0.0.1:11211]]></memcached.servers>
244                 <memcached.max.client>10</memcached.max.client><!-- 最多的客户端数 -->
245                 <memcached.expiretime>900</memcached.expiretime><!-- 过期时间900s -->
246                 <memcached.hash.consistent>true</memcached.hash.consistent><!-- 是否使用一致性hash算法 -->
247                 <memcached.connection.poolsize>1</memcached.connection.poolsize><!-- 每个客户端池子的连接数 -->
248                 <memcached.op.timeout>2000</memcached.op.timeout><!-- 操作超时时间 -->
249             </properties>
250         </profile>
251         <!-- 预上线env -->
252         <profile>
253             <id>rc</id>
254             <activation>
255                 <activeByDefault>false</activeByDefault>
256                 <property>
257                     <name>env</name>
258                     <value>rc</value>
259                 </property>
260             </activation>
261             <properties>
262                 <env>rc</env>
263 
264                 <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
265                 <!-- 假设的一个地址 -->
266                 <jdbc.url><![CDATA[jdbc:mysql://10.10.10.100:3306/blog?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;characterEncoding=utf-8]]></jdbc.url>
267                 <jdbc.username>root2</jdbc.username>
268                 <jdbc.password>1234562</jdbc.password>
269             </properties>
270         </profile>
271         <!-- 线上env -->
272         <profile>
273             <id>prod</id>
274             <activation>
275                 <!-- 这里为了测试方便,改为了false,在上线的时候一定要改成true,否则线上使用的就不是这一套环境了 -->
276                 <activeByDefault>false</activeByDefault>
277                 <property>
278                     <name>env</name>
279                     <value>prod</value>
280                 </property>
281             </activation>
282             <properties>
283                 <env>prod</env>
284 
285                 <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
286                 <!-- 假设的一个地址 -->
287                 <jdbc.url><![CDATA[jdbc:mysql://99.99.99.999:3307/blog?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;characterEncoding=utf-8]]></jdbc.url>
288                 <jdbc.username>sadhijhqwui</jdbc.username>
289                 <jdbc.password>zxczkchwihcznk=</jdbc.password>
290             </properties>
291         </profile>
292     </profiles>
293 </project>

说明:这里给出了完整版,实际上只做了以下5点改动:

  • 新增cache子module
  • 引入了commons-lang3的jar包,该jar封装了一些对于字符串的处理方法,eg.isBlank()
  • <resources>部分因为要将该pom.xml中的配置读取到ssmm0-cache模块的properties文件中去,所以添加了过滤目录
  • 在dev环境下配置了与memcached相关的服务器列表及参数
  • 最后一点,只是为了测试方便,将dev设为默认选用的环境,而prod不是,在实际上线之前,一定要改回来

注意:

  • 在pom.xml中配置memcached服务器列表时,要以"ip1:port1 ip2:port2..."这样的形式,即每台服务器之间用空格隔开,这与Xmemcached的AddrUtil读取服务器列表的方式有关。

 

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击