mysql proxy学习(三)——体系结构

 Mysql proxy体系结构:多线程+libevent+glib2

从前面的内容我们已经确认了accept过程是由主线程完成的,并且它创建了一个con->client的与client连接的event;并且它接着创建一个con->server打算与后台mysql server进行连接。此时如果该fd不可写,则会在proxy_connect_server返回NETWORK_SOCKET_ERROR_RETRY,然后调用WAIT_FOR_EVENT(con->server, EV_WRITE, NULL);该宏完成一个非常重要的作用,部分代码如下:

#define WAIT_FOR_EVENT(ev_struct, ev_type, timeout) \
	event_set(&(ev_struct->event), ev_struct->fd, ev_type, network_mysqld_con_handle, user_data); \
	chassis_event_add(srv, &(ev_struct->event));

		case CON_STATE_CONNECT_SERVER:
			switch ((retval = plugin_call(srv, con, con->state))) {
			case NETWORK_SOCKET_SUCCESS:

				/**
				 * hmm, if this is success and we have something in the clients send-queue
				 * we just send it out ... who needs a server ? */

				if ((con->client != NULL && con->client->send_queue->chunks->length > 0) && 				     con->server == NULL) {
					/* we want to send something to the client */

					con->state = CON_STATE_SEND_HANDSHAKE;
				} else {
					g_assert(con->server);
				}

				break;
			case NETWORK_SOCKET_ERROR_RETRY:
				if (con->server) {
					/**
					 * we have a server connection waiting to begin writable
					 */
					WAIT_FOR_EVENT(con->server, EV_WRITE, NULL);
					NETWORK_MYSQLD_CON_TRACK_TIME(con, "wait_for_event::connect_server");
					return;
				} else {
					/* try to get a connection to another backend,
					 *
					 * setting ostate = CON_STATE_INIT is a hack to make sure
					 * the loop is coming back to this function again */
					ostate = CON_STATE_INIT;
				}

				break;
        …
我们可以看到WAIT_FOR_EVENT修改了它之前的event事件,设置回调函数为network_mysqld_con_handle,然后调用chassis_event_add
void chassis_event_add(chassis *chas, struct event *ev) {
	chassis_event_op_t *op = chassis_event_op_new();

	op->type = CHASSIS_EVENT_OP_ADD;
	op->ev   = ev;
	g_async_queue_push(chas->threads->event_queue, op);

	send(chas->threads->event_notify_fds[1], C("."), 0); /* ping the event handler */
}
可以看到这里将刚才修改的event通过glib2的g_async_queue_push接口push到chas->threads->event_queue这个队列里,然后向chas->threads->event_notify_fds[1]这个文件描述符写一个字符(还记得这会引起什么效果吗?——chassis_event_threads_init_thread),前面我们在初始化的时候创建了一对描述符[0]可读,[1]可写;并且可读的回调函数为:chassis_event_handle。而且所有的线程都监听该fd对应的事件,所以当当前线程send(chas->threads->event_notify_fds[1], C("."), 0)时,显然将唤醒所有其它线程(包括自己),并进入chassis_event_handle,现在我们也可以猜到该回调函数就要完成从chas->threads->event_queue取出刚才push的event,然后由它(现在运行的线程)来监听该event,这样下一刻当这个event可用时,将由该线程调用network_mysqld_con_handle进行当前状态的处理。代码如下:
void chassis_event_handle(int G_GNUC_UNUSED event_fd, short G_GNUC_UNUSED events, void *user_data) {
	chassis_event_thread_t *event_thread = user_data;
	struct event_base *event_base = event_thread->event_base;
	chassis *chas = event_thread->chas;
	chassis_event_op_t *op;
	char ping[1024];
	guint received = 0;
	gssize removed;

	while ((op = g_async_queue_try_pop(chas->threads->event_queue))) {
		chassis_event_op_apply(op, event_base);

		chassis_event_op_free(op);

		received++;
	}

	/* the pipe has one . per event, remove as many as we received */
	while (received > 0 && 
	       (removed = recv(event_thread->notify_fd, ping, MIN(received, sizeof(ping)), 0)) > 0) {
		received -= removed;
	}
}
这里看起来有点像“惊群”的现象,但其实这是proxy故意这样的,因为当有多个client con并发的时候,此时会有多个不同的con的不同状态,它们分别等待进入各自的下一个状态,此时惊群就可以使得所有的线程分别去取当前队列的第一个event,也就是说这样可能使每个线程都同时去处理不同的con。【有点儿晕了,其实就是想让每个线程都跑起来(有说等于没说)】,整个mysql proxy的框架及主流程如下图所示:

图2 mysql proxy体系结构

从图2,我们可以看到mysql proxy的大致启动流程,以及系统中最重要的三个回调函数:network_mysqld_con_accept、chassis_event_handle、network_mysqld_con_handle;以及它们调用的时机。

你可能感兴趣的:(mysql学习)