service与component实现分析

author:sufei

版本:mysql 8.0.30

说明:本文主要介绍mysql service服务以及component组件的实现逻辑,以及解决的问题。


一、插件的局限

我们都知道mysql使用插件引擎,从而支持不同的应用场景。同时,添加一个特性也会快速通过插件实现,方便扩展mysql服务的相关能力。但是插件机制存在一定的局限性。

  • 插件只能与mysql服务程序通信,但是无法进行插件之间相互交互;
  • 插件可以调用使用server服务暴露的任意符号,并且没有封装,存在安全隐患;
  • 插件需要合适的调用使用server层函数,需要对相关使用条件和限制有一定的了解;

从上面可以看到插件是强依赖server层代码的,需要对server层相关功能的实现细节有一定的了解。那如何解决这个问题呢?

目标:server层将提供给插件调用的相关功能进行封装,插件无需关注server层实现细节,只需要了解其相关借口即可正确使用(如合理初始化,合理调用);

针对上面的问题,mysql 5.7在mysql插件的基础上提出了service服务的概念,其本质上就是解偶plugin与server端,使得插件更加方便调用server端的相关功能。

下面我们来看一下service服务的实现。

二、service服务——解偶plugin与server端

首先我们来看一下官方文档中,对于插件中使用服务的流程说明:

服务与插件.png

服务其实就是mysql服务器暴露的一组函数功能供插件方便使用,同时也避免了插件任意调用内部函数造成故障。

2.1 service实现原理

下面是整个mysql services服务框架图


service框架.png

上面则是整个mysql 5.7引入的插件服务框架结构,具体说明如下:

  • 维护了一个全局唯一的服务列表结构体list_of_services,其中包含所有mysql提供的服务函数指针;
  • 每一个服务的头文件,必须是self-contained的,同时为了满足插件包含以及服务器包含两种场景;并且插件包含对应头文件时,并没有函数指针赋值真实的函数指针,仅仅是一个非有效的地址;

从上面我们对于服务框架有了简单的了解,那插件如何调用服务框架中的服务呢?

插件如何关联以及使用service呢?

插件使用服务的关键则是如何获得服务暴露出来的函数指针,以lock服务为例进行说

1、通过mysql/plugin.h->mysql/services.h->mysql/service_locking.h(所有服务的实现头文件)包含关系,将所有服务的头文件包含进来,其中声明了一个锁服务结构体的指针,并且也声明了相关暴露服务函数指针,如下

extern "C" struct mysql_locking_service_st {
  mysql_acquire_locks_t mysql_acquire_locks;
  mysql_release_locks_t mysql_release_locks;
} * mysql_locking_service;  // 1、定义了一个锁服务结构体脂针,后续通过该结构体内指针函数进行服务调用

#ifdef MYSQL_DYNAMIC_PLUGIN 
// 2、对于插件类型,声明相关函数接口,由于编译时期无法得到正确的锁服务结构体脂针,采用宏替代方式

#define mysql_acquire_locking_service_locks(_THD, _NAMESPACE, _NAMES, _NUM,  \
                                            _TYPE, _TIMEOUT)                 \
  mysql_locking_service->mysql_acquire_locks(_THD, _NAMESPACE, _NAMES, _NUM, \
                                             _TYPE, _TIMEOUT)
#define mysql_release_locking_service_locks(_THD, _NAMESPACE) \
  mysql_locking_service->mysql_release_locks(_THD, _NAMESPACE)

#else。                     // 3、如果是服务器,则声明相关服务实现接口

int mysql_acquire_locking_service_locks(
    MYSQL_THD opaque_thd, const char *lock_namespace, const char **lock_names,
    size_t lock_num, enum enum_locking_service_lock_type lock_type,
    uint64_t lock_timeout);

int mysql_release_locking_service_locks(MYSQL_THD opaque_thd,
                                        const char *lock_namespace);

2、在MYSQL_ADD_PLUGIN编译宏中添加libservices库


MYSQL_ADD_PLUGIN.png

3、在进行插件加载plugin_dl_add时,修正编译时锁服务结构体的指针指向真正的结构地址

  for (i = 0; i < array_elements(list_of_services); i++) { // 遍历所有的服务
    if ((sym = dlsym(plugin_dl.handle, list_of_services[i].name))) {
      uint ver = (uint)(intptr) * (void **)sym;
      if ((*(void **)sym) !=
              list_of_services[i].service && /* already replaced */
          (ver > list_of_services[i].version ||
           (ver >> 8) < (list_of_services[i].version >> 8))) {
        char buf[MYSQL_ERRMSG_SIZE];
        snprintf(buf, sizeof(buf), "service '%s' interface version mismatch",
                 list_of_services[i].name);
        mysql_mutex_unlock(&LOCK_plugin);
        mysql_rwlock_unlock(&LOCK_system_variables_hash);
        report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf);
        return nullptr;
      }
      *(void **)sym = list_of_services[i].service;  // 为对应的服务结构体指针赋值真正的服务有效地址
    }
  }

4、最后,可以直接在plugin中调用在对应服务头文件(mysql/service_locking.h)中声明的接口函数即可(此时相关函数已经能够找到真确的地址),如在version token插件中就可以直接使用锁服务进行token检查

case CHECK_VTOKEN: {
        char error_str[MYSQL_ERRMSG_SIZE];
        const char *token_name_cstr = token_name.str;
        if (!mysql_acquire_locking_service_locks(
                thd, VTOKEN_LOCKS_NAMESPACE, &(token_name_cstr), 1,
                LOCKING_SERVICE_READ, LONG_TIMEOUT) &&
            !vtokens_unchanged) {

2.2 实现一个service

下面来看一下如何在mysql服务器中国呢实现一个自己的服务,实现服务相对比较简单,首先你需要将实现文件放入sql文件夹,即让mysql服务器具备相关功能。代码也有相关指导

1、在include/mysql文件夹下实现自己服务的头文件(包含函数指针结构体,以及对外暴露的函数原型),且要求self-contained,主要作用是声明服务接口

作为一个c++规范——Self-contained头文件

头文件应该能够自给自足(self-contained,也就是可以作为第一个头文件被引入),以 .h 结尾。至于用来插入文本的文件,说到底它们并不是头文件,所以应以 .inc 结尾。不允许分离出 -inl.h 头文件的做法.

换言之,用户和重构工具不需要为特别场合而包含额外的头文件。

2、把服务头文件添加到include/mysql/services.h,从而使得插件能够获得相关的函数符号;

3、在include/service_versions.h定义一个自己服务的版本信息;

4、在libservices文件夹下,为插件定义一个对应的函数指针结构体,默认初始化为自己服务版本,后续在加载时会指向真正的实现函数;

5、在sql/sql_plugin_services.h中服务列表list_of_services中,注册自己的服务即可。

三、component组件——实现service动态注册

上面已经说明了服务的实现,下面看看服务还存在哪些问题呢?

  • 服务都是固定的,是否可以动态的添加服务呢?
  • 插件之间相互提供服务,并且调用是否可行?

mysql 8.0提供了component模块,将service进行了扩展,提供了一个反向service的注册能力。下面我们来看看其实现逻辑。

3.1 component架构

在mysql 8.0的服务列表中最后==有一个特殊的服务即plugin_registry_service(插件服务注册)==。该服务就是为了

  1. 让插件提供一个服务注册和注销接口;
  2. 能够获取到其他插件注册的服务;

下面来看一下服务注册的框架的实现,即component框架。不知道对于5.7服务框架是否还有映像,其中最后的plugin_registry_service提供了插件可以直接获取到全局组件服务。

component的其核心的代码框架在component/libminchassis文件夹中,下面是整体架构图


component框架.png

针对上图,几点说明:

  • 有关component提供的所有服务全部在静态变量service_registry中;
  • 在component组件框架初始化时,不仅将内部服务进行了注册加载,同时也为初始化了全局服务仓库指针srv_registry,为后续服务获取提供了全局地址,整体过程如下:
// 1、通过5.7传统的服务plugin_registry_service获得服务仓库地址
reg_srv = mysql_plugin_registry_acquire();
// 2、通过组建仓库地址,获取注册在仓库中的服务
reg_srv->acquire("keyring_reader_with_status",&h_keyring_reader_service)
  • mysql5.7版本提供服务存在在list_of_services结构体中,且固定不变;mysql 8.0在这个基础上构建了component组件,其核心结构体为mysql_registry_imp,其访问需要通过5.7版本的plugin_registry_service服务完成。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 面试必背 会舍弃、总结概括——根据我这些年面试和看面试题搜集过来的知识点汇总而来 建议根据我的写的面试应对思路中的...
    luoyangzk阅读 6,745评论 6 173
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • PostgreSQL 是一个免费的对象-关系(Object-Relationship)数据库服务器(ORDBMS)...
    LeoLongl阅读 3,014评论 0 1
  • 内容来之网络,因为看到布局太乱,重新整理下,并对答案做了一些修改。如果有侵权,请给我私信。谢谢。 1.下列哪些语句...
    蝴蝶之梦天使阅读 1,533评论 2 22