第 7 章:路由

本章系统梳理了云原生环境下服务发现、注册中心、客户端负载均衡与路由服务的核心原理与实践,帮助开发者理解如何实现高可用、弹性和灵活的服务路由架构。

DiscoveryClient 接口与服务注册中心

在分布式系统中,服务注册中心用于维护服务实例的可用性和位置映射,类似云端的“电话簿”。Spring Cloud 提供了 DiscoveryClient 接口,屏蔽了不同注册中心的实现细节,便于灵活切换。常见实现包括 Cloud Foundry、Consul、Zookeeper、Eureka 等。

// DiscoveryClient 接口核心方法(伪代码)
public interface DiscoveryClient {
    String description();
    ServiceInstance getLocalServiceInstance();
    List<ServiceInstance> getInstances(String serviceId);
    List<String> getServices();
}

服务注册中心的设计需权衡 CAP 定理(Consistency, Availability, Partition tolerance),并考虑安全、加密、配置等扩展能力。

Netflix Eureka 服务注册中心

Eureka 是 Netflix 开源的服务注册中心,支持高可用、弹性伸缩。通过 Spring Cloud 集成,可快速搭建 Eureka Server:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServiceApplication.class, args);
    }
}

典型配置如下:

server.port=${PORT:8761}
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.server.enable-self-preservation=false
  • 端口默认为 8761
  • 关闭自注册和自我保护(仅适合开发环境)

Eureka 控制台可视化展示所有注册实例,便于监控和管理。

Eureka 控制台示例
Eureka 控制台示例

服务注册与客户端集成

服务需通过 @EnableDiscoveryClient 注解启用注册能力,并配置唯一的 spring.application.name。示例:

@SpringBootApplication
@EnableDiscoveryClient
public class GreetingsServiceApplication { ... }

@RestController
class GreetingsRestController {
    @GetMapping("/hi/{name}")
    public Map<String, String> hi(@PathVariable String name, @RequestHeader(value = "X-CNJ-Name", required = false) Optional<String> cn) {
        String resolvedName = cn.orElse(name);
        return Collections.singletonMap("greeting", "Hello, " + resolvedName + "!");
    }
}

配置示例:

spring.application.name=greetings-service
server.port=${PORT:0}
eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}

启动多个实例后,Eureka 控制台会显示所有注册节点。

Eureka 注册实例示例
Eureka 注册实例示例

DiscoveryClient 的使用与客户端负载均衡

服务消费者可通过注入 DiscoveryClient 查询服务实例,实现自定义负载均衡策略。例如:

@Component
public class DiscoveryClientCLR implements CommandLineRunner {
    @Autowired
    private DiscoveryClient discoveryClient;

    @Override
    public void run(String... args) {
        List<ServiceInstance> instances = discoveryClient.getInstances("greetings-service");
        // 选择实例并发起请求
    }
}

为简化负载均衡,Spring Cloud 集成了 Netflix Ribbon,可通过 @LoadBalanced 注解自动为 RestTemplate 配置客户端负载均衡:

@Configuration
public class LoadBalancedRestTemplateConfiguration {
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

使用方式:

@Autowired
@LoadBalanced
private RestTemplate restTemplate;

public void callService() {
    ResponseEntity<JsonNode> response = restTemplate.getForEntity(
        "http://greetings-service/hi/{name}", JsonNode.class, Collections.singletonMap("name", "Cloud Natives!"));
    // 处理响应
}

Cloud Foundry 路由服务

对于多语言或第三方客户端,无法直接集成 Ribbon 等负载均衡库时,可通过 Cloud Foundry 路由服务实现中心化路由和请求转发。路由服务本质上是一个 HTTP 代理应用,可对请求进行认证、限流、日志等处理。

典型实现:

@RestController
class Controller {
    @Autowired
    private RestTemplate restOperations;

    @RequestMapping(headers = {"X-CF-Forwarded-Url", "X-CF-Proxy-Metadata", "X-CF-Proxy-Signature"})
    public ResponseEntity<?> service(RequestEntity<byte[]> incoming) {
        // 解析目标 URL,转发请求,记录日志
        return restOperations.exchange(outgoing, byte[].class);
    }
}

绑定路由服务示例:

cf create-user-provided-service route-service -r https://my-route-service.cfapps.io
cf bind-route-service cfapps.io route-service -hostname my-downstream-service

通过绑定,所有发往指定主机的请求都会先经过路由服务处理。

路由与服务发现的最佳实践

  • 优先使用平台内置的服务注册与发现机制,简化服务间通信。
  • 对于内部服务,推荐客户端负载均衡(如 Ribbon + RestTemplate)。
  • 对于外部或多语言客户端,推荐中心化路由服务或 API 网关。
  • 路由服务可扩展为认证、限流、日志等通用中间件。

总结

本章系统梳理了云原生环境下服务注册中心、DiscoveryClient、客户端负载均衡与路由服务的核心原理与实践。通过合理设计服务发现与路由机制,开发者可实现高可用、弹性和灵活的微服务架构,提升系统的可维护性与扩展性。

文章导航

独立页面

这是书籍中的独立页面。

书籍首页

评论区