23、工厂模式在微服务中的应用:服务发现工厂、API网关路由工厂

微服务架构里,工厂模式其实是个「隐形功臣」。你想想看,服务发现、API网关这些核心组件,天天要处理各种动态变化的东西——服务实例上下线、路由规则变更、协议转换……如果全靠硬编码,那维护成本简直不敢想。

我个人习惯,遇到这种「运行时才知道具体要创建什么」的场景,第一反应就是掏出工厂模式。今天咱们就聊聊,工厂模式在微服务里两个非常典型的落地场景:服务发现工厂和API网关路由工厂。

服务发现工厂:动态管理服务实例

微服务里,服务实例的地址是动态的。一个服务可能部署了10个实例,每个实例的IP和端口都不一样。而且,这些实例随时可能扩容、缩容、宕机重启。客户端怎么知道该调用哪个?

服务发现就是干这个的。但问题来了——不同的注册中心,API完全不同。Consul用HTTP API,Eureka用REST,Zookeeper用临时节点……你的代码总不能为每个注册中心都写一套逻辑吧?

嗯,工厂模式正好派上用场。

服务发现工厂的核心设计

说白了,就是定义一个「服务发现」的接口,然后每种注册中心都实现这个接口。工厂类负责根据配置,创建出对应的实现。

// 服务发现接口
public interface ServiceDiscovery {
    List<ServiceInstance> getInstances(String serviceName);
}

// Consul实现
public class ConsulServiceDiscovery implements ServiceDiscovery {
    private ConsulClient client;
    
    public ConsulServiceDiscovery(String host, int port) {
        this.client = new ConsulClient(host, port);
    }
    
    @Override
    public List<ServiceInstance> getInstances(String serviceName) {
        // 调用Consul API获取实例列表
        return client.getHealthServices(serviceName).getResponse()
            .stream()
            .map(service -> new ServiceInstance(
                service.getService().getAddress(),
                service.getService().getPort()
            ))
            .collect(Collectors.toList());
    }
}

// Eureka实现
public class EurekaServiceDiscovery implements ServiceDiscovery {
    private EurekaClient client;
    
    public EurekaServiceDiscovery(String serviceUrl) {
        this.client = new EurekaClient(serviceUrl);
    }
    
    @Override
    public List<ServiceInstance> getInstances(String serviceName) {
        return client.getApplication(serviceName).getInstances()
            .stream()
            .map(instance -> new ServiceInstance(
                instance.getIPAddr(),
                instance.getPort()
            ))
            .collect(Collectors.toList());
    }
}

// 工厂类
public class ServiceDiscoveryFactory {
    public static ServiceDiscovery create(String type, Properties config) {
        switch (type.toLowerCase()) {
            case "consul":
                return new ConsulServiceDiscovery(
                    config.getProperty("consul.host"),
                    Integer.parseInt(config.getProperty("consul.port"))
                );
            case "eureka":
                return new EurekaServiceDiscovery(
                    config.getProperty("eureka.serviceUrl")
                );
            case "zookeeper":
                return new ZookeeperServiceDiscovery(
                    config.getProperty("zookeeper.connectString")
                );
            default:
                throw new IllegalArgumentException("Unsupported registry: " + type);
        }
    }
}

我在项目中遇到过,有些团队把服务发现的逻辑散落在各个微服务里,每个服务自己写一套HTTP调用去查注册中心。结果注册中心一换,所有服务都得改。用工厂模式封装一层,切换注册中心只需要改配置文件,代码一行都不用动。

关键点:服务发现工厂的核心价值在于「解耦」。客户端不需要知道底层用的是Consul还是Eureka,它只依赖ServiceDiscovery接口。工厂类负责把配置和实现类关联起来。

API网关路由工厂:动态路由的基石

API网关是微服务的「总入口」。所有外部请求先打到网关,网关再根据路由规则转发到对应的微服务。但路由规则是动态的——新服务上线要加路由,旧服务下线要删路由,灰度发布要调整权重……

路由工厂就是用来管理这些动态路由的。它根据路由配置,创建出不同的路由对象。

路由工厂的设计思路

路由规则通常包含:路径匹配、目标服务、负载均衡策略、过滤器链等。不同的路由类型,处理逻辑完全不同。比如:

  • 路径路由:根据URL路径转发,比如 /user/** 转到用户服务
  • 权重路由:按比例分发流量,用于灰度发布
  • Header路由:根据请求头转发,比如版本号

每种路由类型,都需要一个对应的工厂方法来创建。

// 路由接口
public interface Route {
    boolean matches(ServerWebExchange exchange);
    URI getTargetUri();
    List<GatewayFilter> getFilters();
}

// 路径路由实现
public class PathRoute implements Route {
    private String pathPattern;
    private URI targetUri;
    private List<GatewayFilter> filters;
    
    public PathRoute(String pathPattern, URI targetUri, List<GatewayFilter> filters) {
        this.pathPattern = pathPattern;
        this.targetUri = targetUri;
        this.filters = filters;
    }
    
    @Override
    public boolean matches(ServerWebExchange exchange) {
        String path = exchange.getRequest().getURI().getPath();
        return path.startsWith(pathPattern);
    }
    
    @Override
    public URI getTargetUri() {
        return targetUri;
    }
    
    @Override
    public List<GatewayFilter> getFilters() {
        return filters;
    }
}

// 权重路由实现
public class WeightRoute implements Route {
    private List<WeightedService> services;
    private AtomicInteger counter = new AtomicInteger(0);
    
    public WeightRoute(List<WeightedService> services) {
        this.services = services;
    }
    
    @Override
    public boolean matches(ServerWebExchange exchange) {
        // 所有请求都匹配权重路由
        return true;
    }
    
    @Override
    public URI getTargetUri() {
        // 根据权重轮询选择目标
        int totalWeight = services.stream()
            .mapToInt(WeightedService::getWeight).sum();
        int index = counter.getAndIncrement() % totalWeight;
        return services.get(index).getUri();
    }
    
    @Override
    public List<GatewayFilter> getFilters() {
        return Collections.emptyList();
    }
}

// 路由工厂
public class RouteFactory {
    public static Route createRoute(RouteDefinition definition) {
        switch (definition.getType()) {
            case "path":
                return new PathRoute(
                    definition.getPath(),
                    URI.create(definition.getTarget()),
                    buildFilters(definition.getFilters())
                );
            case "weight":
                return new WeightRoute(
                    definition.getWeightedServices()
                );
            case "header":
                return new HeaderRoute(
                    definition.getHeaderName(),
                    definition.getHeaderValue(),
                    URI.create(definition.getTarget())
                );
            default:
                throw new IllegalArgumentException("Unknown route type: " + definition.getType());
        }
    }
    
    private static List<GatewayFilter> buildFilters(List<FilterDefinition> filterDefs) {
        // 过滤器也可以使用工厂模式创建
        return filterDefs.stream()
            .map(FilterFactory::createFilter)
            .collect(Collectors.toList());
    }
}

避坑指南:我曾经在网关路由工厂里踩过一个坑——路由规则变更后,没有及时刷新工厂缓存的路由对象。结果新规则一直不生效,排查了半天才发现是缓存问题。后来我加了个「路由刷新」接口,每次配置变更都重新创建路由对象,问题就解决了。

工厂模式在微服务中的整体架构

下面这张图,展示了工厂模式在微服务体系中的位置和作用。你可以看到,服务发现工厂和API网关路由工厂,分别解决了「服务实例管理」和「请求路由」两个核心问题。

工厂模式在微服务架构中的应用 外部客户端 API网关 路由工厂 → 动态创建路由规则 用户服务 订单服务 支付服务 服务发现 服务发现工厂 → 动态管理实例 Consul / Eureka / Zookeeper ① 请求路由 ② 服务发现

从这张图可以看得很清楚:

  • 左侧:API网关通过路由工厂,动态创建不同类型的路由规则。请求进来后,网关根据路由规则转发到对应的微服务。
  • 右侧:微服务启动时,通过服务发现工厂注册到注册中心。其他服务调用时,通过服务发现工厂获取实例列表。

两个工厂模式,一个管「流量分发」,一个管「实例管理」。配合起来,微服务架构的弹性伸缩和动态路由就都有了保障。

实际项目中的经验总结

做了这么多年架构,我总结了几条工厂模式在微服务里的使用心得:

场景 工厂模式的作用 我踩过的坑
服务发现 屏蔽不同注册中心的API差异 曾经忘了处理连接池,导致高并发时注册中心被打挂
API网关路由 支持动态路由规则,无需重启网关 路由缓存没刷新,新规则一直不生效
协议适配 HTTP、gRPC、Dubbo协议转换 协议转换时超时设置不合理,导致调用链雪崩
过滤器链 动态组合认证、限流、日志等过滤器 过滤器顺序搞反了,认证还没做就先限流了

注意:工厂模式虽然好用,但别滥用。如果只有一种实现,或者实现类几乎不变,那用工厂模式就是过度设计。我见过有人给一个只有两个方法的接口也套了工厂模式,结果代码量翻了一倍,维护起来反而更麻烦。

说白了,工厂模式在微服务里的核心价值就四个字:动态解耦。它让系统能够在不修改代码的情况下,应对各种变化——注册中心换了、路由规则改了、协议变了……只要配置一变,工厂就能创建出对应的组件。这才是微服务架构该有的弹性。

嗯,今天就聊到这儿。工厂模式在微服务里的应用远不止这些,后面咱们还会看到它在配置管理、服务治理等场景中的身影。