微服务架构下本地开发环境可以访问到测试环境服务

背景说明:微服务架构下,开发者有一套本地环境及测试环境(nacos分别各有一套开发(本地)环境的命名空间及测试环境的命名空间,测试环境的服务注册在测试环境命名空间,本地启的应用注册在开发环境命名空间);例如共有5个服务,本地开发功能时,实际改动1个服务,但是整个流程涉及其他另外2个服务,这时可以只启动实际改动的服务,需要用到的关联服务调用测试环境服务,可减少本地资源消耗

目的:本地开发环境服务启动时优先调用本地服务,未启动时调用指定的测试环境服务

详细介绍:共有vp-cloud-ring、vp-cloud-onkey、vp-cloud-area、vp-cloud-bm、vp-cloud-common等模块,其中vp-cloud-common属于微服务公共模块,会被其他业务模块包含,需在vp-cloud-common模块中增加如下配置类:

import cn.**.vrbt.common.utils.IpUtil;
import cn.**.vrbt.config.lb.LoadBalanceConfig.MyRibbonConfig;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import java.util.Random;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClientName;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.util.CollectionUtils;

/**
 * 开发环境下优先路由到本机IP服务实例   
 * “dev”即本地开发环境后缀
 * RibbonClients中的都是具体的微服务,有多少就添加多少
 */
@Profile("dev")
@Configuration
@RibbonClients(value = {
  @RibbonClient(name = LoadBalanceConfig.AREA_PROVIDER, configuration = MyRibbonConfig.class),
  @RibbonClient(name = LoadBalanceConfig.RING_PROVIDER, configuration = MyRibbonConfig.class),
  @RibbonClient(name = LoadBalanceConfig.ONEKEY_PROVIDER, configuration = MyRibbonConfig.class),
  @RibbonClient(name = LoadBalanceConfig.BIZ_PROVIDER, configuration = MyRibbonConfig.class),
  @RibbonClient(name = LoadBalanceConfig.BM_PROVIDER, configuration = MyRibbonConfig.class)
})
public class LoadBalanceConfig {

  /**  具体的服务   */
  public static final String AREA_PROVIDER = "vp-cloud-area-provider";

  public static final String RING_PROVIDER = "vp-cloud-ring-provider";

  public static final String ONEKEY_PROVIDER = "vp-cloud-onekey-provider";

  public static final String BIZ_PROVIDER = "vp-cloud-biz-provider";

  public static final String BM_PROVIDER = "vp-cloud-bm-provider";

  private static final Map<String, Server> testServer = new HashMap<>();

  /**  指明测试环境服务的IP端口 */
  static {
    testServer.put(AREA_PROVIDER, new Server("172.**.**.77:8080"));
    testServer.put(RING_PROVIDER, new Server("172.**.**.77:8082"));
    testServer.put(ONEKEY_PROVIDER, new Server("172.**.**.77:8084"));
    testServer.put(BIZ_PROVIDER, new Server("172.**.**.77:8085"));
    testServer.put(BM_PROVIDER, new Server("172.**.**.77:8086"));
  }

  public static class MyRibbonConfig {

    private final Logger logger = LoggerFactory.getLogger(LoadBalanceConfig.class);

    @RibbonClientName
    private String name;

    @Bean
    @Primary
    public IClientConfig devConfig() {
      logger.info("开发环境: dev, 客户端: {}, IP: {}, 使用自定义的Ribbon配置 优先进入本地服务", this.name,
        IpUtil.getFirstLocalIp());
      DefaultClientConfigImpl config = new DefaultClientConfigImpl();
      config.loadProperties(this.name);
      config.set(CommonClientConfigKey.ConnectTimeout, 1000);
      config.set(CommonClientConfigKey.ReadTimeout, 4000);
      config.set(CommonClientConfigKey.GZipPayload, true);
      config.set(CommonClientConfigKey.NFLoadBalancerPingInterval, 5);
      config.set(CommonClientConfigKey.PoolMinThreads, 4);
      config.set(CommonClientConfigKey.MaxAutoRetries, 0);

      return config;
    }

    /**
     * 负载策略bean, 开发环境下为自定义的负载策略, 否则为默认的轮询策略
     */
    @Bean
    @Primary
    public IRule devRule() {
      return new MyRule(name);
    }

  }

  /**
   * 自定义Rule, 在原来的基础上如果有本地同IP的服务则优先进入本地的服务, 需要查询本地IP, 确保注册的IP为VPN的IP, Nacos默认选取第一个网卡的IP,
   * 如果首个网卡不是VPN可在配置文件指定${spring.cloud.nacos.discovery.network-interface}的对应网卡(不建议, 提交代码需要修改),
   * 建议把前面的网卡在设备管理器中卸载或重排序, 具体操作自行Google或百度
   */
  public static class MyRule extends ZoneAvoidanceRule {

    private final Random random = new Random();
    private final String name;
    private final Logger log = LoggerFactory.getLogger(MyRule.class);
    private static final String localIp = IpUtil.getFirstLocalIp();

    public MyRule(String name) {
      super();
      this.name = name;
    }

    public MyRule() {
      super();
      this.name = "";
    }
    
    public MyRule(ILoadBalancer lb) {
      this();
      this.setLoadBalancer(lb);
    }

    @Override
    public Server choose(Object key) {
      ILoadBalancer lb = getLoadBalancer();
      List<Server> allEligbleServers = this.getPredicate()
        .getEligibleServers(lb.getAllServers(), key);
      List<Server> localServers = allEligbleServers.stream().collect(
        Collectors.groupingBy(Server::getHost)).get(localIp);
      if (!CollectionUtils.isEmpty(localServers)) {
        return localServers.get(random.nextInt(localServers.size()));
      }
      // 开发环境有不同ip尝试连接
      // if (!CollectionUtils.isEmpty(allEligbleServers)) {
      //   Server server = allEligbleServers.get(random.nextInt(allEligbleServers.size()));
      //   InetSocketAddress inetSocketAddress = new InetSocketAddress(server.getHost(), server.getPort());
      //   Socket socket = new Socket();
      //   try {
      //     socket.connect(inetSocketAddress, 50);
      //     socket.close();
      //     return server;
      //   } catch (Exception e) {
      //     log.warn("服务不可及: {}-{}", name, server.getHostPort());
      //   }
      // }
      Server server = LoadBalanceConfig.testServer.get(name);
      if (Objects.nonNull(server)) {
        log.warn("本地未启动: {}, 尝试路由到测试环境: {}", name, server.getHostPort());
      }
      return server;
    }

  }

}

使用到的工具类:

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

/**
 * IP工具类
 */
public class IpUtil {

  public static String ip;

  /**
   * 多IP处理,可以得到最终ip
   */
  public static String getLocalIp() {
    if (ip == null) {
      String localip = null;// 本地IP,如果没有配置外网IP则返回它
      String netip = null;// 外网IP
      try {
        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
          NetworkInterface ni = netInterfaces.nextElement();
          Enumeration<InetAddress> address = ni.getInetAddresses();
          while (address.hasMoreElements()) {
            ip = address.nextElement();
            if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress()
                && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
              netip = ip.getHostAddress();
              finded = true;
              break;
            } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress()
                && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
              localip = ip.getHostAddress();
            }
          }
        }
      } catch (SocketException e) {
        e.printStackTrace();
      }
      if (netip != null && !"".equals(netip)) {
        ip = netip;
      } else {
        ip = localip;
      }
    }
    return ip;
  }
  
  /**
   * 获取第一个网卡ip, Nacos注册默认使用第一个
   */
  public static String getFirstLocalIp() {
    String localip = null;// 本地IP,如果没有配置外网IP则返回它
    String netip = null;// 外网IP
    try {
      Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
      InetAddress inetAddress = null;
      boolean finded = false;// 是否找到外网IP
      while (netInterfaces.hasMoreElements() && !finded) {
        NetworkInterface ni = netInterfaces.nextElement();
        Enumeration<InetAddress> address = ni.getInetAddresses();
        while (address.hasMoreElements()) {
          inetAddress = address.nextElement();
          if (!inetAddress.isSiteLocalAddress() && !inetAddress.isLoopbackAddress()
              && !inetAddress.getHostAddress().contains(":")) {// 外网IP
            netip = inetAddress.getHostAddress();
            finded = true;
            break;
          } else if (inetAddress.isSiteLocalAddress() && !inetAddress.isLoopbackAddress()
              && !inetAddress.getHostAddress().contains(":")) {// 内网IP
            return inetAddress.getHostAddress();
          }
        }
      }
    } catch (SocketException e) {
      e.printStackTrace();
    }
    if (netip != null && !"".equals(netip)) {
      ip = netip;
    } else {
      ip = localip;
    }
    return ip;
  }

  public static String getLocalIp(boolean refresh) {
    ip = null;
    return getLocalIp();
  }

  public static void main(String[] args) {
    System.out.println("本机IP:" + IpUtil.getLocalIp());
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值