KubeMQ
ConnectorsGoogle Cloud Pub/SubHow-to guides

Connectivity and emulator mode

How a Google Pub/Sub SDK reaches the connector — the PUBSUB_EMULATOR_HOST drop-in, per-language emulator opt-in, insecure gRPC, and the cluster caveat.

This guide covers how a Google Cloud Pub/Sub client reaches the connector: the standard PUBSUB_EMULATOR_HOST drop-in (the zero-code-change contract), the per-language emulator opt-in, the no-auth / insecure-gRPC posture, the cosmetic AdvertisedEndpoint dashboard hint, the gRPC-only constraint, and the sticky-load-balancer caveat for clusters.

The emulator protocol — zero code changes

The connector is a dedicated gRPC listener (default port 8085, the Pub/Sub emulator convention) inside KubeMQ. Every official Pub/Sub client library (Go, Python, Java, Node.js, C#, Ruby) and gcloud honour the standard PUBSUB_EMULATOR_HOST environment variable. When that variable is set, the SDK clears its Google credentials, skips Google auth, and dials insecure gRPC — exactly as it would against Google's own local emulator. Pointing an unmodified Pub/Sub application at KubeMQ therefore requires no code changes:

export PUBSUB_EMULATOR_HOST=localhost:8085   # connector default gRPC port; SDK uses the insecure path
export PUBSUB_PROJECT_ID=my-project          # any id; the project segment is parsed but ignored
# Some clients and gcloud also read this alias:
# export GOOGLE_CLOUD_PROJECT=my-project

There is no bespoke wrapper variable — the standard Google env var is the contract, and that zero-code-change drop-in is the connector's headline value proposition.

Project id is parsed but ignored. The connector validates the projects/{p} segment but is single-tenant (like the emulator): resource ids are global across "projects". Any project id works; topic orders is always Events Store channel gcp.orders regardless of project. See Channel mapping.

Per-language emulator opt-in

Most clients auto-detect the emulator from PUBSUB_EMULATOR_HOST with no extra code; two need an explicit flag or constructor argument:

LanguageConstruction (emulator)Auto-detect?
Gopubsub.NewClient(ctx, projectID) — reads PUBSUB_EMULATOR_HOST and dials insecurely.Yes
Pythonpubsub_v1.PublisherClient() / SubscriberClient() — honours the env var.Yes
Node/TSnew PubSub({ projectId }) — auto-detects the emulator from the env var.Yes
JavaPoint a plaintext ManagedChannel at the emulator host with NoCredentialsProvider when PUBSUB_EMULATOR_HOST is set.No — explicit channel
C#new PublisherServiceApiClientBuilder { EmulatorDetection = EmulatorDetection.EmulatorOnly }.Build() (or EmulatorOrProduction).No — EmulatorDetection
RubyGoogle::Cloud::PubSub.new(project_id: ENV["PUBSUB_PROJECT_ID"], emulator_host: ENV["PUBSUB_EMULATOR_HOST"]).No — emulator_host:

The three clients that need explicit emulator wiring are Java, C#, and Ruby:

// When PUBSUB_EMULATOR_HOST is set, build a plaintext channel and clear credentials.
String host = System.getenv("PUBSUB_EMULATOR_HOST"); // e.g. localhost:8085
ManagedChannel channel = ManagedChannelBuilder.forTarget(host).usePlaintext().build();
TransportChannelProvider channelProvider =
    FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel));
CredentialsProvider credentialsProvider = NoCredentialsProvider.create();

TopicAdminClient topicAdmin = TopicAdminClient.create(
    TopicAdminSettings.newBuilder()
        .setTransportChannelProvider(channelProvider)
        .setCredentialsProvider(credentialsProvider)
        .build());
// EmulatorDetection reads PUBSUB_EMULATOR_HOST and switches to the insecure path.
var publisher = await new PublisherServiceApiClientBuilder
{
    EmulatorDetection = EmulatorDetection.EmulatorOnly
}.BuildAsync();
# Ruby does not auto-detect the emulator — pass emulator_host: explicitly.
require "google/cloud/pubsub"

pubsub = Google::Cloud::PubSub.new(
  project_id: ENV["PUBSUB_PROJECT_ID"],
  emulator_host: ENV["PUBSUB_EMULATOR_HOST"], # e.g. localhost:8085
)

No auth, no TLS

The connector runs in emulator mode: no Google OAuth2/JWT validation, no IAM enforcement (the IAMPolicy RPCs are permissive stubs), and no TLS. The transport is insecure gRPC.

Do not expose port 8085 to untrusted networks. This no-auth, no-TLS posture is by design and matches Google's local emulator. Run it on a trusted network or behind your own perimeter. For the shared security model across connectors, see Auth & security.

DoS guards remain active even with no auth:

  • CONNECTORS_GCP_MAX_MESSAGE_BYTES — a gRPC receive-size body cap (default 10 MiB);
  • CONNECTORS_GCP_MAX_INFLIGHT_PER_SUBSCRIPTION — hard cap on leased (un-acked) messages per subscription (20,000);
  • CONNECTORS_GCP_MAX_CONCURRENT_POLLS — poller slot budget (1,024);
  • CONNECTORS_GCP_MAX_SEEK_REPLAY — max messages a single Seek may replay (1,000,000);
  • push-delivery backoff.

These knobs are documented in Configuration.

The advertised-endpoint hint

CONNECTORS_GCP_ADVERTISED_ENDPOINT (default empty) is a cosmetic host:port string the dashboard shows as the suggested PUBSUB_EMULATOR_HOST value. It does not change how the listener binds or behaves; set it to the externally reachable address (e.g. kubemq.mycompany.svc:8085) so operators copy the right host into their env.

gRPC only — no REST/JSON

The connector is gRPC-only. There is no REST/JSON v1 (grpc-gateway) surface. Clients and tools that speak only the Pub/Sub REST API will not work; use a gRPC client library or gcloud (which uses gRPC for the emulator). See Capabilities.

Periodic stream reconnect

A StreamingPull stream is closed by the server after CONNECTORS_GCP_STREAM_CLOSE_SECONDS (default 1800 s / 30 min) with UNAVAILABLE; client libraries transparently reconnect. This bounds per-stream resource lifetime and is normal — your receive callback keeps running across the reconnect. See Subscribing.

Sticky-stream caveat (cluster)

Node-local state needs a sticky load balancer. Topic / subscription / snapshot / schema records are synchronized across cluster nodes (a per-node replicated registry, last-writer-wins), but two pieces of delivery state are node-local:

  • Exactly-once ack_ids — an ack_id minted on one node is invalid on another (its node id won't match), so an exactly-once subscription's StreamingPull traffic must be pinned to one node;
  • StreamingPull leases / in-flight tracking — leased messages and flow-control counters live on the node that delivered them.

Cluster deployments must put a sticky load balancer (session affinity) in front of the connector so each subscriber sticks to one node for the lifetime of its in-flight messages. Single-node deployments are unaffected. See Reliability.

Message data itself is not replicated by the connector — it rides the existing Events Store / Queues replication.

Traffic gate

While the message broker is not ready, the traffic-gate interceptor short-circuits requests; on a not-ready → ready transition the connector drops all in-memory leases (their downstream transactions are dead) and the poller rebuilds. SDKs see a transient UNAVAILABLE and retry.

Was this page helpful?

On this page