{"id":22275858,"url":"https://github.com/zigzagak/ngx_zookeeper_lua","last_synced_at":"2025-07-28T16:32:26.943Z","repository":{"id":63301048,"uuid":"72950260","full_name":"ZigzagAK/ngx_zookeeper_lua","owner":"ZigzagAK","description":"Nginx zookeeper lua module","archived":false,"fork":false,"pushed_at":"2024-08-21T08:40:26.000Z","size":389,"stargazers_count":46,"open_issues_count":0,"forks_count":12,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-08-21T09:59:40.972Z","etag":null,"topics":["nginx","nginx-service-discovery","nginx-zookeeper","zookeeper"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ZigzagAK.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-11-05T20:03:51.000Z","updated_at":"2024-08-21T08:39:40.000Z","dependencies_parsed_at":"2023-11-30T22:22:43.551Z","dependency_job_id":"4babc2e8-61b4-47d6-a221-14fcd8bb23e1","html_url":"https://github.com/ZigzagAK/ngx_zookeeper_lua","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZigzagAK%2Fngx_zookeeper_lua","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZigzagAK%2Fngx_zookeeper_lua/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZigzagAK%2Fngx_zookeeper_lua/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZigzagAK%2Fngx_zookeeper_lua/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ZigzagAK","download_url":"https://codeload.github.com/ZigzagAK/ngx_zookeeper_lua/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227932606,"owners_count":17843136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nginx","nginx-service-discovery","nginx-zookeeper","zookeeper"],"created_at":"2024-12-03T14:12:38.973Z","updated_at":"2024-12-03T14:12:39.718Z","avatar_url":"https://github.com/ZigzagAK.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"Name\n====\n\nngx_zookeeper_lua - Lua bindings to interract with Zookeeper.\n\nBuild status\n============\n[![Build Status](https://travis-ci.org/ZigzagAK/ngx_zookeeper_lua.svg)](https://travis-ci.org/ZigzagAK/ngx_zookeeper_lua)\n\nTable of Contents\n=================\n\n* [Name](#name)\n* [Status](#status)\n* [Synopsis](#synopsis)\n* [Description](#description)\n* [Install](#install)\n* [Simple UI](#simple-ui)\n* [Configuration directives](#configuration-directives)\n* [Base methods](#methods)\n  * [connected](#connected)\n  * [get](#get)\n  * [childrens](#childrens)\n  * [set](#set)\n  * [create](#create)\n  * [delete](#delete)\n  * [delete_recursive](#delete_recursive)\n  * [tree](#tree)\n  * [watch](#watch)\n  * [watch_path](#watch_path)\n  * [watcher_exists](#watcher_exists)\n  * [unwatch](#unwatch)\n* [Additional API](#additional-api)\n  * [api.tree](#api-tree)\n  * [api.import](#api-import)\n\nStatus\n======\n\nThis library is production ready.\n\nDescription\n===========\n\nThis module provides Lua bindings to interract with Zookeeper.\n\n[Back to TOC](#table-of-contents)\n\nInstall\n=======\n\nBuild nginx with Zookeeper support.\nAll dependencies are downloaded automaticaly.\n\n```\ngit clone git@github.com:ZigzagAK/ngx_zookeeper_lua.git\ncd ngx_zookeeper_lua\n./build.sh\n```\n\nArchive will be placed in the `install` folder after successful build.\n\n[Back to TOC](#table-of-contents)\n\nSynopsis\n========\n\n```nginx\nhttp {\n  zookeeper                127.0.0.1:2181;\n  zookeeper_log_level      debug;\n  zookeeper_recv_timeout   5000;\n  zookeeper_ephemeral_node /services/nginx 127.0.0.1 \"nginx\";\n\n  lua_shared_dict config    64k;\n  lua_shared_dict zoo_cache 10m;\n\n  init_by_lua_block {\n    ngx.shared.config:set(\"zoo.cache.on\", true)\n    ngx.shared.config:set(\"zoo.cache.ttl\", 60)\n\n    ngx.shared.config:set(\"zoo.cache.path.ttl\", '[' ..\n      '{ \"path\" : \"/services/.*\", \"ttl\" : 0 }' ..\n   ']')\n  }\n\n  init_worker_by_lua_block {\n    assert(ngx.timer.at(1, function()\n      local zoo = require \"zoo\"\n      local cjson = require \"cjson\"\n\n      zoo.delete_recursive(\"/watched1\")\n      zoo.delete_recursive(\"/watched2\")\n\n      zoo.create(\"/watched1\")\n      zoo.create(\"/watched2\")\n\n      local function on_event(ctx)\n        local data = assert(zoo.watch(ctx.path, ctx.watcher_type, on_event, ctx))\n        ngx.log(ngx.INFO, \"on_event: \", ctx.path, \"=\", cjson.encode(data))\n      end\n\n      on_event {\n        watcher_type = zoo.WatcherType.DATA,\n        path = \"/watched1\"\n      }\n\n      on_event {\n        watcher_type = zoo.WatcherType.DATA,\n        path = \"/watched2\"\n      }\n\n      on_event {\n        watcher_type = zoo.WatcherType.CHILDREN,\n        path = \"/watched1\"\n      }\n\n      on_event {\n        watcher_type = zoo.WatcherType.CHILDREN,\n        path = \"/watched2\"\n      }\n\n      local stop\n\n      assert(ngx.timer.at(60, function()\n        assert(zoo.unwatch(\"/watched1\", zoo.WatcherType.DATA))\n        assert(zoo.unwatch(\"/watched1\", zoo.WatcherType.CHILDREN))\n        assert(zoo.unwatch(\"/watched2\", zoo.WatcherType.DATA))\n        assert(zoo.unwatch(\"/watched2\", zoo.WatcherType.CHILDREN))\n        ngx.log(ngx.INFO, \"unwatch\")\n        stop = ngx.now() + 10\n      end))\n\n      local i = 0\n\n      local function change(premature)\n        if premature or (stop and stop \u003c ngx.now()) then\n          return\n        end\n\n        pcall(function()\n          if zoo.connected() then\n            i = i + 1\n\n            assert(zoo.set(\"/watched1\", i))\n            assert(zoo.set(\"/watched2\", i))\n\n            if i % 2 == 1 then\n              assert(zoo.create(\"/watched1/1\"))\n              assert(zoo.create(\"/watched2/1\"))\n            else\n              assert(zoo.delete(\"/watched1/1\"))\n              assert(zoo.delete(\"/watched2/1\"))\n            end\n\n            ngx.log(ngx.INFO, \"update\")\n          end\n        end)\n\n        assert(ngx.timer.at(1, change))\n      end\n\n      assert(ngx.timer.at(1, change))\n    end))\n  }\n\n  server {\n    listen 8000;\n    zookeeper_register_port /services/nginx/8000 8000 \"nginx-8080\";\n\n    location / {\n      return 200 '8000';\n    }\n  }\n\n  server {\n    listen 8001;\n\n    location /a {\n      zookeeper_ephemeral_node /services/nginx/8001/a 127.0.0.1:8001;\n      return 200 '8001:a';\n    }\n\n    location /b {\n      zookeeper_ephemeral_node /services/nginx/8001/b 127.0.0.1:8001;\n      return 200 '8001:b';\n    }\n  }\n\n  server {\n    listen 12181;\n\n    include mime.types;\n    default_type application/json;\n\n    root html/zoo;\n\n    index index.html;\n\n    server_name zoo;\n\n    location = /get {\n      content_by_lua_block {\n        local zoo = require \"zoo\"\n        local cjson = require \"cjson\"\n        local value, stat, err = zoo.get(ngx.var.arg_znode)\n        ngx.say(cjson.encode(value and { value = value, stat = stat } or { error = err }))\n      }\n    }\n\n    location = /childrens {\n      content_by_lua_block {\n        local zoo = require \"zoo\"\n        local cjson = require \"cjson\"\n        local childs, err = zoo.childrens(ngx.var.arg_znode)\n        ngx.say(cjson.encode(childs and childs or { error = err }))\n      }\n    }\n\n    location = /set {\n      content_by_lua_block {\n        local zoo = require \"zoo\"\n        local cjson = require \"cjson\"\n        local stat, err = zoo.set(ngx.var.arg_znode, ngx.var.arg_value, ngx.var.arg_version)\n        ngx.say(cjson.encode(stat and { value = ngx.var.arg_value, stat = stat } or { error = err }))\n      }\n    }\n\n    location = /create {\n      content_by_lua_block {\n        local zoo = require \"zoo\"\n        local cjson = require \"cjson\"\n        local result, err = zoo.create(ngx.var.arg_znode, ngx.var.arg_value)\n        ngx.say(cjson.encode(result and { znode = result } or { error = err }))\n      }\n    }\n\n    location = /delete {\n      content_by_lua_block {\n        local zoo = require \"zoo\"\n        local cjson = require \"cjson\"\n        local ok, err = zoo.delete(ngx.var.arg_znode)\n        ngx.say(cjson.encode(ok and { znode = \"deleted\" } or { error = err }))\n      }\n    }\n\n    location = /tree {\n      content_by_lua_block {\n        local api = require \"zoo.api\"\n        local cjson = require \"cjson\"\n        ngx.say(cjson.encode(api.tree(ngx.var.arg_znode,\n                                      ngx.var.arg_stat and ngx.var.arg_stat:match(\"[1yY]\"))))\n      }\n    }\n\n    location = /import {\n      content_by_lua_block {\n        local api = require \"zoo.api\"\n\n        local method = ngx.req.get_method()\n        if method ~= \"POST\" and method ~= \"PUT\" then\n          ngx.exit(ngx.HTTP_BAD_REQUEST)\n        end\n\n        local content_type = ngx.req.get_headers().content_type\n\n        if not content_type or content_type:lower() ~= \"application/json\" then\n          ngx.exit(ngx.HTTP_BAD_REQUEST)\n        end\n\n        ngx.req.read_body()\n        local data = ngx.req.get_body_data()\n\n        local ok, err = api.import(ngx.var.arg_znode or \"/\", data)\n        if ok then\n          ngx.say(\"Imported\")\n        else\n          ngx.say(err)\n        end\n      }\n    }\n  }\n}\n```\n\n**Watch tree**\n\n```nginx\n  location / {\n    content_by_lua_block {\n      local zoo = require \"zoo\"\n      local cjson = require \"cjson\"\n\n      local function on_event(ctx, ev)\n        local data = assert(zoo.watch(ev.path, ev.watcher_type, on_event, ctx))\n        ngx.log(ngx.INFO, \"on_event: \", ev.path, \", type=\", ev.watcher_type, \" :\", cjson.encode(data))\n        if ev.watcher_type == zoo.WatcherType.CHILDREN then\n          for _,c in ipairs(data) do\n            if not zoo.watcher_exists(ev.path .. \"/\" .. c) then\n              assert(zoo.watch_path(ev.path .. \"/\" .. c, on_event, ctx))\n            end\n          end\n          ctx.data[ev.path] = data\n        end\n      end\n\n      local ctx = { [\"/test\"] = assert(zoo.childrens(\"/test\")) }\n      assert(zoo.watch_path(\"/test\", on_event, ctx))\n    }\n  }\n```\n\n[Back to TOC](#table-of-contents)\n\nSimple UI\n========================\nUI displays Zookeeper content.\nAvailable on `http://127.0.0.1:4444`\n\n![UI](zoo_ui.png)\n\n[Back to TOC](#table-of-contents)\n\nConfiguration directives\n========================\n\nzookeeper\n--------------\n* **syntax**: `zookeeper \u003csever1:port,sever2:port,....\u003e`\n* **default**: `none`\n* **context**: `http`\n\nConfigure Zookeeper servers.\n\nzookeeper_log_level\n--------------\n* **syntax**: `zookeeper_log_level \u003cnumber\u003e`\n* **default**: `error`\n* **values**: `error, warn, info, debug`\n* **context**: `http`\n\nConfigure Zookeeper log level.\n\nzookeeper_recv_timeout\n--------------\n* **syntax**: `zookeeper_recv_timeout \u003cnumber\u003e`\n* **default**: `10000`\n* **values**: `1-60000`\n* **context**: `http`\n\nConfigure Zookeeper socket recv timeout.\n\nzookeeper_node\n--------------\n* **syntax**: `zookeeper_node \u003cpath/to/node\u003e \u003cnode\u003e [data]`\n* **default**: `none`\n* **context**: `http,server,location`\n\nCreate persistent Zookeeper node.\n\nzookeeper_ephemeral_node\n--------------\n* **syntax**: `zookeeper_ephemeral_node \u003cpath/to/instances\u003e \u003cvalue\u003e [data]`\n* **default**: `none`\n* **context**: `http,server,location`\n\nRegister nginx in Zookeeper ephemeral node.\n\nzookeeper_register_port\n--------------\n* **syntax**: `zookeeper_register_port \u003cpath/to/instances\u003e \u003cport\u003e [data]`\n* **default**: `none`\n* **context**: `server`\n\nRegister nginx in Zookeeper ephemeral node with host_IPv4:port.\n\nzookeeper_inactive_time\n--------------\n* **syntax**: `zookeeper_inactive_time \u003cseconds\u003e`\n* **default**: `none`\n* **context**: `http`\n\nDisconnect from zookeeper after inactive period.\n\n[Back to TOC](#table-of-contents)\n\nMethods\n=======\n\nconnected\n---------\n**syntax:** `connected = zoo.connected()`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nReturn status of Zookeeper connection.\n\nReturns true or false.\n\nget\n---\n**syntax:** `value, stat, err = zoo.get(znode, nocache)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nGet value of the `znode` and znode information.\n`stat`: { czxid, mzxid, ctime, mtime, version, cversion, aversion, ephemeralOwner, dataLength, numChildren, pzxid }  \n\n`nocache=true`: bypass cache.  \n\nReturns value on success, or nil and a string describing an error otherwise.\n\nchildrens\n---------\n**syntax:** `childs, err = zoo.childrens(znode, nocache)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nGet child znode's names of the `znode`.  \n\n`nocache=true`: bypass cache. \n\nReturns table with znode's names on success, or nil and a string describing an error otherwise.\n\nset\n---\n**syntax:** `result, err = zoo.set(znode, value, version)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nSet value of the `znode`. Version may be nil (no version check). `value` may be a table (converted to json on store).\n\nReturns znode information on success, or nil and a string describing an error otherwise.\n\ncreate\n------\n**syntax:** `result, err = zoo.create(znode, value, mode)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nCreate the `znode` with initial `value`.\n`mode`: flags.ZOO_EPHEMERAL, flags.ZOO_SEQUENCE\n\nReturns new `znode` path on success, or nil and a string describing an error otherwise.\n\ndelete\n------\n**syntax:** `ok, err = zoo.delete(znode)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nDelete the `znode`.\n\nReturns true on success, or false and a string describing an error otherwise.\n\n[Back to TOC](#table-of-contents)\n\ndelete_recursive\n----------------\n**syntax:** `ok, err = zoo.delete_recursive(znode)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nDelete the `znode` with all childs.\n\nReturns true on success, or false and a string describing an error otherwise.\n\n[Back to TOC](#table-of-contents)\n\ntree\n----\n**syntax:** `data, err = zoo.tree(znode, need_stat)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nReturns subtree of znode, or false and a string describing an error otherwise\n\nwatch\n----------------\n**syntax:** `data, err = zoo.watch(znode, watch_type, callback, ctx)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nGet value or childrens and setup wather for `znode`.  \n\nwatcher_type MUST be one of `zoo.WatcherType.CHILDREN, zoo.WatcherType.DATA`.  \n\nReturns value/childrens on success, or nil and a string describing an error otherwise.  \n\nSee [Synopsis](#synopsis) for details.\n\n[Back to TOC](#table-of-contents)\n\nwatch_path\n----------------\n**syntax:** `tree, err = zoo.watch_path(znode, callback, ctx)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nGet full tree and setup watchers whole tree.  \n\nReturn tree on success, or nil and a string describing an error otherwise.  \n\nSee [Synopsis](#synopsis) for details.\n\n[Back to TOC](#table-of-contents)\n\nwatcher_exists\n----------------\n**syntax:** `flag = zoo.watcher_exists(znode, watch_type)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nCheck for watcher exists for `znode`.  \n\nwatcher_type MUST be one of `zoo.WatcherType.CHILDREN, zoo.WatcherType.DATA` or MAY be nil.  \n\nReturns true or false.  \n\nSee [Synopsis](#synopsis) for details.\n\n[Back to TOC](#table-of-contents)\n\nunwatch\n----------------\n**syntax:** `data, err = zoo.unwatch(znode, watch_type)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nRemove watcher for `znode`.\n\nwatcher_type MUST be one of `zoo.WatcherType.CHILDREN, zoo.WatcherType.DATA`.  \n\nReturns true on success, or nil and a string describing an error otherwise.  \n\nSee [Synopsis](#synopsis) for details.\n\n[Back to TOC](#table-of-contents)\n\nAdditional API\n==============\n\n`local api = require \"zoo.api\"`\n\napi tree\n--------\n**syntax:** `r = api.tree(znode, need_stat)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nReturns subtree of znode, or false and a string describing an error otherwise\n\napi import\n----------\n**syntax:** `r = api.import(root, json)`\n\n**context:** *\u0026#42;_by_lua\u0026#42;*\n\nImport znodes from json (format - api.tree). Overwrite existing values.\n\nReturns true on success, or false and a string describing an error otherwise.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzigzagak%2Fngx_zookeeper_lua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzigzagak%2Fngx_zookeeper_lua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzigzagak%2Fngx_zookeeper_lua/lists"}