跳转至

连接处理

连接处理

宏观的来看连接处理只做两件事 1、接收连接请求并把它包装成 Channel_info 对象 2、创建新线程去执行 handle_connection 函数。

// sql/sql/conn_handler/connection_acceptor.h

// 事件循环
void connection_event_loop() {
    Connection_handler_manager *mgr =
        Connection_handler_manager::get_instance();
    // 无限循环下去
    while (!connection_events_loop_aborted()) {
      // 只要接收到连接就给到 mgr 来处理
      Channel_info *channel_info = m_listener->listen_for_connection_event();
      if (channel_info != nullptr) mgr->process_new_connection(channel_info);
    }
  }

// 单个连接的处理
void Connection_handler_manager::process_new_connection(
    Channel_info *channel_info) {
  if (connection_events_loop_aborted() ||
      !check_and_incr_conn_count(channel_info->is_admin_connection())) {
    channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true);
    delete channel_info;
    return;
  }

  // 事实上 add_connection 里面会负责为新的客户端连接创建对应的线程
  // 
  if (m_connection_handler->add_connection(channel_info)) {
    inc_aborted_connects();
    delete channel_info;
  }
}

// 创建一个线程让它去执行 handle_connection 函数, 函数的参数是 channel_info 对象(连接对象)
bool Per_thread_connection_handler::add_connection(Channel_info *channel_info) {
    // ...
    error =
      mysql_thread_create(key_thread_one_connection, &id, &connection_attrib,
                          handle_connection, (void *)channel_info);
    // ...
}


// 下面是一些于线程相关的技术细节了
// linux 平动上的话,最终用的是 pthread_create 来创建线程的
#define mysql_thread_create(K, P1, P2, P3, P4) \
  inline_mysql_thread_create(K, 0, P1, P2, P3, P4)

static inline int inline_mysql_thread_create(
  PSI_thread_key key [[maybe_unused]],
  unsigned int sequence_number [[maybe_unused]], my_thread_handle *thread,
  const my_thread_attr_t *attr, my_start_routine start_routine, void *arg) 
{
  // ...
  result = my_thread_create(thread, attr, start_routine, arg);
  return result;
}

int my_thread_create(my_thread_handle *thread, const my_thread_attr_t *attr,
                     my_start_routine func, void *arg) {
  // ...
  return pthread_create(&thread->thread, attr, func, arg);
  // ...
}

整体上看堆栈

从收到连接到创建工作线程,看堆栈是这样的。

(gdb) bt
0  my_thread_create (thread=0x7ffd9bd569f8, attr=0x8d2cda0 <connection_attrib>, func=0x58b3479 <pfs_spawn_thread(void*)>, arg=0xbed7ca0)
    at /data/repos/mysql-8.4.0/mysys/my_thread.cc:81
1  0x00000000058b3723 in pfs_spawn_thread_vc (key=5, seqnum=0, thread=0x7ffd9bd569f8, attr=0x8d2cda0 <connection_attrib>, 
    start_routine=0x37d9d2a <handle_connection(void*)>, arg=0xbd10b20) at /data/repos/mysql-8.4.0/storage/perfschema/pfs.cc:3097
2  0x00000000037d972a in inline_mysql_thread_create (key=5, sequence_number=0, thread=0x7ffd9bd569f8, attr=0x8d2cda0 <connection_attrib>, 
    start_routine=0x37d9d2a <handle_connection(void*)>, arg=0xbd10b20) at /data/repos/mysql-8.4.0/include/mysql/psi/mysql_thread.h:140
3  0x00000000037da364 in Per_thread_connection_handler::add_connection (this=0xb974aa0, channel_info=0xbd10b20)
    at /data/repos/mysql-8.4.0/sql/conn_handler/connection_handler_per_thread.cc:421
4  0x000000000392a7a1 in Connection_handler_manager::process_new_connection (this=0xb125ad0, channel_info=0xbd10b20)
    at /data/repos/mysql-8.4.0/sql/conn_handler/connection_handler_manager.cc:263
5  0x00000000033c2cfe in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0xb9d0ea0)
    at /data/repos/mysql-8.4.0/sql/conn_handler/connection_acceptor.h:66
6  0x00000000033b346c in mysqld_main (argc=6, argv=0x95163f8) at /data/repos/mysql-8.4.0/sql/mysqld.cc:9897
7  0x000000000339ec46 in main (argc=2, argv=0x7ffd9bd57448) at /data/repos/mysql-8.4.0/sql/main.cc:26