应用Memcached实现分布式缓存系统


微博是这样炼成的:从聊天室到 Twitter 的技术实现 《微博是这样炼成的:从聊天室到 Twitter 的技术实现》 蓝杰 www.NetJava.cn 胡东峰 http://javaFound.javaEye.com 4.6 应用 Memcached 实现缓存系统.................................................................................49 4.6.1 初识 Memcached...............................................................................................49 1.Memcached 安装...........................................................................................49 2.MM 基本协议...............................................................................................51 4.6.2 缓存系统的网络构架.......................................................................................53 1.为什么需要缓存...........................................................................................53 2.缓存网络结构...............................................................................................53 3.缓存服务器群结构.......................................................................................55 4.Memcached 应用编程示例..........................................................................57 4.6.3 为 JavaKe 应用缓存系统..................................................................................59 48 第 4 章 通信高级技术分析 4.6 应用 Memcached 实现缓存系统 4.6.1 初识 Memcached 1.Memcached 安装 如果你是第一次听到 Memcached,得动手用起来,才能了解它是什么。首先下载安装。假 设你用的是 Windows 平台,请登录 http://jehiah.cz/projects/memcached-win32/下载 Windows 平 台的 MemCached(以后简称 MM)服务器版本,如图 4.57 所示。 ▲ 图 4.57 memcached for Win32 下载页面示意 在解压后的目录下,应存在一个 memcached.exe 文件,在命令执行安装命令,将其以系 统服务的形式安装,如图 4.58 所示。 ▲ 图 4.58 在命令行执行命令安装 MM 服务器 安装完毕后,在服务管理中即看到,MM 已被安装为系统服务,如图 4.59 所示。 ▲ 图 4.59 安装成功后,在服务中可以看到 MM 服务器管理选项 49 微博是这样炼成的:从聊天室到 Twitter 的技术实现 以后,就可通过服务器理器管理 MM 的启动和停止。确保 MM 服务已启动,在命令行执 行连接命令,如图 4.60 所示。 50 第 4 章 通信高级技术分析 ▲ 图 4.60 使用 telnet 连接 MM 服务器 通过 telnet 连接 MM 启动后默认的端口 11211,连接后如图 4.61 所示。 ▲ 图 4.61 连接 MM 服务器后,执行错误的命令示意图 随便输入一些内容,看到返回的却总是 ERROR,这是因为我们并未按照 MM 规定的协 议发送数据,处理这个问题是我们接下来的任务。 2.MM 基本协议 只需要将 MM 理解为一个基于内存的仓库——向 MM 中可以存入数据,MM 服务器通过 TCP/IP 连接接收数据。接收数据的协议非常简洁,通过 telnet 即可测试,例如,向 MM 中保存 key 为 netjava 的值 v2,保存时间是永不过期,然后再从 MM 中取出 key 为 netjava 的值,命令 执行格式如图 4.62 所示。 ▲ 图 4.62 在 telnet 中执行 MM 的存取命令 MM 中数据的存储和更新操作是以如下文本格式的协议规定的命令。 \r\n 51 微博是这样炼成的:从聊天室到 Twitter 的技术实现 向 MM 中存取数据的常用命令和参数说明如下。  为 set,表示向 MM 中存入一条记录。  key 表示这条记录的键值。  flags 是一个十进制的 int,表示存储记录时的客户端标志,在记录取出时会返回。  exptim 表示数据的过期时间,0 表示永不过期,其他数值则表示有效的毫秒数,在过 期时间之后,客户端将取不到这条记录,MM 中的过期记录会被清空或删除。  bytes 表示这条命令要保存的数据字节的长度,回车后即可输入要保存的数据。 即要保存的数据,其长度必须和 bytes 值对应,如图 4.63 所示如果保存时设 定的是 3 个字节的数据,在输入第 4 个字符时系统会报错。 Get 命令表示从 MM 中取出 key 对应的值,格式为 get 。如果 MM 有 key 对应的值则 返回存入的值;如果无,则返回结束标志 END,如图 4.64 所示。 ▲ 图 4.63 输入过长数据时,MM 报错 ▲ 图 4.64 从 MM 中根据键名取得数据 Replace 命令:用以替换 MM 中某 key 对应的值。 Delete 命令:用以从 MM 中删除一条记录,如图 4.65 所示。 想知道当前 MM 服务器的状态相关数据,请输入 stats 命令,如图 4.66 所示。 ▲ 图 4.65 MM 常用存入、替换、提取、删除命令示例 ▲ 图 4.66 用 stats 命令查看 MM 内部状态 图 4.66 显示了当前 MM 服务器中的读取字节数、缓存命中率、空间大小等。 52 第 4 章 通信高级技术分析 当然,如果关闭掉 MM 服务器,其中所存放的数据会全部丢失,MM 是将数据放在内存 中的。以上是常用的 MM 操作命令协议示例,完整版本见其官方网站: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 在这个文档中,读者可以找到更多的命令协议说明。 4.6.2 缓存系统的网络构架 1.为什么需要缓存 考虑 JavaKe 系统的设计容量要支持 100 万的在线人数,从技术角度看,要解决的最大问 题是性能瓶颈。例如一个用户登录的过程要执行如下数据库操作。  要到数据库中查询用户账号,用户相关资料。  要查询用户的所有好友及其相关资料。 即便优化 SQL 语句、多建索引、对数据库大表分区或做只读分离,但数据库总是将数据保 存到磁盘文件中的,数据流程图如图 4.67 所示。 数据库引擎 硬盘 应用程序 数据 内 存 ▲ 图 4.67 数据从文件到数据库再到应用程序的流程示意图 数据总要经过从“磁盘文件装入数据库内存”的过程,而且关系型数据库是专为复杂 结构的数据索引存储所设计的。在数据 I/O 过程中,读写的索引、数据的封装等操作对于小型 系统而言,一秒和半秒的差别是无所谓的。但对于大数据容量的系统,每次操作的小数时间 (毫秒级)累加起来,都有可能导致系统不可用!用户登录某系统时,如果要等超过 5 秒的 时间,还会有耐心吗? 提升系统性能的基本思路就是:让数据尽可能地靠近要处理的 CPU,简单地说就是尽可 能将数据放在内存中。这样就省去了最耗时的、从磁盘文件读取数据的操作! 2.缓存网络结构 MM 不是关系数据库,它是一个内存库的服务器,将需要的数据全部放到 MM 中,提取 时就不需要再从数据库(文件)中读取了。当然,存放在内存中的数据结构须尽量简单,例如 系统的用户资料等。 一个典型的 MM 应用结构如图 4.68 所示。 53 微博是这样炼成的:从聊天室到 Twitter 的技术实现 如图 4.68 所示的结构,应用程序对数据的读写就全部是到 MM 的内存中进行。当然, MM 只是将数据存在内存中,如果 MM 服务器一旦崩溃,内存中的数据肯定丢失。 确实是这样,MM 服务器停止后,数据将全部丢失。不要以关系数据库的标准衡量它! MM 和 RDBMS 的适用方向不一样:数据库以保存数据或者说持久化数据为其核心功能,不 丢失数据是其天职,而 MM 的目标只是缓存,缓存为的只是速度!这样理解吧,数据库保存 数据是为“天长地久”的要求,而 MM 的对保存的数据却是“只求曾经拥有”。 应用程序 用户 memcached客户端库 memcache d memcache d memcache d ▲ 图 4.68 应用 MM 做缓存服务的网络结构图 因此,系统构架时要同时利用到 RDBMS 和 MM 的特长,系统网络结构如图 4.69 所示。 从缓存读取写入缓存第一次读取 应用程序 用户 数据库 memcached客户端库 memcached 数据库 数据库 memcached memcached JDBC ▲ 图 4.69 MM 数据库实现的系统网络结构 当应用程序第一次从数据库中读出数据并返回的同时,将数据存储到 MM 服务器中,以 后取数据时,首先到 MM 服务器中查找,如果找到则返回 MM 中的数据;如果未找到,再去 数据库中查找。 54 第 4 章 通信高级技术分析 3.缓存服务器群结构 大容量数据的应用,其数据库服务器一般采用集群式,这样有两个目的。 (1)热备冗余和故障转移。 高要求的系统通常会要求 All OnLine 的安全机制。考虑要实现的是一个永不崩溃的系统结 构,在典型 B/S 结构下,应用服务器、数据库服务器都可以以集群的方式部署,如图 4.70 所示。 如图 4.70 图所示,两个数据库,一个是主控数据库,一个是从数据库。在正常情况下, 数据的存取在主数据库上进行,并被主数据库同步到从数据库中。如果主库故障,则应用对数 据的访问转移到从数据库上。应用服务器端的备份方案同理。这种方案安全稳定,但对性能的 提升作用不大。 Web服务器群 数据库群 正常 同步数据 从数据库 主数据库 正常 主库故障后 用户 Session同步 ▲ 图 4.70 应用服务器和数据库热备的网络结构图 (2)提升性能的数据库集群或应用服务器群。 为了提升性能,可以将数据库按数据的用途存放到不同的物理数据库服务器上。例如将 数据库表分为只查询表和写入表两类,则可以将这两种表放到不同的服务器上,如图 4.71 所示。 提升性能,或者根据数据的业务性质的不同,将数据存放到不同的服务器上,例如一个 大型门户应用,可以将用户数据根据用户地区的不同,存放到不同的 DB 服务器上,如图 4.72 所示。 55 微博是这样炼成的:从聊天室到 Twitter 的技术实现 Web服务器群 数据库群 数据生产 WriteDB 数据生成服务器 查询服务器 READ DB 查询用户 数据生产 数据 整理 程序 ▲ 图 4.71 根据业务读写类型,分离数据库和应用服务器的网络结构 Web服务器群 数据库群 A区用户 A地区库 A区前置服务器 B区前置服务器 B地区库B区用户 ▲ 图 4.72 根据用户来源进行分区的网络结构 以上两种方式都可以有效提升系统性能,并降低系统结构上的复杂度,从而便于维护和 扩展。如果要将 MM 作为服务器集群部署,首先要解决的问题是:客户端如何确定,或者按 照什么规则将数据保存到哪一台 MM 服务器中? MM 客户端通过 TCP 传送数据到多台 MM 服务器,将数据根据 key 值保存在 MM 服务器 的内存中后,客户端保存数据所对应的 key。当有多台 MM 服务器在线时,客户端应用要保存 56 第 4 章 通信高级技术分析 数据时,首先需要选定某一台服务器,并且,在取数据时,还要知道到哪台 MM 服务器去根 据这个 key 取数据。 在 MM 客户端应用中,必须保存已存储数据的 key 列表与其数据所在的 MM 服务器 IP 对 应的 map,MM 的客户端程序实现中,通过设定的 key 与 MM 服务器 IP 这两个数据的 Hash 算法,实现了查找 key 对应的数据在哪台 IP 所在的 MM 服务器的定位功能,如图 4.73 所示。 (关于 MM 客户端定位 key 与其所对应 IP 的 Hash 算法的具体实现,请查阅 Hash 算法原理或 “Google”一下吧。 应用程序 memcached客户端库 Node1:Memcached 192.168.1.2 Node1:B A写入缓存 Node2:Memcached 192.168.1.3 Node3:Memcached 192.168.1.4 从缓存读取B Node2: Node3:A 服务器选择 Hash算法 ▲ 图 4.73 MM 客户端根据 Hash 算法到指定的 MM 服务器中提取数据 在 MM 客户端类库中已经包含有选定服务器的 Hash 算法的实现,保存数据调用时,只 要传入对应的 key 和 value,MM 客户端将自动选择 MM 服务器存储数据或提取 key 对应的值。 在对 MM 服务器有了初步了解后,接下来马上动手使用它。 4.Memcached 应用编程示例 要让应用将数据保存到 MM 服务器,可以自己根据 MM 的协议在代码中通过编写 Socket 通信程序实现,也可以下载开源已封装好的 MM 客户端库应用到程序中。在此示例 中 使 用 “ Memcached Client For Java” 开 源 库 ( http://www.whalin.com/memcached/ #download)。 首先下载其类库,下载时可选对应 JDK 的版本,下载后,将其类库 java_memcached- release_ 2.0.1.jar 加入项目中(注意:这个客户端库中使用到了 log4j.jar 做日志记录,请另行 下载 log4j.jar 库)。在编写客户端程序之前,一共安装了 3 台 MM 服务器,所在服务器的 IP 地址分别为:192.168.1.147、192.168.1.148、192.168.1.149。 客户端模拟代码如下: import java.util.Map; import com.danga.MemCached.MemCachedClient; import com.danga.MemCached.SockIOPool; /** 57 微博是这样炼成的:从聊天室到 Twitter 的技术实现 *Memcahed 简单测试程序 *JK 即时通信系统 * @author 蓝杰 www.NetJava.cn */ public class TestMM { //创建一个 MM 对象,这个对象就代表了与 MM 服务器器的连接 protected static MemCachedClient mcc = new MemCachedClient(); //静态块中初始化连接相关参数 static { String[] servers = { "192.168.1.147:11211",//MM 三个服务器所在的 IP "192.168.1.148:11211","192.168.1.149:11211" }; //MM 服务器对应的权重 Integer[] weights = { 3, 2, 1 }; //配置 MM 客户端与服务器之间的 Socket 连接池 SockIOPool pool = SockIOPool.getInstance(); //将要连接的服务器信息设置给连接池 pool.setServers( servers ); pool.setWeights( weights ); //连接池的一些初始化数据设定 pool.setInitConn( 5 ); pool.setMinConn( 5 ); pool.setMaxConn( 250 ); pool.setMaxIdle( 1000 * 60 * 60 * 6 ); //维护线程每隔多久检查池中连接情况 pool.setMaintSleep( 30 ); //Socket 的相关设置:连接,读取超时限制 pool.setNagle( false ); pool.setSocketTO( 3000 ); pool.setSocketConnectTO( 0 ); // initialize the connection pool pool.initialize(); } //主函数 public static void main(String args[]) { for(int i=0;i<10;i++){ UserInfo u=new UserInfo(i,"第"+i+"个用户二次"); mcc.set( i+"", u); System.out.println("已存入一个一个对象"); } for(int i=0;i<10;i++){ 58 第 4 章 通信高级技术分析 UserInfo u=(UserInfo) mcc.get(i+"" ); System.out.println("from mm: "+u); //mcc.delete(i+"");//从 MM 中删除对象 } //打印出各个 MM 服务器当前的状态 Map stats = mcc.stats(); System.out.println("stats is "+stats); } } MM 客户端 Java 的实现库通已有良好的封装,使用时,只需要初始化连接池,创建一个 MemCachedClient 实例即可,以后对 MM 中对象的存改都是通过这个 MemCachedClient 实例 完成的。 当调用 MemCachedClient 对象向 MM 服务器中存入数据时,在本例中有 3 台 MM 服务器, 通过设定 3 台不同的权值,决定向每台服务器保存的数据数量,代码如下所示: String[] servers = { "192.168.1.147:11211",//MM 三个服务器所在的 IP "192.168.1.148:11211","192.168.1.149:11211" }; //MM 服务器对应的权重 Integer[] weights = { 3, 2, 1 }; 如果存储完毕后关闭了 147 和 148 服务器的的 MM 服务,这两台 MM 服务器上的数据就 会丢失。再次调用客户端提取已存入的数据时,MM 不会报错,却只会提取出存入 149 号 MM 服务器中的数据。 如果客户端程序重新运行时,必须记住以前放入数据的 key,才可提取出已存放的 数据。 MM 客户端代码较为简单,其他操作示例请查看下载包中的文档的详尽说明。 如果需要给系统增加新的缓存服务器,只需要增加装有 MM 服务的机器即可,同时让 客户端知道这个机器的 IP。当 MM 服务器出现故障时,并不会影响到整个系统的运行,丢失 掉的只是缓存中的数据。读者必须理解“缓存”的意思:只要能将数据暂时放入内存中即可! 4.6.3 为 JavaKe 应用缓存系统 在 JavaKe 系统中,需要大量查询的是用户信息(好友列表)数据,在每次查询这些数据 时,会首先到 MM 服务器群中查找,如果找到则返回。如果未找到,就到数据库中提取数据 并将数据放入 MM 缓存服务器中,以后再提取时,只需要到 MM 缓存服务器中提取。对用户 数据的更新,在提交到数据库中之后,也需要更新 MM 缓存中的数据,网络结构如图 4.74 所示。 59 微博是这样炼成的:从聊天室到 Twitter 的技术实现 数据库 客户端程序 jk服务器 Node3:Memcached Node3:Memcached Node3:Memcached ▲ 图 4.74 MM 中要同步更新存入数据库的数据 具体代码示例如下: //查询数据 public User getUser(String key){ User user=new User(); //1.如果从 MM 中找到了这个数据对象 if(selectFromMM(key,user)){ return user; }//2.到数据库中查询数据 else{ user=selectFromDB(key);//从数据库中查询 //3.将从数据库中找到的数据存入 MM 服务器中 MM.set(key,user);// return user; } } //更新数据 public boolean updateUser(String key,User u){ if(updateUser2DB(key,u)){ MM.replace(key,u);//替换 MM 中原有的数据。 } } 稍作改造,以上代码就可以应用到项目中了,你可以自豪地对别人说:“我的系统有良 好的缓存支持!性能提升啦!”如果你这样说,一样要想到别人会问“提升了多少?”,总 不能摸着脑门说“反正是快了些啊”。这不是技术人员的说法,技术人员会拿出具体数据。例 如,在每秒多少次查询请求下,数据库中有多少条数据;在什么样硬件配置的机器上,配置 了多少个 MM 服务器;使用 MM 缓存前和使用后,在存取/更新多少数据的比较中,相差了 多少毫秒。 60 第 4 章 通信高级技术分析 你能完成这个测试吗? MM 作为缓存系统的不足。 MM 并不是一个“分布式”的缓存系统,或者说,MM 只是一个主从式的缓存系统,如 图 4.75 所示。 3 2 MM客户机 1 Node3:Memcached Key: Value E 010010101 F 001010011 Node2:Memcached Key: Value C 010010101 D 001010011 Node1:Memcached Key: Value A 010010101 B 001010011 ▲ 图 4.75 MM 将数据放在单一的 MM 服务器中,如果某台机器崩溃,数据即丢失 在图 4.75 中,用 3 台机器做 MM 服务器,客户端分别向这 3 台机器中存入了 3 对键值对, 但如果 Node2 崩溃掉,则其中的数据就会丢失。理想的情况是能有如图 4.76 所示的缓存服务 器群结构。 MM二群 MM一群 save Save(key,value) NodeA2: Key: Value C 01101 D 00001 NodeA1: Key: Value A 00001 B 01101 NodeA3: Key: Value E 01011 F 00111 NodeA2: Key: Value C 01101 D 00001 NodeA1: Key: Value A 00001 B 01101 NodeA3: Key: Value E 01011 F 00111 应用程序 MM客户端 ▲ 图 4.76 具有热备功能的 MM 缓存集群网络结构示意 MM 客户端在保存数据时,会将同一份数据保存到两个不同的 MM 集群中(每个群中则 有多台 MM 服务器)。在这种结构下,如果有多个 MM 服务器群,开发者就真的可以不需要 61 微博是这样炼成的:从聊天室到 Twitter 的技术实现 RDBMS 了——多台服务器群同时崩溃的可能性和中彩票差不多吧。图 4.76 这种分布式的实现 可能代价过高,但变通一下,如图 4.77 所示。 save Save(key,value) Host-A: Key: Value A 00001 B 01101 Key: Value A 00001 B 01101 Host-B: 应用程序 MM客户端 ▲ 图 4.77 实现内存共享的 MM 服务器网络结构 要让 MM 将数据同时存入到两台,或多台 MM 服务器中,有两个策略。第一种方法是在 MM 客户端实现上做进一步改进,使其保存数据时,能将数据分别在每台 MM 服务器各存入 一份,同样修改数据时,每台 MM 中的数据也要被同步修改。 另一种办法是在 MM 服务器上做进一步开发,如图 4.77 所示。Host-A 和 Host-B 是两台物 理上独立的 MM 服务器,当 Host-A 中被存入一份数据时,Host-A 就应马上将数据复制一份, 存入 Host-B 服务器。如果这一功能被扩展实现了,从外部看来,Host-A 和 Host-B 两台服务器 的内存就实现了同步,这才是典型的“分布式缓存系统”实现。如果读者有兴趣,可以考虑使 用 JGroups(www.JGroups.org)框架实现这一功能。 MM 的服务器是开源代码,客户端有 Java、C++、PHP 等多种语言实现,读者可以对其进 行定制开发以解决以上提出的问题。 想更加深入地探究 MM,请登录如下网址。  MM 全面剖析:http://tech.idv2.com/2008/08/17/memcached-pdf/  MM 内存模型分析:http://phpcup.cn/viewthread.php?tid=45  MM 深度分析:http://funjackyone.javaeye.com/blog/128384  MM LRU 策略深入分析:http://www.javaeye.com/topic/225692  MM 详解: http://blog.csdn.net/heiyeshuwu/archive/2006/11/13/1380838.aspx  JGoups 集群通信框架: www.jgroups.org 62
还剩15页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 5 人已下载

下载pdf

pdf贡献者

aqc100

贡献于2012-03-02

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf