- 浏览: 253980 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
leibnitz:
有几点要请教下;a.在二阶段里有这样一句:引用例如如果一个 p ...
zookeeper源码学习 -
nettm:
不错,我也遇到了第一个问题
mongodb客户端错误集合 -
lingqi1818:
xiaoych 写道很好,研究了一年多了吧,哈哈 难得你上 ...
80x86系统启动原理 -
xiaoych:
很好,研究了一年多了吧,哈哈
80x86系统启动原理 -
pengpeng:
pengpeng 写道很强大。我觉得mas-slave那块可以 ...
分布式计算需求场景以及解决方案
目录
本文主要对memcached服务器代码进行分析,这里对各种客户端的实现不做阐述。
原生的memcached是一款基于内存的缓存软件,存储的方式基于k/v.
Memcached的服务器主要依赖libevent库+多线程模型来实现核心逻辑。
为了更好的了解memcached的实现,我们首先需要知道下什么是libevent,它是用来干什么的。
Libevent是一个轻量级的开源高性能网络库, Libevent 有几个显著的亮点:
事件驱动(event-driven),高性能;
轻量级,专注于网络,不如ACE那么臃肿庞大;
源代码相当精炼、易读;
跨平台,支持Windows、Linux、*BSD和Mac Os;
支持多种I/O多路复用技术, epoll、poll、dev/poll、select和kqueue等;
支持I/O,定时器和信号等事件;
注册事件优先级;
Libevent已经被广泛的应用,作为底层的网络库;比如memcached、Vomit、Nylon、Netchat等等。
说了那么多,我们来看一个实际的例子:
int main(int argc, char **argv)
{
...
/* Setup listening socket */
event_set(&ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL);
event_add(&ev_accept, NULL);
/* Start the event loop. */
}
以上的例子构建了一个简单服务器。
假如listen_fd有数据可读,那么就会调用on_accept方法。
ev_init用于初始化一个事件环境
event_set和event_add方法分别用于设置和添加事件,event_dispatch则启动当前线程事件监听的主循环。
了解了这个之后,我们再来看一下memcached的多线程服务器模型。
说明:
Mthread:程序启动的主线程
Cthread:用于处理连接请求的分线程
eb:libevent的event_base指针。
Cq:连接队列,每个分线程都拥有一个连接队列。
从图中可以看到,整个服务器处理流程分为以下几个步骤:
1. 主线程建立新的连接,并把连接句柄交给请求队列。
2. 分线程从队列中取出连接数据,并进行处理。
这个地方memcached做了一个优化,因为加入一直没有数据进来,那么cthread就会一直空跑耗费性能,所以这边由管道来实现。
代码如下:
创建管道:
int fds[2];
if (pipe(fds)) {
perror("Can't create notify pipe");
exit(1);
}
threads[i].notify_receive_fd = fds[0];
threads[i].notify_send_fd = fds[1];
线程事件中的逻辑:
if (read(fd, buf, 1) != 1)//假如没有数据则一直阻塞
if (settings.verbose > 0)
fprintf(stderr, "Can't read from libevent pipe\n");
Memcached目前支持2种协议类型,ascii和binary,假如指定协商方式的话,由请求的第一位决定。
if ((unsigned char)c->rbuf[0] == (unsigned char)PROTOCOL_BINARY_REQ) {
c->protocol = binary_prot;
} else {
c->protocol = ascii_prot;
}
1.4.5之前的版本仅对ascii协议完全支持,binary的目前还未完全实现。
下面对协议的处理流程,以及协议的格式做一下阐述。
命令格式:
<command name> <key> <flags> <exptime> <bytes>\r\n
- <command name> 是 set, add, 或者 repalce |
|
- <key> 是接下来的客户端所要求储存的数据的键值 |
- <flags> 是在取回内容时,与数据和发送块一同保存服务器上的任意16位无符号整形(用十进制来书写)。客户端可以用它作为“位域”来存储一些特定的信息;它对服务器是不透明的。 |
- <exptime> 是终止时间。如果为0,该项永不过期(虽然它可能被删除,以便为其他缓存项目腾出位置)。如果非0(Unix时间戳或当前时刻的秒偏移),到达终止时间后,客户端无法再获得这项内容。 |
- <bytes> 是随后的数据区块的字节长度,不包括用于分野的“\r\n”。它可以是0(这时后面跟随一个空的数据区块)。 |
|
在这一行以后,客户端发送数据区块。 |
- <data block> 是大段的8位数据,其长度由前面的命令行中的<bytes>指定。 |
发送命令行和数据区块以后,客户端等待回复,可能的回复如下: |
表明成功. |
表明数据没有被存储,但不是因为发生错误。这通常意味着add 或 replace命令的条件不成立,或者,项目已经位列删除队列(参考后文的“delete”命令)。 |
整个命令的解析过程是由对conn的状态转变来维护的,conn的状态有以下几种:
enum conn_states {
conn_listening, /**< the socket which listens for connections */
conn_new_cmd, /**< Prepare connection for next command */
conn_waiting, /**< waiting for a readable socket */
conn_read, /**< reading in a command line */
conn_parse_cmd, /**< try to parse a command from the input buffer */
conn_write, /**< writing out a simple response */
conn_nread, /**< reading in a fixed number of bytes */
conn_swallow, /**< swallowing unnecessary bytes w/o storing */
conn_closing, /**< closing this connection */
conn_mwrite, /**< writing out many items sequentially */
conn_max_state /**< Max state value (used for assertion) */
下图以插入数据为例来说明状态的变化:
整个处理过程又一个循环+switch逻辑组成:
while (!stop) {
switch(c->state) {
case conn_listening:
addrlen = sizeof(addr);
if ((sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen)) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* these are transient, so don't log anything */
stop = true;
} else if (errno == EMFILE) {
if (settings.verbose > 0)
fprintf(stderr, "Too many open connections\n");
accept_new_conns(false);
stop = true;
} else {
perror("accept()");
stop = true;
}
break;
}
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("setting O_NONBLOCK");
close(sfd);
break;
}
dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
DATA_BUFFER_SIZE, tcp_transport);
stop = true;
break;
case conn_waiting:
if (!update_event(c, EV_READ | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
break;
}
conn_set_state(c, conn_read);
stop = true;
break;
case conn_read:
res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);
switch (res) {
case READ_NO_DATA_RECEIVED:
conn_set_state(c, conn_waiting);
break;
case READ_DATA_RECEIVED:
conn_set_state(c, conn_parse_cmd);
break;
case READ_ERROR:
conn_set_state(c, conn_closing);
break;
case READ_MEMORY_ERROR: /* Failed to allocate more memory */
/* State already set by try_read_network */
break;
}
break;
…
…
…
首先我们看图说话:
Memcached的内存由一块一块的slabclass组成的。
每个slabclass格式化为200个slab.
每个数据item按照大小匹配存放到slab中。Slab的大小按照factor发生递增。
我们可以看一下它的初始化方法:
if (prealloc) {
/* Allocate everything in a big chunk with malloc */
mem_base = malloc(mem_limit);
if (mem_base != NULL) {
mem_current = mem_base; mem_avail = mem_limit; } else { fprintf(stderr, "Warning: Failed to allocate requested memory in" " one large chunk.\nWill allocate in smaller chunks\n"); } } memset(slabclass, 0, sizeof(slabclass)); while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) { /* Make sure items are always n-byte aligned */ if (size % CHUNK_ALIGN_BYTES) size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); slabclass[i].size = size; slabclass[i].perslab = settings.item_size_max / slabclass[i].size; size *= factor; if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab); } } power_largest = i; slabclass[power_largest].size = settings.item_size_max; slabclass[power_largest].perslab = 1; if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab); } 五.参考文献 http://www.ibm.com/developerworks/cn/aix/library/au-libev/index.html?ca=drs-
评论
觉得很多的地方都没有讲。比如:当内存用完时,memcache采用什么样的策略?memcache处理hash冲突的解决办法?怎么知道数据的过期?memcache的CAP实现?
哈哈,这个只是在内部邮件组里的一个分享,只是讲讲原理,细节太多不好表述。。
觉得很多的地方都没有讲。比如:当内存用完时,memcache采用什么样的策略?memcache处理hash冲突的解决办法?怎么知道数据的过期?memcache的CAP实现?
PS:代码格式要是能调整一下就更好看了。
发表评论
-
spring mvc介绍
2014-06-29 18:18 861项目中用到了spring mvc,整理个文档给新手入门使用,欢 ... -
【转】GCC内嵌汇编
2012-07-26 15:37 1013http://wenku.baidu.com/view/58f ... -
汇编和C相互调用
2012-06-06 11:28 1030这里有几个原则: 1.调用者需要在调用前声明被调用者。 c的做 ... -
commons-io引起的ygc问题
2012-05-18 16:49 1350今天接到任务,图片上传服务器的性能有问题,高峰期间YGC频率在 ... -
mongodb客户端错误集合
2011-12-20 10:38 13562错误一: 调用代码: String map = "f ... -
jmeter java请求参数配置
2011-12-20 10:36 2176<JavaSampler guiclass=&quo ... -
openfire简介
2011-09-23 15:07 26946详细文章请下载附件。。。。。。 Openfire简介 ... -
计算机缓存漫谈
2011-06-27 16:36 1020见附件 见附件 见附件 -
va_list和vsnprintf
2011-06-22 15:40 1244http://blog.sina.com.cn/s/blog_ ... -
[转]关于SASL的介绍文档
2011-05-20 11:11 2http://docs. ... -
jetty服务器性能调整过程分析
2011-05-13 10:27 2656见附件 见附件 见附件 -
【转】“INT 21H”指令说明及使用方法
2011-04-07 14:29 2389很多初学汇编语言的同学可能会对INT 21H这条指令感到困 ... -
hbase-0.20.6数据写入服务端代码性能瓶颈分析
2011-03-29 16:33 1776目前我的实际配置是4台8核CPU,装4个regionServe ... -
再见c3p0
2011-03-28 16:24 1186c3p0已经很久不维护了,以后java数据库连接池的代码打算都 ... -
无侵入,系统性能监测程序,配置简单,欢迎下载
2011-03-21 09:51 2961本外挂主要目的是对系 ... -
keepalive的来龙去脉
2011-03-02 09:35 4769今天有同事反应在性能测试环境cpu load很高有500多,我 ... -
深入浅出IO程序设计—序
2011-02-24 10:31 1341作为一个程序员,除了 ... -
hbase&hadoop初探
2011-02-17 10:44 960见附件。。。 -
hbase海量数据的全量导入方法
2011-02-17 10:35 16398最近有个需求要对mysql的全量数据迁移到hbase,虽然hb ... -
服务端到手机端的推送方式
2011-02-11 11:13 13091.无连接的方式 前提条件服务端知道客户端的IP地址,并且客户 ...
相关推荐
Memcached源码分析之内存管理Memcached源码分析之内存管理
memcached 源码剖析笔记和源码。 Memcached 是一个自由、源码开放、高性能、分布式内存对象缓存系统,目的在于过减轻数据库负载来使动态 Web 应用程序提速。
在进行memcached源代码分析时,我们需要关注以下几个核心知识点: 1. **网络通信**:memcached 使用libevent库处理网络事件,这是一个非阻塞I/O模型,能够高效地处理大量的并发连接。libevent通过监听套接字,当有...
**memcached源代码详解** `memcached`是一个高性能的分布式内存对象缓存系统,它用于在动态系统中减少数据库负载,提升网站性能。这个压缩包包含的是`memcached`的源代码,允许开发者深入理解其内部工作原理,并...
《深入解析Memcached源代码——基于1.4.0版本》 Memcached,这个被广泛应用于众多知名互联网公司的分布式内存对象缓存系统,其源代码分析对于提升系统性能和理解其实现原理至关重要。本文将围绕以下几个核心部分...
**memcached源码分析** `memcached`是一个高性能、分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升应用性能。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站的响应速度。本文...
**memcached1.4.5源代码分析** `memcached`是一种高性能、分布式内存对象缓存系统,用于在Web应用程序中存储数据,以减少数据库负载。它的设计目标是减轻数据库的负担,通过缓存经常访问的数据来提高网络应用的响应...
通过对Memcached源码的深入分析,我们不仅理解了其核心功能是如何实现的,还掌握了如何更高效地使用它来提升Web应用的性能。了解这些内部细节对于优化应用程序的缓存策略以及故障排查具有重要意义。
3. **源码结构分析** 解压后的"memcached工具类源码"可能包含以下部分: - **Memcached客户端连接器**:初始化和管理到Memcached服务器的连接,如建立Socket连接,处理心跳机制,保持会话活跃。 - **Key-Value...
- **安装memcached**: 可以通过包管理器(如apt-get或yum)安装,也可以从源码编译安装。 - **启动memcached**: 启动命令通常为`memcached -m [memory] -p [port] -u [username] -l [ip_address]`,其中[memory]表示...
4. **Memcached与源码分析** - 源码阅读可以帮助开发者理解Memcached的内存管理、数据结构、网络通信等核心部分,为自定义扩展或优化提供基础。 - Memcached的内存管理采用了slab分配器,将内存划分为不同的slabs...
**Memcached Java源码分析——Performance分支** Memcached是一款高性能的分布式内存对象缓存系统,广泛应用于Web应用中,用于缓解数据库的负载。在Java环境中,我们常常使用Java客户端库来与Memcached服务器进行...
标题"memcached C++ 客户端 源码"表明了这是一个关于使用C++编写的memcached客户端的源代码库。memcached是一款高性能、分布式的内存对象缓存系统,常用于减轻数据库负载,提高Web应用性能。C++客户端则为开发者提供...
为了便于分析memcached的源码,使用doxygen生成了这个文档
在这个"易语言源码易语言Memcached协议客户端模块源码.rar"压缩包中,我们找到了一个易语言实现的Memcached协议客户端模块的源代码。Memcached是一种高性能、分布式内存对象缓存系统,常用于减轻数据库负载,提高...
Spring Memcached 是一个用于在Spring应用中集成Memcached缓存服务的框架。Memcached是一种分布式内存对象缓存系统,常用于提高网站数据读取...通过源码分析,我们可以了解其工作原理,并根据实际需求进行调整和优化。
安装Memcached通常涉及下载源代码,编译并安装二进制文件。在Unix-like系统上,这通常包括`./configure`, `make`, 和 `make install`等步骤。在某些操作系统上,还可以通过包管理器(如apt-get或brew)进行安装。 ...