# Linkis Label-Based Routing: How It Works and How to Configure Custom Route Labels for Multi-Tenant Traffic

> Learn how Linkis label-based routing works and configure custom route labels for multi-tenant traffic. Linkis routes requests by matching route labels, ensuring efficient traffic distribution.

- Repository: [The Apache Software Foundation/linkis](https://github.com/apache/linkis)
- Tags: deep-dive
- Published: 2026-02-24

---

**Linkis routes HTTP requests to backend service instances by matching route labels parsed from query parameters or request bodies against labels registered in the Instance-Label Service, falling back to unlabeled instances when no match exists.**

Apache Linkis provides a sophisticated label-based routing mechanism that enables multi-tenant traffic isolation by directing requests to specific service instances based on custom metadata labels. This system allows organizations to separate development, production, and testing workloads without modifying gateway code, as implemented in the `apache/linkis` repository.

## How Linkis Label-Based Routing Works

The routing flow traverses six distinct phases, from initial request parsing to final instance selection. Understanding this pipeline is essential for configuring custom route labels effectively.

### Step 1: Parsing Route Labels from Requests

The `GatewayRouterConfiguration` creates a `DefaultLabelGatewayRouter` that orchestrates the routing process. This router maintains a list of `RouteLabelParser` implementations responsible for extracting labels from incoming HTTP requests.

According to the source code in [`DefaultLabelGatewayRouter.scala`](https://github.com/apache/linkis/blob/main/DefaultLabelGatewayRouter.scala), parsers read both **query parameters** (`labels` or `labelsRoute`) and **request body JSON** to construct a `java.util.List[RouteLabel]`. The `DSSRouteLabelParser` handles query parameter extraction, while the generic parser processes JSON payloads.

### Step 2: Converting Parsed Data to RouteLabel Objects

Once raw label data is extracted, the `GenericRoueLabelParser` (note the typo in the source filename) converts JSON representations into concrete `Label` objects via the `LabelBuilderFactory`. Only objects implementing `org.apache.linkis.manager.label.entity.route.RouteLabel` are retained for subsequent routing decisions.

This conversion ensures type safety and validates that labels intended for routing actually support the routing interface.

### Step 3: Searching Candidate Instances by Label

The `AbstractLabelGatewayRouter.route` method delegates instance discovery to `insLabelService.searchInstancesByLabels(routeLabels)`. The **Instance-Label Service**, implemented by `InsLabelServiceAdapter`, maintains a mapping of `labelKey → labelValue → ServiceInstance` in its registry.

This service acts as the central directory for all label-to-instance relationships in the Linkis cluster.

### Step 4: Fallback to Default Instances

When requests lack route labels or when no instances match the specified criteria, the system invokes `getDefaultInstances(applicationName)`. This method retrieves all RPC-registered instances and filters out any that have label-based registrations, leaving only the "unlabeled" pool for fallback routing.

This ensures that legacy clients or unconfigured tenants still receive service through a default instance group.

### Step 5: Instance Selection via Roulette

`DefaultLabelGatewayRouter.selectInstance` implements the final selection logic. The method first checks for an explicit `serviceInstance` in the request context. If absent, it filters candidates by `applicationName` (when supplied) and executes a **roulette** selection using `Random.nextInt` over the remaining instances.

The roulette also validates instance liveness by calling `retainAllInRegistry`, removing any candidates not currently registered in the service registry.

### Step 6: Request Forwarding

After selection, the chosen `ServiceInstance` (containing host and port information) is injected into `gatewayContext.getGatewayRoute.setServiceInstance`. The gateway then forwards the HTTP request to the selected backend instance, completing the routing cycle.

## Core Routing Concepts

Understanding these fundamental concepts is crucial for implementing multi-tenant routing:

- **Route Label Key**: The constant `LabelKeyConstant.ROUTE_KEY` with value `"route"` identifies labels used for routing decisions.
- **Route Label Value**: Tenant-oriented identifiers such as `dev`, `prod`, or `test` that distinguish traffic streams.
- **Label Service**: The `InsLabelServiceAdapter` provides the persistent mapping between label combinations and running service instances.
- **Roulette**: The random load-balancing algorithm implemented in `DefaultLabelGatewayRouter.roulette` that distributes traffic evenly among matching instances.

## Configuring Custom Route Labels for Multi-Tenant Traffic

Implementing multi-tenant isolation requires configuration at both the service instance level and the client request level.

### Registering Route Labels on Service Instances

When a Linkis service instance starts, it registers its metadata through `SpringCloudInstanceLabelClient` (or another `InstanceLabelClient` implementation). The registration attaches route labels to the instance's persistent metadata.

```scala
// Inside SpringCloudInstanceLabelClient.registerLabel(...)
val labels = new util.HashMap[String, Object]()
// Attach a custom route label, e.g. "dev"
labels.put(LabelKeyConstant.ROUTE_KEY, "dev")
insLabelRefreshRequest.setLabels(labels)

```

*Source*: [`SpringCloudInstanceLabelClient.scala`](https://github.com/apache/linkis/blob/main/SpringCloudInstanceLabelClient.scala)

### Defining Tenant-Specific Label Values

Organizations can define arbitrary label values to represent tenant boundaries:

- **Development tenant** → label `dev`
- **Production tenant** → label `prod`
- **Testing tenant** → label `test`

Consistency is critical: the value used during instance registration must match the value sent in client requests.

### Passing Labels via Query Parameters

The gateway configuration in [`DSSGatewayConfiguration.scala`](https://github.com/apache/linkis/blob/main/DSSGatewayConfiguration.scala) defines the query parameter names for label extraction:

```scala
val DSS_URL_LABEL_PREFIX   = CommonVars("wds.dss.gateway.url.prefix.name", "labels")
val DSS_URL_ROUTE_LABEL_PREFIX = CommonVars("wds.dss.gateway.url.prefix.name", "labelsRoute")

```

Clients can specify labels using either parameter:

```

GET /gateway/entrance/execute?labels=dev
GET /gateway/entrance/execute?labelsRoute=prod

```

### Embedding Labels in Request Body JSON

For POST requests, labels can be embedded within the JSON payload. The `GenericRoueLabelParser` extracts the `labels` field and builds `RouteLabel` objects:

```json
{
  "labels": {
    "route": "test"
  },
  "code": "select * from production_table",
  "engineType": "spark"
}

```

### Routing Behavior and Fallback Logic

The routing engine applies the following precedence rules:

1. **Exact match**: If the request carries a matching route label (e.g., `dev`), the label service returns all instances registered with that label.
2. **Load balancing**: When multiple instances match, the gateway selects one randomly via roulette.
3. **No label fallback**: If the request lacks labels entirely, the gateway routes to the unlabeled default pool.
4. **No match error**: If a label value has no registered instances (e.g., `staging`), the system returns a `CANNOT_ROETE_SERVICE` error.

## End-to-End Multi-Tenant Configuration Example

Consider a scenario with two Spark engine instances serving different tenants.

**Engine-A Configuration** (Development):

```bash

# In Engine-A configuration (application.conf)

linkis.instance.label.ROUTE_KEY = dev

```

**Engine-B Configuration** (Production):

```bash
linkis.instance.label.ROUTE_KEY = prod

```

Both instances register their labels via the `SpringCloudInstanceLabelClient` upon startup.

**Development Tenant Request**:

```http
POST http://gateway/linkis/entrance/execute?labels=dev
Content-Type: application/json

{
  "code": "print('Hello development environment')",
  "engineType": "spark"
}

```

The `DSSRouteLabelParser` extracts `labels=dev`, `DefaultLabelGatewayRouter` queries `InsLabelService` for instances bearing `route=dev`, and the request routes to Engine-A.

**Production Tenant Request**:

```http
POST http://gateway/linkis/entrance/execute?labels=prod
Content-Type: application/json

{
  "code": "analyze production_dataset",
  "engineType": "spark"
}

```

This request follows the same flow but selects Engine-B based on the `prod` label match.

## Summary

- **Linkis label-based routing** matches `RouteLabel` objects from requests against registered instance labels in the `InsLabelService`.
- The **routing pipeline** involves parsing (via `RouteLabelParser`), conversion (via `GenericRoueLabelParser`), instance discovery (via `AbstractLabelGatewayRouter`), and selection (via `DefaultLabelGatewayRouter.roulette`).
- **Custom route labels** are configured per service instance by setting the `ROUTE_KEY` entry in the instance metadata through `InstanceLabelClient`.
- **Tenant traffic direction** is achieved by supplying matching label values as query parameters (`labels` or `labelsRoute`) or within request body JSON.
- The system provides **automatic fallback** to unlabeled instances when no route labels are specified, ensuring backward compatibility.

## Frequently Asked Questions

### What happens if a request contains a route label that no instance has registered?

If the request specifies a label value (e.g., `labels=staging`) that has no corresponding instances in the `InsLabelService` registry, the label search returns an empty set. This triggers a `CANNOT_ROETE_SERVICE` error, indicating that no suitable service instance exists for the requested tenant label. To resolve this, ensure at least one instance registers with the matching label key-value pair.

### Can I use multiple route labels simultaneously for finer-grained routing?

Yes, the `RouteLabelParser` implementations return a `java.util.List[RouteLabel]`, allowing multiple labels to be specified in a single request. The `AbstractLabelGatewayRouter` passes this list to `searchInstancesByLabels`, which finds instances matching all specified labels. This enables complex routing scenarios combining tenant identifiers with environment or version labels.

### How does Linkis handle load balancing when multiple instances match the same route label?

When multiple instances match the requested route labels, `DefaultLabelGatewayRouter.selectInstance` performs a **roulette** selection using `Random.nextInt` over the candidate list. This random distribution ensures even load across identically-labeled instances. The method also filters out instances not present in the current registry via `retainAllInRegistry`, preventing routing to unhealthy or terminated services.

### Where do I configure the route label for a Linkis service instance?

Configure the route label in the service instance's configuration file (typically [`application.conf`](https://github.com/apache/linkis/blob/main/application.conf) or via environment variables) by setting `linkis.instance.label.ROUTE_KEY` to your desired tenant value. During startup, the `SpringCloudInstanceLabelClient` reads this configuration and registers the label with the central `InsLabelService` through the `registerLabel` method. This registration persists the mapping between the label value and the instance's network location.