Memcached 源码分析--网络模型流程分析

memcached源码分析-----网络模型 转载请注明出处: 半同步/半异步:         memcached使用半同步/半异步网络模型处理客户端的连接和通信。         半同步/半异步模型的基础设施:主线程创建多个子线程(这些子线程也称为worker线程),每一个线程都维持自己的事件循环,即每个线程都有自己的epoll,并且都会调用epoll_wait函数进入事件监听状态。每一个worker线程(子线程)和 阅读详情
一、功能介绍

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。
它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。
Memcached基于一个存储键/值对的hashmap。其守护进程(daemon)是用C写的,但是客户端可以用任何语言来编写,
并通过memcached协议与守护进程通信。

memcached缺乏认证以及安全管制,这代表应该将memcached服务器放置在防火墙后。

memcached的API使用32位元的循环冗余校验(CRC-32)计算键值后,将资料分散在不同的机器上。
当表格满了以后,接下来新增的资料会以LRU机制替换掉。

memcached是一款非常普及的服务器端缓存软件,memcached主要是基于Libevent库进行开发的

memcached服务器端并没有分布式功能、各个memcached不会互相通信以共享信息、完全取决于客户端的实现达到分布式。

研究版本:memcached-1.4.31

二、网络线程模型

主要涉及两个主要文件:memcached.c 和thread.c文件


主线程主要用于监听accpet客户端的Socket连接,而工作线程主要用于接管具体的客户端连接。
主线程和工作线程之间主要通过基于Libevent的pipe的读写事件来监听,当有连接连接上来的时候,主线程会将连接交个某一个工作线程去接管,后期客户端和服务端的读写工作都会在这个工作线程中进行。
工作线程也是基于Libevent的事件的,当有读或者写的事件进来的时候,就会触发事件的回调函数。


分析后的整体流程图解:


主线程负责客户端的套接字监听、当有新的连接到来时则均衡选择工作线程、工作线程负责读写并处理相应的命令。



main函数@memcached.c 文件了解整个构建逻辑


int main (int argc, char **argv) {
	/* handle SIGINT and SIGTERM */
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);

    /* init settings */
    settings_init();

    /* Run regardless of initializing it later */
    init_lru_crawler();
    init_lru_maintainer();

	hash_init(hash_type);
	
	/* initialize main thread libevent instance */
    main_base = event_init();

	/* start up worker threads if MT mode */
	// 这个方法主要用来创建工作线程,默认会创建8个工作线程
    memcached_thread_init(settings.num_threads);

	// 根据启动配置可以创建TCP/UDP协议套接字
	server_sockets(settings.port, tcp_transport, portnumber_file);
	server_sockets(settings.udpport, udp_transport, portnumber_file);

    /* enter the event loop */
    // 进入主线程的事件循环
	event_base_loop(main_base, 0);	
}


先看主线程的监听工作:

<pre name="code" class="cpp">server_sockets(settings.port, tcp_transport, portnumber_file);
-> server_socket(settings.inter, port, transport, portnumber_file);

/**
 * Create a socket and bind it to a specific port number
 */
static int server_socket(const char *interface,
                         int port,
                         enum network_transport transport,
                         FILE *portnumber_file) {
   
   // socket的bind、listen、setopt 等操作创建socket server
   socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   fcntl(sfd, F_SETFL, flags | O_NONBLOCK);
   setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
   setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
   setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
   setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
   bind(sfd, next->ai_addr, next->ai_addrlen);
   
   // state的类型为:conn_listening 监听类型套接字、那么在哪里进行accept.
   listen_conn_add = conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1,transport, main_base);                
}

conn *conn_new(const int sfd, enum conn_states init_state,...){
	// 这里首先会将监听套接字加入到libevent体系中,并设定event_handler事件回调函数
    event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
    event_base_set(base, &c->event);
    c->ev_flags = event_flags;
	event_add(&c->event, 0);
}

void event_handler(const int fd, const short which, void *arg) {
	drive_machine(c); // 异步fd编程最重要的状态机
}

// 
static void drive_machine(conn *c) {
	while (!stop) {
		case conn_listening: // 调用accept接受客户端的socket连接代码 
			sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);
			
			// 客户端用socket连接上来,则会调用这个分发逻辑的函数、分发到工作线程接管具体的读写操作
			dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST, DATA_BUFFER_SIZE, c->transport);
			break;
			
		case conn_read:
			IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);
			break;
		case conn_parse_cmd :
			try_read_command(c);
			break;
		case conn_write:
			add_iov(c, c->wcurr, c->wbytes);
			break;
		...			
	}
}



dispatch_conn_new函数@thread.c

void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags...){
	// 申请一块CQ_ITEM的内存块,用于存储连接的基本信息
	CQ_ITEM *item = cqi_new();
	
	// 通过求余数的方法来得到当前的连接需要哪个工作线程接管、且记录每次最后一次使用的工作线程
	// 通过最后记录就可以让工作线程进入一个轮询,保证了每个工作线程处理的连接数的平衡-相同连接数
	int tid = (last_thread + 1) % settings.num_threads;
    LIBEVENT_THREAD *thread = threads + tid;
    last_thread = tid;

	item->sfd = sfd;
    item->init_state = init_state;
    item->event_flags = event_flags;
    item->read_buffer_size = read_buffer_size;
    item->transport = transport;
	// 工作线程的队列中放入CQ_ITEM
    cq_push(thread->new_conn_queue, item);
	
	// 向工作线程的pipe中写入数据,只为唤醒工作线程
	buf[0] = 'c';
	write(thread->notify_send_fd, buf, 1);
}


总结一下主线程工作:
1、memcached首先在主线程中会创建main_base,memcached的主线程的主要工作就是监听和接收listen和accpet新进入的连接。
2、当用户有连接进来的时候,main thread主线程会通过libevent驱动在状态机中accept新的连接。
3、main thread主线程会通过求余的方式选择一个worker thread工作线程。

主线程处程流程图:




再看工作线程的工作:

在main中调用 memcached_thread_init 创建工作线程的函数:

<pre name="code" class="cpp">/*
 * Initializes the thread subsystem, creating various worker threads.
 *
 * nthreads  Number of worker event handler threads to spawn
 */
void memcached_thread_init(int nthreads) {
	for (i = 0; i < nthreads; i++) {
        int fds[2];
        // 创建pipe,主要用于主线程和工作线程之间的通信
        if (pipe(fds)) {
            perror("Can't create notify pipe");
            exit(1);
        }

		// 每个线程的LIBEVENT_THREAD基本数据结构
        threads[i].notify_receive_fd = fds[0];
        threads[i].notify_send_fd = fds[1];

		// 主要是创建每个线程自己的libevent的event_base 
        setup_thread(&threads[i]);
        /* Reserve three fds for the libevent base, and two for the pipe */
        stats_state.reserved_fds += 5;
    }
    
    /* Create threads after we've done all the libevent setup. */
    for (i = 0; i < nthreads; i++) {
        create_worker(worker_libevent, &threads[i]);
    }
}

/*
 * Set up a thread's information.
 */
static void setup_thread(LIBEVENT_THREAD *me) {
	/* Listen for notifications from other threads */
    event_set(&me->notify_event, me->notify_receive_fd, EV_READ | EV_PERSIST, thread_libevent_process, me);
    event_base_set(me->base, &me->notify_event);
	event_add(&me->notify_event, 0) ;
	
	// 初始化一个工作队列
	me->new_conn_queue = malloc(sizeof(struct conn_queue));
	cq_init(me->new_conn_queue);
}



在些注册回调函数 thread_libevent_process 用于数据读事件监控。create_worker@thread.c 就是调用 pthread_create 创建线程、执行worker_libevent函数实体。

/*
 * Worker thread: main event loop
 */
static void *worker_libevent(void *arg) {
	register_thread_initialized();

	// 每个线程中都会有自己独立的event_base和事件的循环机制、独立处理自己接管的连接。
    event_base_loop(me->base, 0);
}


创建工作线程的工作结束后、那么工作线程如何配合主线程工作的。主线程接收到accept新的连接后,
调用 dispatch_conn_new 分配一个工作线程并往队列中写入数据,利用管道唤醒工作线程函数 thread_libevent_process。

<pre name="code" class="cpp">/*
 * Processes an incoming "handle a new connection" item. This is called when
 * input arrives on the libevent wakeup pipe.
 */
static void thread_libevent_process(int fd, short which, void *arg) {
	// 主线程中如果有新的连接,会向其中一个线程的pipe中写入1
	read(fd, buf, 1);
	
	//从工作线程的队列中获取一个CQ_ITEM连接信息、如果item不为空,则需要进行连接后的接管。
    item = cq_pop(me->new_conn_queue); 
    if(item != NULL){
    	//conn_new这个方法非常重要,主要是创建socket的读写等监听事件。  
        //init_state 为初始化的类型,主要在drive_machine中通过这个状态类判断处理类型  
        conn *c = conn_new(item->sfd, item->init_state, item->event_flags,  
                           item->read_buffer_size, item->transport, me->base);  
    	...
    }
}




又通过conn_new函数介入到event_handler->drive_machine状态机中,如此就形成由数据事件驱动的完整运行态。


总结一下工作线程工作:
1、memcached启动的时候会初始化N个worker thread工作线程、默认是8个可配置。
2、worker thread工作线程和main thread主线程之间主要通过pipe来进行通信。
3、main thread会将当前用户的连接信息放入一个CQ_ITEM,并且将CQ_ITEM放入这个线程的conn_queue处理队列,然后主线程会通过写入pipe的方式来通知worker thread工作线程。
4、工作线程得到主线程通知,从自已的conn_queue队列中取得一条连接信息进行处理,创建libevent的socket读写事件
5、工作线程会监听用户的socket,如果用户有消息传递过来,则会进行消息解析和处理,返回相应的结果。

工作线程数据处理流程:




memcached源码分析-----set命令处理流程 转载请注明出处:         前一篇博文以get命令为例子把整个处理流程简单讲述了一遍,本篇博文将以set命令详细讲述memcached的处理流程。具体的命令为“set tt 3 0 10”,并假设当然memcached服务器没有名为tt的item。 读取命令:         在前一篇博文的最后,conn的状态被设置为conn_new_cmd,回 阅读详情

相关推荐

memcached源码分析-----get命令处理流程

转载请注明出处:         本文以get命令为例子,探讨memcached是如何处理命令的。本文只是探讨memcached处理命令的工作流程,具体的代码细节在不影响阅读的前提下能省略的就省略、能取默认值就取默认值、内存是足够的(不需要动态申请空间就够用了)。涉及到数组、缓存区的就假设已经分配好了。         现在假定memcached里面有了一个键值

luotuo44的专栏 3767

Memcached源码分析 - 源码分析之总结篇(8)

Memcached源码分析共8篇文章,前7篇文章主要分析每个模块的c源代码。这一篇文章主要是将之前的流程串起来,总结和回顾。同时通过这篇文章可以全局去看Memcached的结构。 之前7篇文章: 《Linux c 开发 - Memcached源码分析之基于Libevent的网络模型(1)》 《Linux c 开发 - Memcached源码分析之命令解析(2)》 《Linux c 开发 -

自娱自乐的代码人 1万+

memcached工具类源码

memcahed工具类源码,解压之后可直接在开发工具中运行,解压之后根据memcahed环境修改配置文件后可直接使用,代码里已经附带了各个方法的测试类

memcached 源码分析

1.Memcached概述 memcached是一个高性能的分布式内存缓存服务器,memcached在Linux上可以通过yum命令安装,这样方便很多,在生产环境下建议用Linux系统,memcached使用libevent这个库在Linux系统上才能发挥它的高性能。它的分布式其实在服务端是不具有分布式的特征的,是依靠客户端的分布式算法进行了分布式,memcached是一个纯内存型的数据库,这样在读写速度上相对来说比较快。 MemCache虽然被称为分布式缓存,但是MemCache本身完全.

魏言华的博客 2452

最新版Memcached for windows + 源码

最新版Memcached for windows + 源码,最新版Memcached for windows + 源码

Memcached源码剖析笔记

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。 Memcached是一个自由、源码开放、高性能、分布式内存对象缓存系统,目的在于通过减轻数据库负载来使动态Web应用程序提速。 Memcached是一个在内存中对任意的数据(比如字符串,对象等)所使用的key-value存储。数据可以来自数据库调用,API调用,或者页面渲染的结果。 Memcached设计理念就是小而强大,它简单的设计促进了快速部署、易于开发,并解决面对大规模的数据缓存的许多难题。所开放的API能用于大部分流行的程序语言

Memcached 源码分析--命令流程分析

一、执行命令 首先是启动memcached 自带参数如下: -p 设置TCP端口号(默认设置为: 11211) -U UDP监听端口(默认: 11211, 0 时关闭) -l 绑定地址(默认:所有都允许,无论内外网或者本机更换IP,有安全隐患,若设置为127.0.0.1就只能本机访问) -c max simultaneous connections

andyhuabing的专栏 2608

memcache源代码分析

memcached 源代码分析,出自社区大咖,货真价实,值得信赖。

Memcached源码解析(一)—网络模型

Memcached网络模型

yh88623131的博客 519

memcached源代码下载.rar

memcached源代码下载.rar

UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position 0-4: ordinal not in range(128)

python3报错处理:UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-4: ordinal not in range(128) 一、报错场景 // 报错代码 Traceback (most recent call last): File "test_autoo3_2.py", line 214, in <module> main() File "test_autoo3_2.py",

浪里小飞侠的博客 4201

Memcached源码分析 - 基于Libevent的网络模型实现(1)

                    目录 一、Memcached简介 二、Memcached分析 1. 网络模型流程分析 2. 网络模型数据结构 3. main启动入口 4. 工作线程worker thread 5. 主线程main thread 一、Memcached简介 memcached.

qq_45105731的博客 498

基于STM32的超声波流量计设计

本项目基于STM32微控制器设计了一种超声波流量计,采用时间差法测量液体或气体的流速,计算流量并通过显示屏实时显示,同时支持远程数据传输。本设计通过STM32微控制器实现了基于时间差法的超声波流量计,集成了超声波信号采集、流量计算、状态显示和远程通信功能。本系统需要实现超声波信号的实时采集与处理,计算流速和流量,并将数据通过OLED显示和无线模块上传。系统需具备高精度的时间测量能力和良好的抗干扰能力。通过计算超声波在顺流和逆流方向上的传播时间差,结合管道直径与流体特性,计算流速并进一步计算流量。

stm32d1219的博客 2349

Memcached源码分析之资源分享

这次的Memcached源码分享总共写了20篇文章,主要是记录自己看源码的一个过程,因为我们大脑不靠谱。在看源码过程中,已有的一些文章帮助很大,在学习了很多人的文章之后,自己分析源码,过程才没那么痛苦,下面就把我认为好的文章分享出来,这些文章涵盖了Memcached的各个方面,大家在学习Memcached的过程中可以参考。 Facebook对memcached的提升 Memcached源码分析...

lc的大脑备份 9805

RK3568 解决MIPI屏幕开机只显示uboot logo的问题

之前写过一篇文章介绍了RK3568驱动MIPI屏幕的方法,这次产品迭代,硬件改版,选型的新的屏幕了。所以再次开启调试屏幕的过程。本以为会和上次一样会轻车熟路,事情总是事与愿违,同样的路,遇到新的坑。

fhqlongteng的博客 5426

NXP ChessDE的安装和激活

如果客户申请的是试用版,那么一份为期 30 天的 License 将会发送申请人的账号或邮箱中,客户使用该文件进行安装即可。② 将“VENDOR snpslmd /path/to/snpslmd”中的“/path/to/snpslmd”替换成 snpslmd.exe 实际存放路径(位于 SCL 安装目录下的 win32\bin 文件夹中,默认安装的情况下就是“VENDOR snpslmd C:\Synopsys\SCL\<version>\win32\bin\snpslmd.exe”)。

weixin_47351160的博客 2044

Memcached源码分析 - 基于Libevent的网络模型实现(1)

文章列表: 《Memcached源码分析 - Memcached源码分析之基于Libevent的网络模型(1)》 《Memcached源码分析 - Memcached源码分析之命令解析(2)》 《Memcached源码分析 - Memcached源码分析之消息回应(3) 》 《Memcached源码分析 - Memcached源码分析之HashTable(4) 》 《Memcach

自娱自乐的代码人 1万+

AI智能体在零售行业的5大变革性应用

本文旨在全面介绍AI智能体在零售行业的关键应用场景,分析其技术原理和商业价值,帮助读者理解这些技术如何改变传统零售模式。文章将从核心概念入手,逐步深入五大应用场景的技术实现,最后探讨未来趋势和挑战。AI智能体:能够感知环境、做出决策并执行行动的智能软件实体机器学习:让计算机从数据中学习模式而无需显式编程的技术深度学习:基于神经网络的机器学习方法,能够处理复杂模式识别核心概念回顾AI智能体是零售数字化转型的关键驱动力从推荐系统到库存管理,AI正在重塑每个零售环节。

AI 原生应用开发的博客 1351

Memcached源码分析之基于Libevent的网络模型

memcached是一款非常普及的服务器端缓存软件,memcached主要是基于Libevent库进行开发的。Memcached分析 1.  网络模型流程分析 Memcached主要是基于Libevent的事件库来实现网络线程模型的。我们先需要下载memcached的源码包,上面我们已经给出了源码包下载地址。 Memcached的网络线程模型主要涉及两个主要文件:memcached.c 和t

woofilian的专栏 1934

改进yolov8|BackBone改进:PWConv、FasterNeXt替换yolov8主干网络

各位哥哥姐姐弟弟妹妹大家好,我是干饭王刘姐,主业干饭,主业2.0计算机研究生在读。和我一起来改进yolov8变身计算机大牛吧!本文中的论文笔记都是刘姐亲自整理,原创整理哦~

weixin_44379985的博客 749

性能调优秘籍——让OpenClaw运行速度提升10倍(2026优化版)

OpenClaw性能优化核心摘要 本文详细介绍了提升OpenClaw系统性能的关键技术,包括: 使用性能分析器进行代码级性能诊断 内存优化与高效缓存策略 并发处理优化技术 数据库查询性能提升方法 重点内容包括: 全面的性能分析工具实现(计时、内存监控、错误跟踪) 自动生成性能报告和优化建议 操作耗时与内存占用分析算法 关键性能指标(平均耗时、内存增长)计算 通过系统性的性能分析优化,可使OpenClaw运行速度提升10倍以上。

博客 903

Memcached源码分析-网络模型(1)

1 网络模型 Memcached采用了,单进程多线程的工作方式,同时采用了libevent事件驱动进行网络请求的处理。 2 工作原理 2.1 libevent介绍 libevent是一个事件驱动库,详细的工作原理,请看我的另外一篇博客的介绍,《libevent源码分析》:https://blog.youkuaiyun.com/CleverCode/article/details/82821548 2.2 网络...

CleverCode的博客 634

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Memcached源码分析 - 网络模型(1)

Memcached源码分析 - 网络模型(1)Memcached源码分析 - 命令解析(2)Memcached源码分析 - 数据存储(3)Memcached源码分析 - 增删改查操作(4)Memcached源码分析 - 内存存储机制Slabs(5)Memcached源码分析 - LRU淘汰算法(6)Memca...

weixin_34278190的博客 152

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

memcached分析之网络模型

memcache源码分析之网络模型 1.memcached使用libevent作为进行事件监听; 2.memcached往管道里面写的内容不是fd,而是一个简单的字符。 每一个worker线程都维护一个CQ队列,主线程把fd和一些信息写入一个CQ_ITEM里面,然后主线程往worker线程的CQ队列里面push这个CQ_ITEM。 接着主线程使用管道通知worker线程:“我已经发了一个新客户给你,你去处理吧”

heeyw的专栏 591

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

上一篇: Siege web 压测工具代码分析
下一篇: Memcached 源码分析--命令流程分析
andyhuabing
博客等级 码龄19年 704粉丝 165原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值