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

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 最新话题
Recorded Future
Recorded Future
月光博客
月光博客
博客园 - 【当耐特】
博客园 - 叶小钗
宝玉的分享
宝玉的分享
量子位
雷峰网
雷峰网
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
Vercel News
Vercel News
L
LangChain Blog
B
Blog
Y
Y Combinator Blog
爱范儿
爱范儿
GbyAI
GbyAI
S
Security @ Cisco Blogs
T
The Blog of Author Tim Ferriss
A
About on SuperTechFans
博客园 - Franky
P
Palo Alto Networks Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
Scott Helme
Scott Helme
I
Intezer
T
The Exploit Database - CXSecurity.com
MyScale Blog
MyScale Blog
T
Tor Project blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Troy Hunt's Blog
N
News and Events Feed by Topic
大猫的无限游戏
大猫的无限游戏
F
Fortinet All Blogs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
Security Affairs
Cyberwarzone
Cyberwarzone
PCI Perspectives
PCI Perspectives
小众软件
小众软件
D
DataBreaches.Net
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Know Your Adversary
Know Your Adversary
Forbes - Security
Forbes - Security
S
Securelist
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
The Last Watchdog
The Last Watchdog

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-20 · via Posts on WKLKEN THINKING

基于 3.10.0 版本

机制

0. 入口

在 apisix 的ngx_tpl.lua中

    -- 初始化
    init_worker_by_lua_block {
        apisix.http_init_worker()
    }

        -- balance
        balancer_by_lua_block {
            apisix.http_balancer_phase()
        }

1. 初始化

apisix/init.lua

local router          = require("apisix.router")

local load_balancer

function _M.http_init_worker()
    .....
    -- 这个什么都没做
    require("apisix.balancer").init_worker()
    -- 这个这个是核心逻辑
    load_balancer = require("apisix.balancer")
  

    .....
end

2. 执行入口

在 balance 阶段,进入http_balancer_phase

apisix/init.lua

function _M.http_balancer_phase()
    ......

    load_balancer.run(api_ctx.matched_route, api_ctx, common_phase)
end

进入

apisix/balancer.lua

function _M.run(route, ctx, plugin_funcs)
    local server, err

    if ctx.picked_server then
        -- use the server picked in the access phase
        server = ctx.picked_server
        ctx.picked_server = nil

        set_balancer_opts(route, ctx)

    else
        -- retry 相关,暂时忽略
        ......
    end

    local ok, err = set_current_peer(server, ctx)
    if not ok then
        return core.response.exit(502)
    end

    ctx.proxy_passed = true
end

3. api_ctx.picked_server

apisix/init.lua

实际上,在 http_access_phase 就已经做了 pick_server

function _M.http_access_phase()
      ......
      _M.handle_upstream(api_ctx, route, enable_websocket)
end

function _M.handle_upstream(api_ctx, route, enable_websocket)
    ......
    local server, err = load_balancer.pick_server(route, api_ctx)
    if not server then
        core.log.error("failed to pick server: ", err)
        return core.response.exit(502)
    end

    api_ctx.picked_server = server
    ......
  

4. load_balancer.pick_server

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 up_conf = ctx.upstream_conf
    
    local nodes_count = #up_conf.nodes
    -- 如果只有一个 node, 直接返回
    if nodes_count == 1 then
        local node = up_conf.nodes[1]
        ctx.balancer_ip = node.host
        ctx.balancer_port = node.port
        node.upstream_host = parse_server_for_upstream_host(node, ctx.upstream_scheme)
        return node
    end

    -- 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
        -- 从lrucache中获取,没有的话创建一个 create_server_picker
        server_picker = lrucache_server_picker(key, version,
                                               create_server_picker, up_conf, checker)
    end
    if not server_picker then
        return nil, "failed to fetch server picker"
    end

    -- 获取一个
    local server, err = server_picker.get(ctx)
    if not server then
        err = err or "no valid upstream node"
        return nil, "failed to find valid upstream server, " .. err
    end
    ctx.balancer_server = server
  
  
local function create_server_picker(upstream, checker)
    -- 根据配置加载对应的 负载均衡算法实现模块
    local picker = pickers[upstream.type]
    if not picker then
        pickers[upstream.type] = require("apisix.balancer." .. upstream.type)
        picker = pickers[upstream.type]
    end
  
    if picker then
        ......
        local server_picker = picker.new(up_nodes[up_nodes._priority_index[1]], upstream)

        return server_picker
    end  
  

the balance algorithm chash/ewma/least_conn/priority/roundrobin

以 roundrobin 为例 apisix/balancer/roundrobin.lua


function _M.new(up_nodes, upstream)
    local safe_limit = 0
    for _, weight in pairs(up_nodes) do
        -- the weight can be zero
        safe_limit = safe_limit + weight + 1
    end

    local picker = roundrobin:new(up_nodes)
    local nodes_count = nkeys(up_nodes)
    return {
        upstream = upstream,
        get = function (ctx)
            if ctx.balancer_tried_servers and ctx.balancer_tried_servers_count == nodes_count then
                return nil, "all upstream servers tried"
            end

            local server, err
            for i = 1, safe_limit do
                server, err = picker:find()
                if not server then
                    return nil, err
                end
                if ctx.balancer_tried_servers then
                    if not ctx.balancer_tried_servers[server] then
                        break
                    end
                else
                    break
                end
            end

            return server
        end,
        after_balance = function (ctx, before_retry)
            if not before_retry then
                if ctx.balancer_tried_servers then
                    core.tablepool.release("balancer_tried_servers", ctx.balancer_tried_servers)
                    ctx.balancer_tried_servers = nil
                end

                return nil
            end

            if not ctx.balancer_tried_servers then
                ctx.balancer_tried_servers = core.tablepool.fetch("balancer_tried_servers", 0, 2)
            end
            -- 标记 及 计数
            ctx.balancer_tried_servers[ctx.balancer_server] = true
            ctx.balancer_tried_servers_count = (ctx.balancer_tried_servers_count or 0) + 1
        end,
        before_retry_next_priority = function (ctx)
            if ctx.balancer_tried_servers then
                core.tablepool.release("balancer_tried_servers", ctx.balancer_tried_servers)
                ctx.balancer_tried_servers = nil
            end

            ctx.balancer_tried_servers_count = 0
        end,
    }
end

这几个函数在哪里被调用的?

  • get: 在pick_server(route, ctx) new 得到一个实例后, 立马get, local server, err = server_picker.get(ctx)并且执行赋值ctx.balancer_server = server
  • after_balance: 在 http_log_phase 阶段, 请求结束后, 调用的api_ctx.server_picker.after_balance(api_ctx, false); 另外, pick_server的retry过程中, 也会ctx.server_picker.after_balance(ctx, true)
  • before_retry_next_priority: balancer/priority.lua 中调用的, priority.get(ctx) -> picker.before_retry_next_priority; 在逐一确认priority的过程中, 调用这个函数