Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Etcd Discovery Laravel Package

teamones/etcd-discovery

A Laravel package that integrates etcd for service registration and discovery, letting applications dynamically locate and connect to available service instances. It simplifies distributed service configuration and load-balanced endpoint resolution.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites: PHP 8.x, Composer, a running etcd v3 cluster (single node is fine for dev: etcd --advertise-client-urls http://127.0.0.1:2379 --listen-client-urls http://127.0.0.1:2379), and a Workerman-based runtime (Workerman, GatewayWorker, webman, or a custom Worker). Confirm etcd is reachable before touching the package:

etcdctl --endpoints=http://127.0.0.1:2379 endpoint health
etcdctl --endpoints=http://127.0.0.1:2379 get --prefix /

Install:

composer require teamones/etcd-discovery

Where to look first:

  1. README.md — the package is Chinese-first and README-driven; class names, config array keys, and the exact getInstance/client factory vary between releases. Read this before trusting any snippet below.
  2. src/ — look for the client wrapper (likely EtcdClient/Client), a discovery/resolver class, and a config file or config/etcd.php-style default.
  3. composer.json — check whether it pulls workerman/workerman, grpc/grpc, or a raw HTTP client. That single detail determines whether calls are coroutine-safe and whether you need the etcd gRPC gateway enabled (--enable-grpc-gateway) for the HTTP v3 JSON API.

First use case — register one service, discover it from another:

// bootstrap: register this worker's address after the worker forks
use Teamones\Etcd\EtcdClient;   // verify exact FQCN in README

$etcd = EtcdClient::getInstance([
    'endpoints' => ['http://127.0.0.1:2379'],
    'ttl'       => 10,
]);

$key   = '/services/order-api/' . $worker->getWorkerId();
$value = json_encode(['host' => '127.0.0.1', 'port' => 8081]);

$etcd->put($key, $value, $ttl);       // leased registration
$etcd->keepAlive($key, $ttl);          // or a lease ID you hold onto

// from another process
$instances = $etcd->getPrefix('/services/order-api/');

Once /services/order-api/... shows up in etcdctl get --prefix /services, you have the full loop working. Everything else is lifecycle hygiene around those two calls.

Implementation Patterns

Register on onWorkerStart, never in the parent process. Workerman forks workers. A registration performed before fork gives every child the same key and a lease owned by a process that may not be the one serving traffic. Do it inside onWorkerStart, and key by worker->id or by a stable host:port so restarts overwrite rather than duplicate. Deregister in onWorkerStop/onWorkerReload and also handle SIGTERM — a graceful revoke/delete is what keeps discovery clean during rolling deploys.

Always pair keys with leases. A plain put with no TTL leaves a permanent ghost entry after a crash. Prefer a lease with TTL ≈ 3–5× your keepalive interval, and run keepalive on a Workerman\Timer (e.g. every ttl/3 seconds) rather than a busy loop. If the process is frozen longer than the TTL, the key disappears — which is usually the behaviour you want, but budget for it.

Watch, don't poll, for membership. Polling every second across dozens of services is the fastest way to melt a small etcd. Use the package's watcher/watch call inside onWorkerStart and keep an in-memory list of instances; update it on events. If the package only exposes a blocking watch, run it in a dedicated Worker process (one per cluster, not one per request handler) and publish changes through shared memory or a local cache that other workers read.

Centralize a resolver, don't sprinkle keys. Wrap the client in your own thin ServiceRegistry class exposing register(), deregister(), instances($name), and pick($name). This isolates the package's API surface — which, at 10 stars and a single maintainer, is the most likely thing to churn — and gives you one place to add caching and load balancing. Round-robin or random selection over the cached instance list is fine; a health-aware picker is overkill until you have real failure data.

Namespace your keys and encode enough metadata. Follow a /env/service/instance convention (/prod/order-api/10.0.0.5:8081) so a shared etcd doesn't mix staging and production. Store JSON values (host, port, weight, version, started_at) rather than bare addresses — you will want weighted routing and canary splits eventually, and retrofitting the value format means touching every producer.

Integrate at the edges. In webman, register in config/process.php-defined worker onWorkerStart callbacks and expose the resolver via a container binding. In GatewayWorker, register the Gateway/BusinessWorker addresses so gateways can find business workers without a static config file. Keep registration out of HTTP request handlers — it belongs to process bootstrap.

Multi-endpoint config. Pass all etcd endpoints in the config array rather than one. Even if the client only round-robins naively, it turns a single-node restart into a transient error instead of an outage.

Gotchas and Tips

Fork safety is the top pitfall. Any etcd connection, lease, or watcher created before Worker::runAll() is shared across forked children and will behave unpredictably. Create the client lazily inside onWorkerStart, and if the package uses a static singleton, verify it isn't inherited from the master.

Watch out for v2 vs v3 and transport mismatch. The description says "PHP version based on Workerman" — many such packages target the etcd v3 HTTP JSON gateway rather than gRPC. If you see 404s on /v3/kv/..., the gateway isn't enabled on your etcd (--enable-grpc-gateway). If you see connection resets, check TLS and whether the client expects https:// endpoints. Confirm which API the package speaks before debugging application code.

A blocking client in the event loop stalls everything. If the underlying HTTP call is synchronous, a slow or unreachable etcd blocks the worker for the full timeout. Set an explicit connect/request timeout (1–2s) in the config, and never let registration failure prevent the worker from starting — log it and continue serving, then retry on a timer. Failing closed on etcd unavailability turns a control-plane blip into a full outage.

Leases leak if you only put. keepAlive on a key that was written without a lease is a no-op in most clients; keep the lease ID returned by the write and pass that around. On deregistration, revoke the lease, not just delete the key — otherwise the lease survives until TTL expiry and any orphaned keepalive timer keeps it alive.

Stale timers after reload. onWorkerReload restarts workers; timers registered in the old process can keep firing or hold references that prevent clean shutdown. Clear timers explicitly and re-register on the new onWorkerStart.

Stale/duplicate keys under aggressive restarts. With short TTLs, a restarted worker can register its new key while the old one is still within TTL, briefly doubling capacity in the instance list. Keying by host:port (stable across restarts) instead of a random UUID avoids this; if you need per-process keys, filter by started_at.

Debug with etcdctl, not with your app. etcdctl get --prefix /services/ --keys-only and etcdctl lease list / etcdctl lease timetolive <id> tell you instantly whether the problem is registration, keepalive, or your resolver. Add a debug endpoint that dumps the resolver's cached instance list — nine times out of ten the app is fine and the cache is stale.

Small-package risk. At 10 stars, 0 dependents, and a single maintainer, treat the API as unstable: pin an exact version in composer.json, read the diff before upgrading, and keep your ServiceRegistry wrapper thin enough that swapping in etcdctl-backed shelling-out or a hand-rolled v3 HTTP client is a one-file change. Vendor-fork it if you need a fix — you are unlikely to get one upstream quickly.

Extension points worth using. A custom key prefix builder, a pluggable load-balancing strategy, and a cache TTL for the instance list are the three seams that make this package usable across projects. If the package doesn't expose them, add them in your wrapper rather than patching vendor/.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core