转载

Spring Cloud集中环境中开发如何避免服务冲突

使用中央环境开发Spring Cloud微服务,同时避免服务冲突。开发人员如何在同一个中央弹簧云环境中同时工作并且仍然不会互相干扰?

Spring Cloud集中环境中开发如何避免服务冲突

使用 spring boot 和 spring cloud 时,开发基于微服务架构的软件非常容易。只需输入几行代码就可以启动并运行微服务。但是,如何在这样的环境中开发真实世界的应用程序呢?从理论上讲,每个微服务都是孤立的,可以单独开发,但实际上并非如此。要在使用它的应用程序的上下文中开发和测试您的服务,不仅需要您的微服务启动和运行。那么,如何在多微服务环境中方便地开发呢?

好吧,如果您只需要两到三个服务,那么您可以在本地运行它们,因此设置这样的环境并不是什么大问题。但是,如果您的应用程序由许多服务(大型应用程序的常见情况)组成,那么启动这样的环境,保持最新状态等等可能会变成一个真正令人头痛的问题。

另一个极端,在本地只运行一个微服务,其余的在一些中央服务器上也是一个问题:

例如,如果我正在开发'MyService'微服务,并且中央环境也有一个“MyService”服务正在运行(所有开发人员都在使用这个环境,因此它运行了所有服务,公共发现服务会同时引用这两个服务,当许多开发人员正在使用这样的环境时,问题当然会变得更糟。

我们找到了一种享受这两个世界的优雅方式 - 每个开发人员只在本地运行他或她当前正在处理的服务,而所有其他服务都在某个中央环境中运行,我们设法避免实例之间的冲突和混淆那个服务!

这种魔力是如何发生的?好吧,我们最初问题的根源是开发人员正在处理的服务及其在中央环境(以及其他开发人员的机器上)上的匹配实例在发现服务上使用相同的名称注册(我们使用Eureka,顺便说说)。如果每个实例都使用不同的名称注册自己并且仍然可以被需要它的任何其他服务使用,该怎么办?嗯,有可能!但这有点棘手。这些是必要的步骤:

1.对于每个服务(或在所有服务正在使用的一个基础架构jar中),我们定义一个@Configuration bean(RemoteEurekaConfig)来调整对Eureka的注册。在这个类中,我们返回一个EurekaInstanceConfigBean,它通过将主机名添加到已注册的服务来覆盖超类行为。这样'MyService'将被注册为' MyHostName.MyService',因此我和我的开发人员每个人都有这个服务的唯一名称,允许我们同时处理它(而不是与' MyService冲突)'中央环境的实例,所以其他人可以在开发其他服务时使用它)。

我们通过将application属性值设置是否为true来确定是否要执行此行为(在我的示例中名为devDiscovery)。您还可以使用当前配置文件(dev / prod)或任何其他所需标志来确定您的服务是否应更改默认发现服务注册。此外,您可以将newAppName设置为您想要的任何唯一值(开发人员名称,您使用的版本等),只要它在开发人员中是唯一的并且足够有意义。

RemoteEurekaConfig:

@Value(“${dev.discovery:false}”)
<b>private</b> Boolean devDiscovery;
@Bean
@Autowired
@Profile(“development”)
<b>public</b> EurekaInstanceConfigBean eurekaInstanceConfigBean(<b>final</b> InetUtils inetUtils) {

String newAppName = getHostname() + “.” + appName;

config = <b>new</b> EurekaInstanceConfigBean(inetUtils) {

@Override

<b>public</b> <b>void</b> setEnvironment(Environment environment) {

<b>super</b>.setEnvironment(environment);

<b>if</b> (devDiscovery != <b>null</b> && devDiscovery == <b>true</b>) {

setAppname(newAppName);

setVirtualHostName(newAppName);

setSecureVirtualHostName(newAppName);

}

}

};

config.setNonSecurePort(port);

config.setIpAddress(getHostAddress());

config.getMetadataMap().put(“instanceId”, config.getHostname() + “:” + config.getAppname() + “:” + port);

<b>return</b> config;

}

2.现在,我们的服务是登记在远程尤里卡一个独特的名字其实是不够的,因为我们的网关仍会路由UI(或其他)请求的“MyService ”实例,而不是“ MyHostName.MyService”,因为这些路由是在Gateway定义的。

每当我们在本地运行服务时,我们都可以在application.properties文件中修改这些路由,但这可能容易出错且繁琐。

我们可以做得更好 - 我们可以通过定义一个新的bean DynamicRouting来动态处理它,在初始化时将遍历所有已注册的服务,并将本地路由更新为本地运行的服务。

它如何知道哪些服务在本地运行?简单 - 这样的服务将有我们独特的前缀,当然:)。我们应该迭代所有已注册的服务,因为我们可能在本地运行多个服务。

不过,使用此解决方案,本地运行的服务应该在Gateway之前启动。如果需要更动态的行为,我们可以每隔X秒应用此逻辑以始终保持最新状态(尽管在大多数情况下我发现它是一种过度杀伤)。

DynamicRouting:

@Autowired
<b>private</b> ZuulProperties zuulProperties;

@Autowired
DiscoveryClient discoveryClient

@PostConstruct
<b>public</b> <b>void</b> init() {

<font><i>// Get all services from Eureka</i></font><font>

List<String> allServices = discoveryClient.getServices();

String prefix = getHostname()+”.”;

<b>for</b>(String service : allServices) {

</font><font><i>// If a service starts with my designated prefix, replace the original route to it</i></font><font>

<b>if</b> (service.startsWith(prefix)) {

String originalService = service.substring(service.indexOf(“.”)+1);

<b>for</b>(ZuulProperties.ZuulRoute route : zuulProperties.getRoutes().values())

{

<b>if</b> (route.getServiceId().equals(originalService)) {

</font><font><i>// Change original route to ‘my’ service id</i></font><font>

route.setServiceId(service);

}
}
}
}
}
</font>

3.我们差不多完成了!最后一件事 - 如果你直接从其他服务而不是通过网关使用REST来调用服务,你也必须要处理它。例如,如果您使用Spring的RestTemplate,则必须将其包装并应用与上面相同的逻辑,这意味着:

  • 确定这是服务呼叫还是对实际URL的调用(并且不对后者执行任何操作)。例如,服务调用将类似于 http://MyService/sth/1
  • 通过查询Eureka检查服务调用是否是对本地运行服务的调用,并检查前缀。
  • 如果是这样,请将URL的主机部分更改为本地运行的服务名称,如前所述。例如 http://myHostName.MyService/sth/1 并且应该调用本地服务!

您现在可以在一个中央环境中快速运行数百个微服务的系统,开发人员可以在本地仅运行一个服务的同时进行开发,节省资源和时间,同时始终自动同步。

要记住两件事:

- 此处显示的相关bean应该是@Profile(“development”)的注释,并且不应该在开发development之外处于active 状态,以避免混淆。

- 由于我们在工作场所编写完整堆栈,因此我们始终在本地运行Gateway。如果您只需要后端,则无需在本地运行Gateway,只需使用Swagger或Postman(或类似)来调用服务API。如果您确实想要使用应用程序的UI并且不想在 本地运行Gateway ,也可以通过向应用程序的URL添加一些信息(本地服务前缀和本地运行的服务的通用名称)来完成此操作。 。然后,UI可以轻松地将REST调用地址替换为本地服务名称(就像我们在服务器中所做的那样),并且它们将被路由到开发人员的计算机。

原文  https://www.jdon.com/51706
正文到此结束
Loading...