Memcached源码分析-主架构分析

Posted 一 铭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Memcached源码分析-主架构分析相关的知识,希望对你有一定的参考价值。

概述

本文通过阅读memcached的源代码,剖析了memcached的主流程处理的架构设计。包括主流程的线程模型和网络事件处理机制。 该架构通过一个主线程来分发客户端的连接任务,worker接管连接后负责处理客户端的各种请求。 注:本文是我在2013年写的一篇博客整理而写成的。

memcached简介

memcached是一个基于内存的缓存系统,具有很高的性能,广泛的使用于网站的各种内容的缓存。一般用它来缓存一些较小的内容(<1M),这是由它的内存限制和分配策略决定的。 memcached系统是利用libevent事件驱动库实现的一个多线程K/V缓存系统,支持TCP和UDP。

在memcached中,可以大致把线程分为两种:

  • 一种是分发线程(主线程)
  • 一种是worker线程,也就是进行后续命令处理的线程。

主架构实现分析

memcached的内部实现的架构,如下图所示:说明

主线程(main thread)会和每个worker线程都建立一个管道(pipe),当client连接memcached时,会先把连接请求发送给主线程,并和主线程完成连接的建立,让后主线程会选择一个管道,也就是选择了一个worker thread发送一个字符: ‘c’,并把创建一个新的连接实体放到连接队列中,此时阻塞在管道读区端的worker线程被唤醒,worker线程从连接队列中取出连接实体,并对完成连接的socket注册读事件处理函数,最后进入命令处理流程来处理client端的发送命令和各种连接异常的事件。

主线程

在memcached中主线程负责监听client端的连接请求,接收并创建和client的tcp连接。并选择一个worker线程来处理该client端的后续请求。

主线程初始化

初始化时memcached的主线程主要完成以下几项工作:

创建N个worker线程,并和每个线程创建一个双向管道(pipe)
为每个woker线程创建保存conn_queue_item对象的队列:new_conn_queue
为管道的读端fd(代码中的变量是:notify_receive_fd),注册读事件处理函数:thread_libevent_process
创建conn实体,并初始化状态为conn_listening
创建绑定(bind)的socket:sfd,并为该socket注册读事件处理函数:event_handler

worker线程

worker线程负责处理client端发送的命令,连接超时等。

worker线程初始化

worker线程的初始化完成的工作如下:

为管道的读fd:notify_receive_fd,注册读事件监听函数thread_libevent_process 该函数会阻塞在管道的notify_receive_fd描述符上进行读取。

事件处理

处理流程概要

处理客户端创建连接请求的处理流程如下:

主线程和客户端完成tcp连接的建立
主线程创建conn对象,并把该对象放到连接队列中
主线程向worker线程的管道中发送字符:’c’
worker线程从管道中读取命令’c’,并从连接队列中取出一个conn实体
worker线程创建一个新的conn实体,并把最新的sfd(已完成tcp连接的socket)读事件注册到event_handler
event_handler会调用drive_machine处理客户端的各种命令和事件

详细说明

当客户端向memcached发送连接请求时会触发sfd的读事件处理函数event_handler的执行。该函数会调用drive_machine函数,在该函数中完成与client端的tcp连接建立。

当主线程接收到客户端的连接请求时,会选择一个worker线程的管道,选择哪一个worker线程呢,规则如下:

int tid = (last_thread + 1) % settings.num_threads;
可以看到,其实是按轮训的方式来选择worker线程。这样可以保证每个worker线程服务的client的数量基本相同。

通过以上方式,选择好一个线程的管道后,创建一个conn_queue_item对象,并把该对象放到连接队列new_conn_queue中,然后向该管道发送’c’字符,该字符表示客户端要创建连接了。 此时会触发worker线程的管道读事件处理函数thread_libevent_process的执行,当worker线程的从管道中接收到该字符时,会从事件队列中取出conn_queue_item对象,根据该实体的信息创建一个新的conn实体,并为已经完成连接的socket描述,注册event_handler读事件处理函数。

此时已经由worker线程接管了client的连接,后续的客户端发送的命令都会由event_handler事件处理函数进行处理。

event_handler函数会最终调用driver_machine()函数来处理客户端所有的请求。

小结
通过该文的分析,我们知道memcached是通过一个线程池来处理客户端请求,和redis的单进程架构不一样。这种基于libevent的多线程架构,可以把client端的负载均匀分担到多个处理线程中,使用的还是比较多。

Memcached主主复制+Keepalived高可用架构(内含所有源码包)

初步了解memcached主主复制:

Memcached主主复制是指在任意一台memcached服务器修改数据都会被同步到另外一台,但是memcached API客户端是无法判断连接到哪一台memcached服务器的,所以需要设置VIP地址,提供给memcached API客户端进行连接。可以使用keepalived产生的VIP地址连接主memcached服务器,并且提供高可用架构。
Memcached的复制功能支持多个memcached之间进行相互复制(双向复制,主备都是可读可写的),可以解决memcached的容灾问题。

Memcached主主复制+Keepalived高可用架构

因为memcached主主复制这种架构,在程序连接时不知道应该连接哪个主服务器,所以需要在前端加上VIP地址,实现高可用架构。这里用Keepalived实现,因而Keepalived作用是用来检测memcached服务器的状态是否正常。
Keepalived不断检测memcached主服务器的11211端口,如果检测到memcached服务器发生故障,就会将VIP从主服务器移至从服务器,从而实现memcached的高可用性。

下面为memcached高可用结构图
技术分享图片

下面我通过实验进行说明
实验环境:
技术分享图片

软件包链接:https://pan.baidu.com/s/1fBNcsyrqNxxlORg8TyJk1g
提取码:hx7g
一、在主节点上进行配置
关闭防火墙

systemctl stop firewalld.service
setenforce 0

安装编译环境

yum install gcc gcc-c++ -y

解压所需安装包

tar zxvf libevent-2.1.8-stable.tar.gz -C /opt
tar zxvf memcached-1.5.6.tar.gz -C /opt

因为magenta下的文件需要进行更改,所以单独建一个目录用来解压

tar zxvf magent-0.5.tar.gz -C /opt/magent/ #解压过后便会出现以下四个文件
ketama.c
magent.c
ketama.h
Makefile

编译并且安装libevent

cd /opt/libevent-2.1.8-stable/
./configure --prefix=/usr
make && make install

编译并且安装memcached,需要指明libevent安装路径

cd ../memcached-1.5.6/
./configure --with-libevent=/usr
make && make install

对magent目录下的ketama.h进行配置

cd ../magent/
vim ketama.h
#ifndef SSIZE_MAX
#define SSIZE_MAX 32767 #将开头两句进行更改

对magent目录下的Makefile进行配置

vim Makefile
LIBS = -levent -lm #在开头这句话的后面加上-lm

文件配置完成后执行make

make
gcc -Wall -O2 -g -c -o magent.o magent.c
gcc -Wall -O2 -g -c -o ketama.o ketama.c
gcc -Wall -O2 -g -o magent magent.o ketama.o -levent -lm

将magent目录下配置好的文件复制到从服务器上的/usr/bin目录下,同时主服务器也要把该目录放到/usr/bin目录下,好让系统识别。接着安装openssh-clients这个工具,不然无法使用scp命令

yum install openssh-clients -y
cp magent /usr/bin/
scp magent [email protected]:/usr/bin/

技术分享图片

安装keepalived软件并编辑主服务器的keepalived配置文件

yum install keepalived -y
vim /etc/keepalived/keepalived.conf

在该配置文件中,除了保留第一个实例外,余下的122行可以全部删除

技术分享图片
技术分享图片

创建脚本

mkdir /opt/shell #注意这个路径,要和keepalived配置文件中指明的路径一致
vim /opt/shell/magent.sh
#!/bin/bash
k=ps -ef | grep keepalived | grep -v grep | wc -l
if [ $k -gt 0 ]; then
magent -u root -n 51200 -l 192.168.199.188 -p 12000 -s 192.168.199.129:11211 -b 192.168.199.128:11211
else
pkill -9 magent
fi

赋予脚本执行权限

chmod +x /opt/shell/magent.sh

开启keepalived服务

systemctl start keepalived.service

查看12000这个端口,这个端口启动时间会稍微慢一些,如果成功开启这个虚拟端口说明服务启动成功

netstat -ntap | grep 12000

技术分享图片

查看虚拟端口

ip addr

技术分享图片

二、在从节点上进行安装,注意从节点是不需要安装magent
关闭防火墙

systemctl stop firewalld.service
setenforce 0

安装编译环境

yum install gcc gcc-c++ -y

解压软件包

tar zxvf libevent-2.1.8-stable.tar.gz -C /opt
tar zxvf memcached-1.5.6.tar.gz -C /opt

编译并且安装libevent

cd /opt/libevent-2.1.8-stable/
./configure --prefix=/usr
make && make install

编译并且安装memcached

cd ../memcached-1.5.6/
./configure --with-libevent=/usr
make && make install

安装keepalived工具

yum install keepalived -y

因为在主节点上已经对keepalived配置文件进行了配置,所以这里只需要将主服务器上的配置文件复制过来稍作修改,在这之前需要先将从服务器的配置文件重命名,不然会有冲突

cd /etc/keepalived/
mv keepalived.conf keepalived.conf.bk
scp [email protected]:/etc/keepalived/keepalived.conf ./
vim keepalived.conf

技术分享图片

创建脚本,同样这里要注意路径

mkdir /opt/shell
vim /opt/shell/magent.sh

编写脚本

#!/bin/bash
k=ps -ef | grep keepalived | grep -v grep | wc -l
if [ $k -gt 0 ]; then
magent -u root -n 51200 -l 192.168.199.188 -p 12000 -s 192.168.199.129:11211 -b 192.168.199.128:11211
else
pkill -9 magent
fi

赋予脚本执行权限

chmod +x /opt/shell/magent.sh

开启服务

systemctl start keepalived.service

查看端口

netstat -ntap | grep 12000

技术分享图片

查看虚拟IP

ip addr

技术分享图片

在主从服务器上分别开启memcached服务

memcached -m 512k -u root -d -l 192.168.199.129 -p 11211 #先在主服务器上开启
netstat -ntap | grep 11211 #查看端口

memcached -m 512k -u root -d -l 192.168.199.128 -p 11211 #在从服务器上开启
netstat -ntap | grep 11211 #查看端口

三、测试
客户端:

yum install telnet -y
telnet 192.168.199.188 12000

技术分享图片

主节点:

yum install telnet -y
telnet 192.168.199.129 11211

在主节点上可以看到刚刚所创建的数据
技术分享图片

从节点:

yum install telnet -y
telnet 192.168.199.128 11211

在从服务器上同样可以看到这条数据,说明两台服务器之间已经实现了主主复制
技术分享图片

关闭主节点的keepalived

systemctl stop keepalived.service

可以看到,关闭keepalived服务后,虚拟端口也就消失了
技术分享图片

再次在客户端上对数据进行查看,可以发现并没有什么影响,所以这也体现了该架构的高可用性
技术分享图片

以上是关于Memcached源码分析-主架构分析的主要内容,如果未能解决你的问题,请参考以下文章

在LAMP架构中安装Memcached高性能内存对象缓存应用(内含所有源码包)

Memcached主主复制+Keepalived高可用架构(内含所有源码包)

Memcached源码分析

Memcached源码分析之内存管理

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

Memcached源码分析之memcached.h