Spring Boot 3.5 + MyBatis-Plus 实现基于数据库的 Feign 服务注册与发现(替代注册中心)

一、背景说明

在传统微服务架构中,Feign 通常依赖注册中心(如 Nacos、Eureka
等)进行服务发现。但在部分企业场景中:

  • 不希望引入独立注册中心
  • 内网环境无法部署中间件
  • 希望服务治理完全可控
  • 希望服务元数据可持久化、可审计
  • 小型/微型/接口型项目,希望架构统一但又无需引入多的中间件

因此,我们实现了一套:

基于数据库的 ServiceInstanceListSupplier 实现, 在不改变 Feign
使用方式的前提下,替代注册中心实现服务发现。


二、适用范围

  • Spring Boot 3.5.x
  • Spring Cloud 2023.x
  • OpenFeign
  • MyBatis-Plus
  • JDK 21
  • 无注册中心或需要替代注册中心场景

特点:

  • 不修改 Feign 使用方式
  • 支持权重负载均衡
  • 支持数据库动态维护服务列表

三、数据库注册表设计

表名:uniops_system_register

核心字段:

字段 说明


system_id spring.application.name
ip 服务IP
port 服务端口
status 1=在线
weight 负载权重


四、核心实现代码

1. 自定义 ServiceInstance

public class DbServiceInstance implements ServiceInstance {

    private final String serviceId;
    private final String host;
    private final int port;
    private final Integer weight;

    public DbServiceInstance(String serviceId, String host, int port, Integer weight) {
        this.serviceId = serviceId;
        this.host = host;
        this.port = port;
        this.weight = weight;
    }

    public Integer getWeight() {
        return weight;
    }

    @Override
    public String getServiceId() {
        return serviceId;
    }

    @Override
    public String getHost() {
        return host;
    }

    @Override
    public int getPort() {
        return port;
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public URI getUri() {
        return URI.create("http://" + host + ":" + port);
    }

    @Override
    public Map<String, String> getMetadata() {
        return Map.of();
    }
}

2. 自定义 ServiceInstanceListSupplier

public class DbServiceInstanceSupplier implements ServiceInstanceListSupplier {

    private final String serviceId;
    private final SystemRegisterMapper mapper;

    public DbServiceInstanceSupplier(String serviceId, SystemRegisterMapper mapper) {
        this.serviceId = serviceId;
        this.mapper = mapper;
    }

    @Override
    public String getServiceId() {
        return serviceId;
    }

    @Override
    public Flux<List<ServiceInstance>> get() {

        List<SystemRegister> list = mapper.selectList(
                new LambdaQueryWrapper<SystemRegister>()
                        .eq(SystemRegister::getSystemId, serviceId)
                        .eq(SystemRegister::getStatus, "1")
        );

        List<ServiceInstance> instances = list.stream()
                .map(e -> new DbServiceInstance(
                        e.getSystemId(),
                        e.getIp(),
                        Integer.parseInt(e.getPort()),
                        e.getWeight() == null ? 1 : e.getWeight()
                ))
                .toList();

        return Flux.just(instances);
    }
}

3. 全局替换 ServiceInstanceListSupplier

@Configuration
@LoadBalancerClients(defaultConfiguration = GlobalDbLoadBalancerConfiguration.class)
public class GlobalLoadBalancerConfig {
}

子容器配置类(不要加 @Configuration):

public class GlobalDbLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier serviceInstanceListSupplier(
            Environment environment,
            SystemRegisterMapper mapper) {

        String serviceId =
                environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

        return new DbServiceInstanceSupplier(serviceId, mapper);
    }
}

五、Feign 使用方式保持不变

@FeignClient(name = "wit-ptjk-uni-notice")
public interface NoticeFeign {

    @PostMapping("/yx-uni-notice/notice/toDing")
    Boolean toDing(DingCondition condition);
}

无需修改调用代码。


六、架构运行流程

Feign
   ↓
Spring Cloud LoadBalancer
   ↓
DbServiceInstanceSupplier
   ↓
MyBatis-Plus
   ↓
uniops_system_register 表

八、总结

本文实现了:

  • 无注册中心的 Feign 服务发现
  • 数据库存储服务实例
  • 全局替换 ServiceInstanceListSupplier
  • 保持 Feign 使用方式完全不变

适合内网、轻量级、强可控场景使用。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐