Arch-03-14-缓存策略

缓存策略与优化
本文探讨了多种缓存策略,包括操作系统、数据库及应用程序层面的缓存管理,并深入介绍了使用Voldemort实现一致性和高可用性的方法,以及如何通过调整JVM参数和搭建简易CDN来提升缓存性能。

Arch-03-14-缓存策略

 

robin(有人谓肉饼,太有才了)的一文很强悍,摘在这儿做个灯引。

http://robbin.iteye.com/blog/770553

 

 

 写道
操作系统磁盘缓存
数据库缓存
--Query Cache
--Data Buffer
应用程序缓存
--对象缓存
--查询缓存
页面内部缓存
--动态页面静态化
--Servlet缓存
Web服务器缓存
--squid/nginx
--CDN
客户端浏览器缓存
--AJAX缓存
--基于HTTP协议的资源缓存
如果是对象Cache,自然是ORM透明化出来; 
如果是查询Cache,自然是DAO层处理; 
如果是Action Cache,自然是Web 层Action处理; 
如果是Page Cache,自然是Servlet Filter处理;

用 ibatis + memcached 的策略明显有缺陷,粒度太大,考虑用更小粒度高效的方案吧。

 

听起来 hibernate 的缓存不错,原理很好,之前的项目一直是类似机制,只不过用的是自己实现的 simpleJdbc. 现在需要在 ibatis 的基础上实现细粒度缓存,好象没那么简单。

 

=================================================

1.  Voldemort (伏地魔)

  • 一致性
  • 可插拔
  • 独立
  • 一个强大的Java客户端,支持可插拔的序列化
  • 可整合
2. 尽管 ibatis 无好的方法直接配对象缓存,但项目已经使用 ibatis 很久了(历史遗留问题),还得改造成 ibatis + 对象缓存 +查询缓存,不过不指望 ibatis 本身能做什么,权把它当作 SQL解析器,想办法把返回的对象作缓存吧。其中有个关键点,就是对象中包含下级对象的处理。
    (1)ibatis 对下级对象的处理是直接对象实例化后返回,如果是一对多(父子)关系的情形,则返回的父对象中已经包含了全部的子对象,使用起来是比较方便,可缓存的粒度就肥大了许多,几乎无法控制。比如基于一树型组织机构的查询,公司级、部门级、个人级三级过滤分三个查询,查询出来的记录集可以想象是个什么样子了,大部份是重复的,把这些对象整包放入缓存,还真有些不伦不类。
    (2)理想的方法不应该将子对象实例化后放入父对象,而应该存放 ID 的 list。有段代码以 JDBC 为例。

 public MessageBean getByID(long id)
    throws MessageNotFoundException, DAOException
  {
        MessageBean bean = null;

        Connection con = getConnection();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
        	// load message row
          pstmt = con.prepareStatement("SELECT pMessageID, ownerID, senderID, recipientID, subject, body, readStatus, folderID, pMessageDate, status, creationDate, sentDate FROM message WHERE pMessageID = ?");
          pstmt.setLong(1, id);

          rs = pstmt.executeQuery();
          if (!rs.next()) {
            throw new MessageNotFoundException("Message " + id + " could not be loaded from the database.");
          }

          bean = read(rs, new MessageBean());

          rs.close();
          pstmt.close();

        	// load message properties rows
          Map p = new HashMap();
          pstmt = con.prepareStatement("SELECT name, propValue FROM MessageProp WHERE pMessageID=?");
          pstmt.setLong(1, id);
          rs = pstmt.executeQuery();
          while (rs.next())
          {
            p.put(rs.getString(1), rs.getString(2));
          }
          bean.setProperties(p);
          rs.close();
          pstmt.close();

        	// load message attachment IDs
          LongList attachmentList = new LongList();
          try
          {
            pstmt = con.prepareStatement("SELECT attachmentID FROM attachment WHERE objectType=5 AND objectID=?");
            pstmt.setLong(1, bean.getID());
            rs = pstmt.executeQuery();
            while (rs.next()) {
              attachmentList.add(rs.getLong(1));
            }
            bean.setAttachments(attachmentList);
          }
          catch (SQLException sqle) {
            Log.error(sqle);
          }
          finally {
            ConnectionManager.close(rs, pstmt);
          }
        }
        catch (SQLException sqle) {
          throw new MessageNotFoundException("Message with id " + id + " could not be loaded from the database.");
        }
        catch (NumberFormatException nfe)
        {
          throw new MessageNotFoundException("Message with id " + id + " could not be loaded from the database.");
        }
        finally
        {
          ConnectionManager.close(rs, pstmt);
          releaseConnection(con);
        }

        return bean; 
  }
  
3. 历尽黑暗,总算勉强制伏了伏地魔,同时还有应用的集群。

(3.1) voldemort 配置文件

#!/bin/bash/voldemort-service.sh

#
#   Copyright 2008-2009 LinkedIn, Inc
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

if [ $# -gt 1 ];
then
	echo 'USAGE: bin/voldemort-server.sh [voldemort_home]'
	exit 1
fi

base_dir=$(dirname $0)/..

for file in $base_dir/dist/*.jar;
do
  CLASSPATH=$CLASSPATH:$file
done

for file in $base_dir/lib/*.jar;
do
  CLASSPATH=$CLASSPATH:$file
done

for file in $base_dir/contrib/*/lib/*.jar;
do
  CLASSPATH=$CLASSPATH:$file
done

CLASSPATH=$CLASSPATH:$base_dir/dist/resources

if [ -z "$VOLD_OPTS" ]; then
  VOLD_OPTS="\
-Xmx2G -server \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=6650 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false"
fi

java -Dlog4j.configuration=log4j.properties $VOLD_OPTS -cp $CLASSPATH voldemort.server.VoldemortServer $@
 
不要持久化,只要内存缓存的配置

<!--cluster.xml-->
<cluster>
<name>pluscluster</name>
<server>
    <id>0</id>
    <host>127.0.0.1</host>
    <http-port>8071</http-port>
    <socket-port>6666</socket-port>
    <admin-port>6667</admin-port>
    <partitions>0,1,2,3,4,5,6,7
    </partitions>
</server>
</cluster>

server.properties
 
# The ID of *this* particular cluster node
node.id=0

max.threads=100

############### DB options ######################

http.enable=false
socket.enable=true
jmx.enable=true
storage.configs=voldemort.store.bdb.BdbStorageConfiguration,voldemort.store.memory.InMemoryStorageConfiguration,voldemort.store.memory.CacheStorageConfiguration
slop.store.engine=memory

# NIO connector settings.
enable.nio.connector=true
enable.nio.admin.connector=false

# don't throttle admin client - set to a really high value
stream.read.byte.per.sec=1000000000000
stream.write.byte.per.sec=1000000000000

# default buffer size is 10MB. We'll turn it down to 512KB
admin.streams.buffer.size=524288

# use only 8 selectors (for nio)
nio.connector.selectors=8
nio.admin.connector.selectors=8

 

4. 应用架构基于共享 Voldemort 缓存,利用 JGroup 传送消息的应用集群,讲起来有一匹布那么长,暂且略过,隔几天画个图上来。


 

5. 启动 JMX 监控

 

开启一个Jconsole 本地连接
 


 

 

6. 缓存性能优化之道

 

(1)客户端资源缓存

 

 

HTTPD server - “cache.conf.rpmnew”

 

(2)服务端页缓存

 

 

HTTP header in the response to Cache-Control max-age=3600.
 

 

(3)配置外部 CDN 缓存服务器

 

 

. 安装 CDN 缓存服务器
. 基于 URL
. 服务器端生成页面时,重写静态资源地址指向 CDN URL
. 当完成页面请求后,客户端使用 CDN URL 返回资源
. 如果 CDN 命中,直接返回客户端,否则,从应用服务器返回并存入 CDN,备用。

 

 

 

 

(4)调整虚拟机参数

 

 

JVM_HEAP_MAX=4096
JVM_HEAP_MIN=4096
 

 

7. 动手搭建山寨 CDN

 

 

参考原文 http://blog.youkuaiyun.com/jackem/article/details/3206440

目前对于 CDN 网络搭建技术有很多成熟的商业方案,对于资金受限的网站可能无法自己搭建或购买。这里提供了一套简单的实现CDN网络的技术架构,所使用的软件全部是开源高效且免费的。根据CDN网络技术原理,必须有一个动态DNS服务器,开源的可以使用bind,免费开源。不修改代码,bind 基本上可以实现简单的动态DNS解析功能。需要更强的自定义功能,可以自己修改代码实现。

(1)假设web站点部署在上海电信。

(2)在上海电信部署Bind,提供动态解析服务,在上海电信部署主Web站点;

(3)在全国各地找几个比较重要的机房部署 lighttpd 服务器提供静态资源下载,如北京网通,广州电信两个机房各放置2台服务器,安装上 lighttpd+mod_cache+mod_proxy 等;

(4)配置子下载服务器的 lighttpd 的 modCache 功能,并设置 proxy.server 为主站点上海机房IP。

(5)当北京网通用户访问主站的静态文件时,通过动态DNS解析得到北京网通机房服务器的IP地址,浏览器就从北京网通服务器上下载文件了,提高了响应速度,同时降低了主站的压力。

这样一个简单的技术架构已经完成。对于 lighttpd+modCache 的详细配置参考http://www.linux.com.cn/modcache/。

 

 

 

 
    <bean id="usersCache" parent="cacheBean" depends-on="cacheFactoryManager">
        <constructor-arg value="Users" />
        <property name="type" ref="local"/>
        <!-- in milliseconds, 24 hours, checked every hour -->
        <property name="expirationTime" value="86400000"/>
        <property name="expirationPeriod" value="3600000"/>
        <property name="transactional" value="false"/>
        <property name="keyRetrieval" value="false"/>
        <property name="type" value="false"/>
        <property name="timeoutTime" value="60"/>
        <property name="timeUnit" value="false"/>
        <property name="additionalProperties" value="Map<String, String>"/>
        <property name="valueSerializer" value="false"/>
        <property name="disableEviction" value="false"/>
        <property name="prefixAware" value="false"/>
        <property name="maxCacheSize" value="10g"/>
        <sizeLimit
    </bean>

<think>这是一个Maven构建时依赖解析失败的报错。错误信息表明在构建项目`lims-job-arch-mh`时,无法解析依赖项`com.sinoyd.lims.job:lims-job-public-mh:jar:0.0.1-SNAPSHOT`,因为Maven在远程仓库(nexus)中找不到该依赖项,并且在本地仓库中缓存了该失败信息,因此在nexus仓库的更新间隔过去之前或强制更新之前,不会重新尝试解析。 错误信息关键点: 1. 项目:com.sinoyd.lims.job:lims-job-arch-mh:jar:0.0.1-SNAPSHOT 2. 缺失的依赖:com.sinoyd.lims.job:lims-job-public-mh:jar:0.0.1-SNAPSHOT 3. 仓库:http://nexusproxy.dev.yd/repository/maven-public/ 4. 错误原因:Maven在远程仓库中找不到`com.sinoyd.lims.job:lims-job-mh:pom:0.0.1-SNAPSHOT`(注意这里依赖的artifactId变成了`lims-job-mh`,可能是父项目或模块?) 可能的原因: 1. 依赖项`lims-job-public-mh`在远程仓库中确实不存在(可能还未构建上传)。 2. 依赖项的版本号写错(0.0.1-SNAPSHOT)。 3. 仓库配置问题,比如仓库地址错误、需要认证等。 4. 本地仓库缓存了失败信息,导致不会重新尝试下载(除非强制更新或等待更新间隔)。 解决方法: 1. 检查依赖项是否存在:确保`lims-job-public-mh`项目已经成功构建并部署到了nexus仓库中。可以访问nexus仓库的web界面,查看该依赖是否存在。 2. 检查版本号:确认项目pom.xml中声明的依赖版本是否正确。 3. 强制更新快照依赖:使用Maven命令时加上`-U`参数强制更新快照版本,例如: ``` mvn clean install -U ``` 4. 检查仓库配置:确保settings.xml中配置的仓库地址(nexus)是正确的,并且如果有认证信息,也需要正确配置。 5. 如果依赖项确实不存在,需要先构建并安装/部署缺失的项目(即`lims-job-public-mh`和可能缺失的父项目`lims-job-mh`)。 详细步骤: 步骤1:检查nexus仓库中是否存在该依赖 - 访问nexus仓库的web界面,在maven-public仓库中搜索`com.sinoyd.lims.job:lims-job-public-mh:0.0.1-SNAPSHOT`,看是否存在。 步骤2:如果不存在,需要先构建并部署该依赖 - 进入`lims-job-public-mh`项目目录,执行: ``` mvn clean deploy ``` 步骤3:如果存在,可能是缓存问题,强制更新 - 在项目根目录下执行: ``` mvn clean install -U ``` 步骤4:检查settings.xml配置 - 确保Maven的settings.xml中配置了正确的仓库地址和认证信息(如果需要)。 步骤5:如果问题仍然存在,检查依赖项的pom文件是否正确 - 确保`lims-job-public-mh`项目的pom文件中没有错误,特别是它的父项目(可能是`lims-job-mh`)是否存在并且可访问。 注意:错误信息中提到了`lims-job-mh:pom:0.0.1-SNAPSHOT`,说明这个项目可能是多模块项目,而`lims-job-mh`是父项目。因此,也需要确保父项目已经被正确安装或部署到仓库中。 如果以上步骤都无效,可能需要检查网络连接和nexus仓库的可用性。 示例代码(这里不需要代码,但根据要求,提供一段Java代码示例无实际意义,因此跳过。但为了满足格式要求,可以写一个简单的Java类,但问题本身是构建问题,非代码问题。) 不过,由于要求必须提供Java代码,这里提供一个简单的Java类示例: ```java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` 但是,请注意,这个示例代码与解决上述Maven依赖问题无关。 因此,更相关的是如何通过Maven命令解决依赖问题,但Maven命令不是Java代码。所以,我们只能按照要求提供一个Java代码示例,但实际解决方法是上述的步骤。 最后,按照要求提出相关问题: §§相关问题§§: 1. Maven构建时出现“Could not resolve dependencies”错误通常有哪些原因? 2. 如何配置Maven的settings.xml文件以使用私有仓库(如Nexus)? 3. Maven中的SNAPSHOT版本和RELEASE版本有什么区别? 4. 如何强制Maven重新下载快照(SNAPSHOT)依赖?</think>### 问题分析 该错误是 Maven 构建过程中依赖解析失败导致的,具体原因如下: 1. **缺失依赖**: Maven 无法从仓库中找到 `com.sinoyd.lims.job:lims-job-mh:pom:0.0.1-SNAPSHOT` 这个父 POM 文件。 2. **缓存问题**: 本地仓库已缓存该依赖缺失的错误状态,且未到重新尝试更新的时间(受 Maven 更新策略控制)。 3. **仓库配置**: 依赖在配置的 Nexus 仓库(`http://nexusproxy.dev.yd/repository/maven-public/`)中不存在或无法访问。 --- ### 解决方法 #### 1. **检查依赖是否存在** 访问 Nexus 仓库的 Web 界面,直接搜索 `com.sinoyd.lims.job:lims-job-mh:0.0.1-SNAPSHOT`,确认是否已部署。 #### 2. **强制更新 Maven 本地缓存** 在构建命令中添加 `-U` 参数强制更新快照依赖: ```bash mvn clean install -U ``` #### 3. **检查 Maven 仓库配置** 确保 `settings.xml` 中仓库配置正确(示例): ```xml <settings> <mirrors> <mirror> <id>nexus</id> <url>http://nexusproxy.dev.yd/repository/maven-public/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> </settings> ``` #### 4. **手动安装缺失依赖** 如果依赖在仓库中不存在,需先构建并安装该模块: ```bash # 进入 lims-job-mh 项目目录 cd path/to/lims-job-mh mvn clean install ``` #### 5. **检查依赖范围** 确保父 POM 在 `lims-job-public-mh` 中正确声明: ```xml <!-- lims-job-public-mh/pom.xml --> <parent> <groupId>com.sinoyd.lims.job</groupId> <artifactId>lims-job-mh</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> ``` #### 6. **清理本地仓库缓存** 手动删除本地仓库中的缓存目录(路径示例): `~/.m2/repository/com/sinoyd/lims/job/lims-job-mh/0.0.1-SNAPSHOT` --- ### 根本原因总结 - **依赖未部署**:`lims-job-mh` 模块未被构建或部署到 Nexus。 - **快照更新策略**:Maven 默认每 24 小时更新一次快照依赖(除非用 `-U` 覆盖)。 - **网络/权限问题**:Nexus 仓库无法访问或缺少认证(需检查 `settings.xml` 的 `<server>` 配置)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值