关于golang:服务树系列二开源项目streeindex-物化路径倒排索引实现的开源服务树核心组件

50次阅读

共计 5076 个字符,预计需要花费 13 分钟才能阅读完成。

开源我的项目地址:

我的项目地址: https://github.com/ning1875/stree-index

什么是服务树及其外围性能

服务树效果图
能够看我之前写的文章 服务树系列(一):什么是服务树及其外围性能

外围性能有三个

  • 树级构造
  • 灵便的资源查问
  • 权限相干

明天仅探讨前两种的实现

树级构造实现

调研后发现有下列几种实现形式

  • 左右值编码
  • 区间嵌套
  • 闭包表
  • 物化门路

stree-index 采纳的是物化门路

物化门路

原理

在创立节点时,将节点的残缺门路进行记录,计划借助了 unix 文件目录的思维,次要时以空间换工夫

+-----+-------+------------+-------------------------+
| id  | level | path       | node_name               |
+-----+-------+------------+-------------------------+
|  84 |     3 | /1/18/84   | ads-schedule            |
| 213 |     3 | /1/212/213 | share                   |
| 317 |     3 | /1/212/317 | ssr                     |
| 320 |     3 | /1/212/320 | prod                    |
| 475 |     3 | /1/212/475 | share-server-plus       |
| 366 |     3 | /1/365/366 | minivideo               |
| 368 |     3 | /1/365/368 | userinfo                |
+-----+-------+------------+-------------------------+

实现阐明

  • 接口代码在 E:\go_path\src\stree-index\pkg\web\controller\node-path\path_controller.go
  • level 字段代表层级 eg: a.b.c 对应的 level 别离为 2.3.4
  • path 门路 path 最初的字段为其 id 值,上一级为其父节点 id 值
  • node_name 叶子节点 name
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| level     | tinyint(4)   | NO   | MUL | NULL    |                |
| path      | varchar(200) | YES  |     | NULL    |                |
| node_name | varchar(200) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+
减少节点
  • 只须要判断传入的 g.p.a 各层级是否存在并增加即可
  • 为了防止过多查问,我这里应用 getall 后变更。TODO 改为事务型

删除节点 大体同减少
查问节点

须要分两种状况

  • 查问下一级子节点 即 依据获取 g 下 gp 列表
def get_gp():
    """

    :return:  返回申请的子节点
    t = ['hawkeye', 'stree', 'gateway']
    """data = {"node":"sgt","level": 2,"max_dep": 1}
    tree_uri = '{}/query/node-path'.format(base_url)
    res = requests.get(tree_uri, params=data)
    print(res.json())
  • 查问所有子孙节点
def get_node():
    """
    :return:  返回申请的子节点
    t = ['sgt.hawkeye.m3coordinator',
         'sgt.hawkeye.etcd',
         'sgt.hawkeye.collecter',
         'sgt.hawkeye.rule-eval',
         'sgt.hawkeye.query',
         'sgt.hawkeye.m3db',
         'sgt.stree.index',
         ]
    """data = {"node":"sgt","level": 2}
    tree_uri = '{}/node-path'.format(base_url)
    res = requests.get(tree_uri, params=data)
    print(res.json())

外围查问

如何满足灵便且高效的查问

惯例思路拼 sql 查问

比方: 查问 G.P.A=a.hawkeye.etcd 的 ecs 资源
拼出的 sql 相似

select * from  ecs where group="a" and product="hawkeye" and app="etcd"

同时 mysql 也满足上面 5 中查问条件

- eq 等于            : key=value 
- not_eq 不等于      : key!=value
- reg 正则匹配       : key=~value
- not_reg 正则非匹配 : key!~value
- 比照               : key> value

弊病

  • 须要拼接的 sql 中每个条件是 table 的字段
  • 当然常常变动的字段能够存 json 字段应用 tags->'$."stree-project"'='hawkeye'
  • 性能问题:单表字段过多导致的查问问题
  • 不能间接给出散布状况,只能再叠加 count

更好的办法是应用倒排索引缓存记录

什么是倒排索引

简略来说依据 id 查问记录叫索引,反过来依据 tag 匹配查找 id 就是倒排索引

具体实现
  • 外围构造 MemPostings 是一个双层 map,把 tag=value 的记录依照 tag 作为第一层 map 的 key,value 作为内层 map 的 key 内存 map 值为对应 id set
type MemPostings struct {
   mtx     sync.RWMutex
   m       map[string]map[string][]uint64
   ordered bool
}
  • 同时为了 反向匹配 统计需要 须要保护 values 和 symbols
type HeadIndexReader struct {
   postings *index.MemPostings
   values   map[string]stringset
   symbols  map[string]struct{}
   symMtx   sync.RWMutex
}
  • 将 db 的记录每条记录依照 tag 和 id 的对应关系构建索引
  • 最内层 set 存储的是 db 记录的主键 id
  • 这样就可能依据一组标签查问到主键 id 再去 db 中获取全量信息即可
  • 这样查问速度是最快的
  • db 中所有的字段出 timestamp 外都能够用来构建索引,而后能所有的字段都能够被用作查问条件

举例

    req_data = {
        'resource_type': 'elb',
        'use_index': True,
        'labels': [
            # 查问 group 不等于 CBS,name,正则匹配.*0dff.*,stree-app 等于 collecter 的 elb 资源列表
            {'key': 'group', 'value': 'CBS', 'type': 2},
            {'key': 'name', 'value': '.*0dff.*', 'type': 3},
            {'key': 'stree-app', 'value': 'collecter', 'type': 1}]
    }

按 key 查问散布状况的实现

  • 匹配过程和上述统一
  • 再用构建一个堆就能够失去散布状况

举例:依据 kv 组合查问 某一个 key 的散布状况

eg:  查问 G.P.A=SGT.hawkeye.m3db 的 ecs 资源按 cluster 标签散布状况
def query_dis():
    """

    :return:
    返回的是条件查问后依照指标 label 的散布状况
    dis = {
        'group': [{'name': 'business', 'value': 9},
            {'name': 'inf', 'value': 9},
            {'name': 'middleware', 'value': 9},
            {'name': 'bigdata', 'value': 9}
        ]
    }
    """req_data = {'resource_type':'ecs','use_index': True,'labels': [
            # 查问 G.P.A=SGT.hawkeye.m3db 的 ecs 资源按 cluster 标签散布状况
            {'key': 'group', 'value': 'SGT', 'type': 1},
            {'key': 'stree-project', 'value': 'hawkeye', 'type': 1},
            {'key': 'stree-app', 'value': 'm3db', 'type': 1}],
        'target_label': 'cluster'
    }
    query_uri = "{}/query/resource-distribution".format(base_url)
    res = requests.post(query_uri, json=req_data)
    print(res.json())

应用

创立表

依据 scripts/db_schema.sql 建表

资源数据表

  • ecs 云服务器
  • elb 云负载均衡器
  • rds 云关系型数据库
  • dcs 云缓存
  • 对应表名为

service_tree_ecs service_tree_elb service_tree_rds service_tree_dcs

ecs 云服务器规格表 service_tree_cloud_instance_type

树结构 path 表

灌入数据

  • 资源数据能够由同步得来,自行实现即可
  • 各个资源表中数据 tags 字段为 json 类型,切必须蕴含 stree-index.yml 的服务树 tag

    # g.p.a 模型 key 对应 table 中 json 字段名称
    tree_info:
     name_g: group
      name_p: stree-project
      name_a: stree-app

  • ecs 云服务器规格表 能够由 scripts/instance_type_insert.sh 灌入,其中蕴含华为和 aws 的大部分规格数据

装置 stree-index

git clone https://github.com/ning1875/stree-index.git 
cd  dynamic-sharding/pkg/ && go build   -o stree-index  main.go

补充 stree-index.yml 中 db,redis 等信息

启动服务

./stree-index --config.file=stree-index.yml

stree-index 会主动依据资源表中服务树 tag 构建服务树

查问 path 表应该有数据
select * from service_tree_path_tree limit 20;

应用 stree-index

查问接口数据结构

{
    "resource_type":"ecs",
    "use_index":true,
    "labels":[
        {
            "key":"group",
            "value":"sgt",
            "type":1
        }],
    'target_label': 'cluster' # 查问散布时才须要
}

查问资源返回数据结构

 {
        'code': 0,
        'current_page': 2, # 以后分页
        'page_size': 10,   # 每页 limit
        'page_count': 0,   # 页数
        'total_count': 0,  # 总数
        'result': [] # 为合乎查问条件的资源列表}

查问数据分页参数,传在 path 外面

  • page_size 代表每页数量,默认 10
  • current_page 代表以后分页 num,默认 1
  • get_all 等于 1 时代表不分页获取符合条件的全副数据,默认 0

查问资源类型: 对应字段 resource_type

目前反对的类型如下

  • ecs 云服务器
  • elb 云负载均衡器
  • rds 云关系型数据库
  • dcs 云缓存

查问条件反对: 对应字段 labels 中的 type 字段

  • 1:eq 等于 : key=value
  • 2:not_eq 不等于 : key!=value
  • 3:reg 正则匹配 : key=~value
  • 4:not_reg 正则非匹配 : key!~value

– 比照 : key> value

查问条件自由组合

labels可传入多个 key 和 value 组合,可自由组合不同 kv 查问

运维 stree-index

监控

stree-index 会打点,应用 prometheus 采集查看即可

  - job_name: stree
    honor_labels: true
    honor_timestamps: true
    scrape_interval: 60s
    scrape_timeout: 4s
    metrics_path: /metrics
    scheme: http
    static_configs:
    - targets:
      - $stree-index:9393

正文完
 0