4

我们有一个 sidecar 应用程序,使我们能够向 Eureka 注册我们的 node.js 应用程序以启用服务发现。

我们的 Sidecar 应用程序配置如下所示:

server:
  port: 9000

spring:
  application:
    name: session-service

sidecar:
  port: 3000
  health-url: http://sessionServiceNode:${sidecar.port}/health.json

eureka:
  client:
    serviceUrl:
      defaultZone: http://discoveryService:8761/eureka/
  instance:
    lease-renewal-interval-in-seconds: 5
    prefer-ip-address: true

根据配置,我们的节点应用程序3000在属性定义的端口上运行sidecar.port,我们的边车应用程序应该在端口上9000运行server.port

我们已经向我们的节点应用程序添加了一个端点,以允许边车检查应用程序的健康状况 ( sidecar.health-url)。主机名sessionServiceNode是我们为运行节点应用程序的容器提供的别名。

我们的 Eureka 服务运行在一个单独的容器中,该容器也通过别名 discoveryService 链接到 sidecar 应用程序容器。

我们有一个单独的测试 Spring Boot 应用程序,它在一个单独的容器中运行,该容器是会话服务的消费者。此容器仅链接到发现服务容器。

Sidecar 应用程序按预期向 Eureka 注册自身

在此处输入图像描述

测试服务对会话服务使用两种形式的查找。一个使用假客户端:

@FeignClient(value = "session-service") // name of our registered service in eureka
interface SessionServiceClient {

    @RequestMapping(value = "/document/get/24324", method = GET)
    String documentGetTest();

}

另一种方法使用更程序化的查找:

@Autowired
private DiscoveryClient discoveryClient;

...
discoveryClient.getInstances("session-service");

当我们向测试服务发出请求时,测试服务会查找会话服务,但是 eureka 给我们返回的实例信息的 URI 为:http://172.17.0.5:3000这是不正确的。172.17.0.5是 sidecar 应用程序容器的 IP 地址,但端口3000是节点应用程序运行的端口?

应该期望看到 eureka 用会话服务端口http://172.17.0.5:9000http://172.17.0.6:3000还是应该 Eureka 直接给我们节点应用的位置?

我在下面包含了来自 Eureka 的会话服务实例信息:

<?xml version="1.0" encoding="UTF-8"?>
<application>
   <name>SESSION-SERVICE</name>
   <instance>
      <hostName>172.17.0.5</hostName>
      <app>SESSION-SERVICE</app>
      <ipAddr>172.17.0.5</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="true">3000</port>
      <securePort enabled="false">443</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
         <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
         <renewalIntervalInSecs>5</renewalIntervalInSecs>
         <durationInSecs>90</durationInSecs>
         <registrationTimestamp>1461223810081</registrationTimestamp>
         <lastRenewalTimestamp>1461224812429</lastRenewalTimestamp>
         <evictionTimestamp>0</evictionTimestamp>
         <serviceUpTimestamp>1461223810081</serviceUpTimestamp>
      </leaseInfo>
      <metadata class="java.util.Collections$EmptyMap" />
      <homePageUrl>http://c892e0c03cf4:3000/</homePageUrl>
      <statusPageUrl>http://c892e0c03cf4:9000/info</statusPageUrl>
      <healthCheckUrl>http://c892e0c03cf4:9000/health</healthCheckUrl>
      <vipAddress>session-service</vipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1461223810081</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1461223033045</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
   </instance>
</application>

编辑:

看了代码后,Eureka 使用InetAddress.getLocalHost().getHostAddress()InetAddress.getLocalHost().getHostName()分别返回的主机信息来确定实例的地址。这就是为什么我们要获取 sidecar 容器的 IP 地址。有什么办法可以覆盖这种行为?

4

2 回答 2

4

因此,从外观上看,Sidecar 假设 Spring Boot Sidecar 应用程序和非 jvm 应用程序运行在同一主机上。在我们的场景中,我们在单独的容器中运行所有内容。一个容器用于我们的 sidecar jvm 应用程序,一个容器用于我们的 node.js 应用程序。理论上,我们可以让两个应用程序在同一个容器中运行,但这违反了 Docker 的最佳实践,即每个容器拥有一个 unix 进程。

为了让它工作,我们覆盖了EurekaInstanceConfigBean,它允许我们控制为实例选择的主机名和 IP 地址。在这种情况下,我们委托给inetUtils类并从主机名(这是通过 docker 链接的非 jvm 应用程序的别名)查找 IP 地址。我们使用 spring@ConfigurationPropertiesapplication.yml配置文件中控制主机名/端口。

SessionServiceSidecar.java

@Component
@ConfigurationProperties
public class SessionServiceSidecarProperties {

    @Value("${sidecar.hostname}")
    private String hostname;

    @Value("${sidecar.port}")
    private Integer port;

    public String getHostname() {
        return hostname;
    }

    public Integer getPort() {
        return port;
    }

}

会话服务应用程序.java

@SpringBootApplication
@EnableSidecar
@EnableDiscoveryClient
@Configuration
@ComponentScan
@EnableConfigurationProperties
public class SessionServiceApp {

    private @Autowired SessionServiceSidecarProperties properties;

    public static void main(String[] args) {
        SpringApplication.run(SessionServiceApp.class, args);
    }

    @Bean
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {

        final String sidecarHostname = properties.getHostname();
        final Integer sidecarPort = properties.getPort();

        try {

            final EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
            instance.setHostname(sidecarHostname);
            instance.setIpAddress(inetUtils.convertAddress(InetAddress.getByName(sidecarHostname)).getIpAddress());
            instance.setNonSecurePort(sidecarPort);
            return instance;

        } catch(UnknownHostException e) {
            throw new IllegalStateException("Could not resolve IP address of sidecar application using hostname: " + sidecarHostname);
        }

    }

}

应用程序.yml

spring:
  application:
    name: session-service

server:
  port: 9000

sidecar:
  hostname: sessionServiceNode
  port: 3000
  health-url: http://sessionServiceNode:${sidecar.port}/health.json

eureka:
  client:
    serviceUrl:
      defaultZone: http://discoveryService:8761/eureka/
  instance:
    lease-renewal-interval-in-seconds: 5
    prefer-ip-address: true

sidecar.hostname希望 Spring 家伙将来允许我们通过属性控制 sidecar 应用程序的主机名以及端口。

希望这可以帮助!

于 2016-04-22T10:17:01.983 回答
0

Spring Cloud 正在挑选第一个非环回 IP 地址。你可以做几件事中的一件。

一:忽略你不想选择的网络接口。文档在这里

spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*

或二:显式设置 eureka 注册的 ipaddress 或主机名(如果您在任何地方使用相同的 docker 别名。对于主机名设置eureka.instance.hostname=sessionServiceNode并删除该prefer-ip-address=true选项。对于 ip addr eureka.instance.ipAddress=<ipaddr>,.

于 2016-04-21T08:24:30.103 回答