惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
G
Google Developers Blog
Latest news
Latest news
Know Your Adversary
Know Your Adversary
O
OpenAI News
腾讯CDC
月光博客
月光博客
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Help Net Security
Help Net Security
L
LINUX DO - 最新话题
雷峰网
雷峰网
AI
AI
Hacker News - Newest:
Hacker News - Newest: "LLM"
有赞技术团队
有赞技术团队
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
Google DeepMind News
Google DeepMind News
T
Tor Project blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hacker News: Ask HN
Hacker News: Ask HN
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
H
Heimdal Security Blog
I
Intezer
WordPress大学
WordPress大学
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy & Cybersecurity Law Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
V
V2EX
博客园 - 三生石上(FineUI控件)
G
GRAHAM CLULEY
Security Archives - TechRepublic
Security Archives - TechRepublic
F
Fortinet All Blogs
L
LangChain Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V2EX - 技术
V2EX - 技术
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
T
Tenable Blog
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
SecWiki News
SecWiki News
Cisco Talos Blog
Cisco Talos Blog

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming 缓存使用的一些经验 Better Code: 抽象: 可扩展性与可维护性的抉择 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 Go: 一种error wrap调用链处理方式 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 APUE笔记-第一章 UNIX基础知识 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考 Logstash+ElasticSearch+Kibana处理nginx访问日志
apisix 中的服务发现机制
2024-09-21 · via Posts on WKLKEN THINKING

基于 3.10.0 版本

机制

0. 入口

在 apisix 的ngx_tpl.lua中

    init_worker_by_lua_block {
        apisix.http_init_worker()
    }

apisix/init.lua

local router          = require("apisix.router")

function _M.http_init_worker()
    .....
    local discovery = require("apisix.discovery.init").discovery
    if discovery and discovery.init_worker then
        discovery.init_worker()
    end
    .....
end

1. discovery.init_worker

apisix/discovery/init.lua

local discovery_type = local_conf.discovery
local discovery = {}

if discovery_type then
    for discovery_name, _ in pairs(discovery_type) do
        log.info("use discovery: ", discovery_name)
        discovery[discovery_name] = require("apisix.discovery." .. discovery_name)
    end
end

function discovery.init_worker()
    if discovery_type then
        for discovery_name, _ in pairs(discovery_type) do
            discovery[discovery_name].init_worker()
        end
    end
end

根据配置文件中配置的服务发现类型,加载对应的模块,在 init_worker中调用对应模块的init_worker

假设配置文件 config.yaml 中我们配置的是 dns (最简单的实现,其他的实现原理上一一样的)

discovery:                      # Service Discovery
  dns:
    servers:
      - "127.0.0.1:8600"         # Replace with the address of your DNS server.
    resolv_conf: /etc/resolv.conf # Replace with the path to the local DNS resolv config. Configure either "servers" or "resolv_conf".
    order:                       # Resolve DNS records this order.
      - last                     # Try the latest successful type for a hostname.
      - SRV
      - A
      - AAAA
      - CNAME

2. apisix.discovery.dns

apisix/discovery/dns/init.lua

一共两个方法 (实现其他类型服务发现同理需要实现这两个方法)

  1. init_worker() 初始化,根据配置,构建一个 client
  2. nodes(service_name) 根据 service_name, 使用client做服务发现,返回 service_name 对应可用的 nodes
function _M.init_worker()
    ......
    local client, err = core.dns_client.new(opts)
    if not client then
        error("failed to init the dns client: ", err)
        return
    end

    dns_client = client
end

unction _M.nodes(service_name)
    local host, port = core.utils.parse_addr(service_name)
    core.log.info("discovery dns with host ", host, ", port ", port)

    local records, err = dns_client:resolve(host, core.dns_client.RETURN_ALL)
    if not records then
        return nil, err
    end

    local nodes = core.table.new(#records, 0)
    local index = 1
    for _, r in ipairs(records) do
        ......
    end

    return nodes
end

nodes示例

[
  {
    "host": "192.168.1.100",
    "port": 8761,
    "weight": 100,
    "metadata": {
      "management.port": "8761"
    }
  }
]

3. 调用点在哪里?

apisix/init.lua

local set_upstream    = apisix_upstream.set_by_route

-- from access phase
function _M.http_access_phase()
      ......
      _M.handle_upstream(api_ctx, route, enable_websocket)
end

function _M.handle_upstream(api_ctx, route, enable_websocket)
    ......
    local code, err = set_upstream(route, api_ctx)
    ......
end

apisix/upstream.lua

function _M.set_by_route(route, api_ctx)
    ......
    -- 如果 upstream 中有配置 service_name, 则需要服务发现
    if up_conf.service_name then
        ......
        -- 获取服务发现类型对应的实例
        local dis = discovery[up_conf.discovery_type]
        -- 根据service_name 获取 nodes
        local new_nodes, err = dis.nodes(up_conf.service_name, up_conf.discovery_args)
        -- 跟之前的比较是否一致
        local same = upstream_util.compare_upstream_node(up_conf, new_nodes)
        if not same then
            ......
            -- 这里设置了 新的节点
            up_conf.nodes = new_nodes
            up_conf.original_nodes = up_conf.nodes

            local new_up_conf = core.table.clone(up_conf)

            local parent = up_conf.parent
            if parent.value.upstream then
                -- the up_conf comes from route or service
                parent.value.upstream = new_up_conf
            else
                parent.value = new_up_conf
            end
            up_conf = new_up_conf
        end
    end

    local id = up_conf.parent.value.id
    local conf_version = up_conf.parent.modifiedIndex
    -- include the upstream object as part of the version, because the upstream will be changed
    -- by service discovery or dns resolver.
    set_directly(api_ctx, id, conf_version .. "#" .. tostring(up_conf), up_conf)
local function set_directly(ctx, key, ver, conf)
    ......
    -- 注意这里,更新了 api_ctx 的这几个字段
    ctx.upstream_conf = conf
    ctx.upstream_version = ver
    ctx.upstream_key = key
    return
end

apisix/balancer.lua

-- pick_server will be called:
-- 1. in the access phase so that we can set headers according to the picked server
-- 2. each time we need to retry upstream
local function pick_server(route, ctx)
    ......
    local version = ctx.upstream_version
    local key = ctx.upstream_key
    local checker = ctx.up_checker

    -- the same picker will be used in the whole request, especially during the retry
    local server_picker = ctx.server_picker
    if not server_picker then
        server_picker = lrucache_server_picker(key, version,
                                               create_server_picker, up_conf, checker)
    end
    ......

    local server, err = server_picker.get(ctx)

这里,如果服务发现导致 upstream_key/upstream_version 变化,那么意味着 server_picker 对应的 lrucache 会失效,进入 create_server_picker的逻辑

数据面服务发现的问题

如果服务发现获取上游的 node 数据变更非常频繁

  1. 会被对比出来,有差异
  2. 会多一些赋值
  3. 负载均衡lrucache会失效重建,重建时,如果配置有主动健康检查,还会触发主动健康检查获取健康的节点列表

这样会导致服务出现抖动。

基于控制面的服务发现

官方有一个基于控制面的服务发现项目 api7/apisix-seed: Do service discovery on the CP side

通过订阅 service_name的变更, 获取服务发现的变更,写入到etcd

相关文档