12

我正在尝试一种非常类似于此处图片中详细说明的设置:https ://raw.githubusercontent.com/Oreste-Luci/netflix-oss-example/master/netflix-oss-example.png

在此处输入图像描述

在我的设置中,我使用了一个客户端应用程序 ( https://www.joedog.org/siege-home/ )、一个代理 (Zuul)、一个发现服务 (Eureka) 和一个简单的微服务。一切都部署在 PWS 上。

我想在不停机的情况下从我的简单微服务的一个版本迁移到下一个版本。最初,我从这里描述的技术开始:https ://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html

在我看来,这种方法与 Eureka 等发现服务不“兼容”。事实上,我的服务的新版本已在 Eureka 中注册,甚至在我重新映射所有路由(CF 路由器)之前就已接收流量。

这导致我采用另一种方法,其中我依赖 Spring Cloud/Netflix 中的故障转移机制:

  1. 我启动了我的服务的新(向后兼容)版本。
  2. 当这个版本被 Zuul/Eureka 选中时,它开始获得 50% 的流量。
  3. 一旦我确认新版本可以正常工作,我就会删除“旧”实例。(我只需点击 PWS 中的“停止”按钮)

据我了解,Zuul 在后台使用 Ribbon(负载平衡),因此在旧实例仍在 Eureka 但实际上正在关闭的那一瞬间,我希望重试新实例,而不会对客户端产生任何影响。

然而,我的假设是错误的。我的客户端出现了一些 502 错误:

Lifting the server siege...      done.

Transactions:               5305 hits
Availability:              99.96 %
Elapsed time:              59.61 secs
Data transferred:          26.06 MB
Response time:              0.17 secs
Transaction rate:          89.00 trans/sec
Throughput:             0.44 MB/sec
Concurrency:               14.96
Successful transactions:        5305
Failed transactions:               2
Longest transaction:            3.17
Shortest transaction:           0.14

我的 application.yml 的一部分

server:
  port: ${PORT:8765}

info:
  component: proxy

ribbon:
  MaxAutoRetries: 2   # Max number of retries on the same server (excluding the first try)
  MaxAutoRetriesNextServer: 2 # Max number of next servers to retry (excluding the first server)
  OkToRetryOnAllOperations: true # Whether all operations can be retried for this client
  ServerListRefreshInterval: 2000 # Interval to refresh the server list from the source
  ConnectTimeout: 3000 # Connect timeout used by Apache HttpClient
  ReadTimeout: 3000 # Read timeout used by Apache HttpClient

hystrix:
  threadpool:
      default:
        coreSize: 50
        maxQueueSize: 100
        queueSizeRejectionThreshold: 50
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

我不确定出了什么问题。

这是技术问题吗?

还是我做出了错误的假设(我确实在某处读到过 POST 无论如何都不会重试,我不太明白)?

我很想听听你是怎么做到的。

谢谢,安迪

4

1 回答 1

2

我也想知道这个。我不会声称使用过 Spring Cloud “In Anger”。我刚刚试验了一段时间。

假设:我们假设所有实例状态的真实来源都存储在 Eureka 中,那么 Eureka 应该是我们的操作控制机制。我们可以使用 Eureka 通过将实例状态设置为 来使实例停止服务OUT_OF_SERVICE。当 Ribbon 刷新其服务器列表时,它不会使用这些停止服务的实例。Eureka 提供了一个 REST API 用于查询实例和设置实例状态。伟大的。

问题是:如何确定哪些实例属于蓝色组,哪些实例属于绿色组?

我在想... Eureka 为每个实例提供了一个元数据映射。假设在我们的构建/烘焙步骤中,我们在元数据映射中设置了版本 ID?我们可以使用 Git 提交 ID 或一些语义版本控制方案或其他任何东西。好的,现在我可以查看 Eureka 元数据并在给定版本值的情况下识别蓝色和绿色实例。我们可以使用属性在每个服务中设置元数据值。

例如eureka.instance.metadataMap.version=8675309

现在如果我们可以告诉尤里卡就好了。“停止使用 FUBAR 服务和版本 8675309 的所有实例。” 好吧,我认为这不是开箱即用的。Spring Cloud 最酷的地方在于,所有这些服务,包括 Eureka Server,都只是 Spring 应用程序,我们可以根据自己的需要进行破解。下面的代码公开了一个端点,该端点在给定应用名称和版本的情况下将实例设置为“停止服务”。只需将此控制器添加到您的 Eureka Server。它还没有准备好生产,只是一个想法。

现在,一旦 Eureka 停止服务这些实例并且 Ribbon 刷新其服务器列表,就可以安全地杀死或路由远离这些实例。

邮寄至:

http://[eurekahost:port]/takeInstancesOutOfService?applicationName=FOOBAR&version=8675309

希望有帮助吗?

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Application;
import com.netflix.eureka.EurekaServerContextHolder;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;

@RestController
public class EurekaInstanceStateController {

    @RequestMapping(value="/instancesQuery", method=RequestMethod.POST)
    public Collection<String> queryInstancesByMetaData(
            @RequestParam("applicationName") String applicationNameCriteria,
            @RequestParam("version") String versionCriteria)
    {
        return getRegistry().getSortedApplications()
                .stream()
                .filter(hasApplication(applicationNameCriteria))
                .flatMap(app -> app.getInstances().stream())
                .filter(hasVersion(versionCriteria))
                .map(info -> info.getAppName() + " - " + info.getId() + " - " + info.getStatus() + " - " + info.getMetadata().get("version"))
                .collect(Collectors.toList());
    }

    @RequestMapping(value="/takeInstancesOutOfService", method=RequestMethod.POST)
    public Collection<String> takeInstancesOutOfService(
            @RequestParam("applicationName") String applicationNameCriteria,
            @RequestParam("version") String versionCriteria)
    {
        return getRegistry().getSortedApplications()
                .stream()
                .filter(hasApplication(applicationNameCriteria))
                .flatMap(app -> app.getInstances().stream())
                .filter(hasVersion(versionCriteria))
                .map(instance -> updateInstanceStatus(instance, InstanceStatus.OUT_OF_SERVICE) )
                .collect(Collectors.toList());
    }

    /**
     * @param instance
     * @return
     */
    private String updateInstanceStatus(InstanceInfo instance, InstanceStatus status)
    {
        boolean isSuccess = getRegistry().statusUpdate(instance.getAppName(), instance.getId(),
        status, String.valueOf(System.currentTimeMillis()),
        true);

        return (instance.getAppName() + " - " + instance.getId() + " result: " + isSuccess);
    }

    /**
     * Application Name Predicate
     * @param applicationNameCriteria
     * @return
     */
    private Predicate<Application> hasApplication(final String applicationNameCriteria)
    {
        return application -> applicationNameCriteria.toUpperCase().equals(application.getName());
    }

    /**
     * Instance Version Predicate.  Uses Eureka Instance Metadata value name "version".</br>
     * 
     * Set / Bake the instance metadata map to contain a version value.</br>  
     * e.g. eureka.instance.metadataMap.version=85839c2
     * 
     * @param versionCriteria
     * @return
     */
    private Predicate<InstanceInfo> hasVersion(final String versionCriteria)
    {
        return info -> versionCriteria.equals(info.getMetadata().get("version"));
    }

    private PeerAwareInstanceRegistry getRegistry() {
        return EurekaServerContextHolder.getInstance().getServerContext().getRegistry();
    }
}
于 2016-05-05T21:53:43.073 回答