微服务架构
你当前正在浏览历史版本 Vert.x 5.1.10 的文档。 访问本页面的 最新版本。

Vert.x 服务解析器与客户端负载均衡 (vertx-service-resolver)

Preview

服务解析器(Service Resolver)是一个功能强大的 Vert.x 插件库,允许 Vert.x 客户端使用逻辑服务名(Logical Service Names)替代传统的物理 IP 和端口来发起远程调用。此外,服务解析器还内建了客户端负载均衡能力,支持主流的负载均衡路由策略。

你可以直接使用服务解析器客户端独立查询服务终结点,也可以将其深度集成至 Vert.x 原生客户端中透明使用。

独立服务解析器客户端

你可以通过独立客户端主动向注册中心查询服务终结点实例:

ServiceResolverClient client = ServiceResolverClient.create(vertx, new KubeResolverOptions());

// 解析指定服务名背后的物理终结点
Future<Endpoint> fut = client.resolveEndpoint(ServiceAddress.of("the-service"));

fut.onSuccess(endpoint -> {
  // 遍历并打印所有活跃的物理服务器节点详情:
  List<ServerEndpoint> servers = endpoint.servers();
  for (ServerEndpoint server : servers) {
    System.out.println("可用服务器节点: " + server.address());
  }
});

你也可以让负载均衡器全自动决策路由节点。

与 Vert.x 核心客户端深度集成

服务解析器能够与 Vert.x HTTP Client 及 Web Client 原生无缝集成。

1. 在 Vert.x HTTP Client 中集成

通过 HttpClientBuilder 为 HTTP 客户端注入地址解析器:

HttpClient client = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .build();

集成后,直接使用 ServiceAddress 替代传统的物理 SocketAddress:

ServiceAddress serviceAddress = ServiceAddress.of("the-service");

Future<HttpClientRequest> requestFuture = client.request(new RequestOptions()
  .setMethod(HttpMethod.GET)
  .setURI("/")
  .setServer(serviceAddress));

Future<Buffer> resultFuture = requestFuture.compose(request -> request
  .send()
  .compose(response -> {
    if (response.statusCode() == 200) {
      return response.body();
    } else {
      return Future.failedFuture("服务端返回非正常状态码: " + response.statusCode());
    }
  }));

2. 在 Vert.x Web Client 中集成

同样借助 HttpClientBuilder 构建底层网络通道,并将其包装为高层 Web Client:

HttpClient httpClient = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .build();
WebClient webClient = WebClient.wrap(httpClient);

发起基于服务名的请求调用:

ServiceAddress serviceAddress = ServiceAddress.create("the-service");

Future<HttpResponse<Buffer>> future = webClient
  .request(HttpMethod.GET, new RequestOptions().setServer(serviceAddress))
  .send();

客户端负载均衡策略

默认的负载均衡路由策略为 轮询(Round-Robin),你可以根据实际需求自由定制算法:

HttpClient client = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .withLoadBalancer(LoadBalancer.LEAST_REQUESTS) // 最少请求数负载均衡策略
  .build();

常见服务解析器实现生态

服务解析器现已深度集成了 Kubernetes 与 DNS SRV 等多种服务注册发现基础设施。

Kubernetes 服务解析器

Kubernetes 解析器能够实时感知并定位 Kubernetes 集群内部的微服务 Endpoints。

Kubernetes 解析器要求 Pod 所绑定的 ServiceAccount 拥有对 Kubernetes 原生 endpoints 资源的访问与监听权限。

针对 default ServiceAccount 的 RBAC 角色与绑定配置示例如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: observe-endpoints
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: observe-endpoints
  namespace: default
roleRef:
  kind: Role
  name: observe-endpoints
  apiGroup: rbac.authorization.k8s.io
subjects:
  - kind: ServiceAccount
    name: default
    namespace: default

在代码中初始化 Kubernetes 解析器:

KubeResolverOptions options = new KubeResolverOptions();

KubeResolver resolver = KubeResolver.create(options);

HttpClient client = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .build();

默认配置项会自动从 Pod 容器环境变量与标准挂载路径读取: - KUBERNETES_SERVICE_HOST - KUBERNETES_SERVICE_PORT - /var/run/secrets/kubernetes.io/serviceaccount/token - /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - /var/run/secrets/kubernetes.io/serviceaccount/namespace

你可以自由覆盖上述任意参数。

针对服务账号短期凭据自动轮转(Ephemeral Tokens),可通过 tokenProvider 注册动态获取函数:

KubeResolverOptions options = new KubeResolverOptions();

KubeResolver resolver = KubeResolver.create(options)
  .tokenProvider(() -> loadToken());

HttpClient client = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .build();

解析器会在需要新鲜 Token 时主动调用该函数,并自动维护内部缓存,直到遇到服务端返回 401 Unauthorized 状态码时自动作废旧 Token 并重新拉取。

匹配特定服务端口

当 Kubernetes Service 暴露了多个端口时,解析器默认仅选留一个端口,这可能不符合业务预期。你可以显式指定端口号:

ServiceAddress serviceAddress = KubernetesServiceAddressBuilder
  .of("the-service")
  .withPortNumber(8080)
  .build();

或者按端口名称(Port Name)精准匹配:

ServiceAddress serviceAddress = KubernetesServiceAddressBuilder
  .of("the-service")
  .withPortName(portName)
  .build();

DNS SRV 服务解析器

SRV 解析器利用标准 DNS SRV 资源记录来动态解析并定位后端服务实例:

SrvResolverOptions options = new SrvResolverOptions()
  .setServer(SocketAddress.inetSocketAddress(dnsPort, dnsServer));

AddressResolver resolver = SrvResolver.create(options);

HttpClient client = vertx.httpClientBuilder()
  .withAddressResolver(resolver)
  .build();