微服务架构中的超媒体响应
Hypermedia response in a microservice architecture
我已经开始使用 Spring Cloud 和 Netflix 的项目进一步了解此架构。
从一般的角度来看,我确实理解架构,但现在我实际上正在编写一个应用程序,对一些可能对该架构的大方法产生重大影响的最小的东西产生了怀疑。
首先,我的应用程序基于本教程,我认为它是有效的,因为它来自 Spring 本身:https://spring.io/blog/2015/07/14/microservices-with-spring
现在,问题是:(所有这些都使用 EUREKA)假设我有一个微服务 "accounts",它仅包含 Spring 数据 REST 用于 persistence/HATEOAS 和顶部其中我有一个使用 RestTemplate 使用这些端点的客户端服务(因为我使用的是 Ribbon 的负载均衡器)。在我看来,它是这样工作的:用户 -> 前端 -> 客户端服务 -> Spring 数据 REST 端点(微服务) -> 数据库。
这很好,但当我收到端点响应(来自 RestTemplate)时,它包含直接进入微服务的超媒体,就其本身而言,您现在可以在忽略负载均衡器的情况下驱动您通过微服务,对我来说,这扼杀了它的目的。如果我喜欢 5 个微服务相互扩展,这意味着用户现在只关注由负载均衡器管理的 5 个微服务的广泛方式之一。
Q1:
用户可以了解 API(然后有点杀死结构的一部分)吗?
即使我将链接添加到 Eureka 检测到的服务并删除该微服务的当前端点,这些链接也不会起作用,这意味着我正在切断超媒体链接。
Q2:
还有另一种方法吗?通过某种代理、外部负载平衡器、dns 或类似的东西?
P.D:不知道我说的够不够清楚。如果有什么不明白的地方请告诉我。
P.D.2: 总结性问题: 如何处理这些媒体链接?让它们成为或使用其他东西(请告诉)来实际获得它们 "right"?
编辑: 根据下面的建议,我添加了 Zuul,这似乎是我问题的解决方案。现在,关于 Zuul,我应该在哪里使用过滤?
我的应用程序现在是这样的:帐户微服务(Spring 数据 REST)和帐户网络服务(带有 RestTemplate 负载均衡器的客户端)。这些是 Eureka 注册名称。
@EnabledZuulProxy 必须去哪里?在微服务内部还是在客户端内部?
我目前的配置是这个(目前@EnabledZuulProxy在客户端):
我的微服务:
@SpringBootApplication
@EnableEurekaClient
public class AccountServer {
public static void main(String[] args) {
System.setProperty("spring.config.name", "AccountServerClient");
SpringApplication.run(AccountServer.class, args);
}
}
//Config:
spring.application.name=account-microservice
spring.application.freemarker.enabled=false
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
eureka.client.instance.leaseRenewalIntervalInSeconds=5
我的客户:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
@EnableCircuitBreaker
public class AccountMicroService {
public static void main(String[] args) {
// Tell server to look for registration.properties or registration.yml
System.setProperty("spring.config.name", "AccountMicroServiceClient");
SpringApplication.run(AccountMicroService.class, args);
}
}
//Config
# Spring properties
spring.application.name=account-web-service
spring.freemarker.enabled=false
eureka.instance.leaseRenewalIntervalInSeconds: 5
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
zuul.routes.account-web-service.path=/accounts/**
zuul.routes.account-web-service.serviceId=account-web-service
zuul.routes.account-web-service.stripPrefix=false
使用 SDR 的客户端代码:
//@HystrixCommand(fallbackMethod="test")
public ResponseEntity<PagedResources<AccountResource>> findAll(Pageable pageable) throws RestClientException, URISyntaxException {
return restTemplate.exchange(serviceUrl + "/accounts" + "?page={page}&size={size}&sort={sort}", HttpMethod.GET,
null, new ParameterizedTypeReference<PagedResources<AccountResource>>() {
}, pageable.getPageNumber(), pageable.getPageSize(), pageable.getSort());
}
这样子,根本不行。也没有过滤。如果我更改它并在微服务中使用它,那么它会不断将指向微服务的所有内容发送到客户端,甚至是来自客户端的请求,最终会出现循环然后出现错误(超时、负载均衡器已满、电路断开...) 通常是这样的:com.netflix.zuul.exception.ZuulException:转发错误。
编辑 2: 我终于让 Zuul 开始工作并了解它的作用。现在,当我访问我的新网关应用程序时,它将请求转发到我的微服务,并且生成的超媒体现在指向该网关,这正是我想要的。
问题最初是架构问题,后来变成了编码问题,但现在我想清楚了这些技术。
这杯茶了解 Zuul 做什么、何时以及如何做。 @Ryan Baxter 指南和这两篇文章:https://dzone.com/articles/microservice-architecture-with-spring-cloud-and-do and https://spring.io/guides/gs/routing-and-filtering/ 完成这项工作的基础知识。我将根据我所做的和我理解的内容编辑一个答案。
如果您使用来自 Spring Cloud Netflix 的 Zuul 作为您的负载均衡器,它会自动添加 X-Forwarded-Host
header,Spring HATEOAS 会遵守。当 header 存在时,HATEOAS 将使用 header 中的主机值生成链接。如果您不使用 Zuul,则必须配置负载均衡器以添加 header.
好的,我所做的只是使用 Zuul(正如@Ryan 所建议的)。事情是,最初,我有点没有得到它的方法。这是一个很小的架构问题,在用我添加到问题底部的两篇文章对@Ryan 的建议进行了内部化之后,终于把它们搞定了(希望如此)。
首先:现在我的项目正在使用 Zuul(通过按照我的两个链接建议创建网关应用程序)并且在其类路径中同时具有 Hybrix 和 Ribbon,Zuul 必须将它们连接在一起并变成一个多功能代理网关具有断路器和负载平衡特性。
其次:我摆脱了那个 rest 模板负载均衡器,因为:a) 获取微服务超媒体的响应,因为我没有转发端口 header,这几乎使 Zuul 的代理功能无效;和 b) 因为 Zuul 充当网关代理负载均衡器,所以我确实认为使用 rest 模板再次使用 ribbon 有点样板。
现在,我的应用是这样的:用户 -> {[Zuul 的网关 -> Spring Data REST 微服务(由 Zuul 转发)] <- Eureka}。
我会接受@Ryan 的回答,因为他准确地指出了发生的事情以及我最终做了什么来解决我的问题。不过,我也在回答,因为我认为对我最终所做的事情的进一步解释可能对另一个 SO 用户有帮助。
如果我的解决方案有问题,请评论。
我已经开始使用 Spring Cloud 和 Netflix 的项目进一步了解此架构。
从一般的角度来看,我确实理解架构,但现在我实际上正在编写一个应用程序,对一些可能对该架构的大方法产生重大影响的最小的东西产生了怀疑。
首先,我的应用程序基于本教程,我认为它是有效的,因为它来自 Spring 本身:https://spring.io/blog/2015/07/14/microservices-with-spring
现在,问题是:(所有这些都使用 EUREKA)假设我有一个微服务 "accounts",它仅包含 Spring 数据 REST 用于 persistence/HATEOAS 和顶部其中我有一个使用 RestTemplate 使用这些端点的客户端服务(因为我使用的是 Ribbon 的负载均衡器)。在我看来,它是这样工作的:用户 -> 前端 -> 客户端服务 -> Spring 数据 REST 端点(微服务) -> 数据库。
这很好,但当我收到端点响应(来自 RestTemplate)时,它包含直接进入微服务的超媒体,就其本身而言,您现在可以在忽略负载均衡器的情况下驱动您通过微服务,对我来说,这扼杀了它的目的。如果我喜欢 5 个微服务相互扩展,这意味着用户现在只关注由负载均衡器管理的 5 个微服务的广泛方式之一。
Q1: 用户可以了解 API(然后有点杀死结构的一部分)吗?
即使我将链接添加到 Eureka 检测到的服务并删除该微服务的当前端点,这些链接也不会起作用,这意味着我正在切断超媒体链接。
Q2: 还有另一种方法吗?通过某种代理、外部负载平衡器、dns 或类似的东西?
P.D:不知道我说的够不够清楚。如果有什么不明白的地方请告诉我。
P.D.2: 总结性问题: 如何处理这些媒体链接?让它们成为或使用其他东西(请告诉)来实际获得它们 "right"?
编辑: 根据下面的建议,我添加了 Zuul,这似乎是我问题的解决方案。现在,关于 Zuul,我应该在哪里使用过滤?
我的应用程序现在是这样的:帐户微服务(Spring 数据 REST)和帐户网络服务(带有 RestTemplate 负载均衡器的客户端)。这些是 Eureka 注册名称。
@EnabledZuulProxy 必须去哪里?在微服务内部还是在客户端内部?
我目前的配置是这个(目前@EnabledZuulProxy在客户端):
我的微服务:
@SpringBootApplication
@EnableEurekaClient
public class AccountServer {
public static void main(String[] args) {
System.setProperty("spring.config.name", "AccountServerClient");
SpringApplication.run(AccountServer.class, args);
}
}
//Config:
spring.application.name=account-microservice
spring.application.freemarker.enabled=false
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
eureka.client.instance.leaseRenewalIntervalInSeconds=5
我的客户:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
@EnableCircuitBreaker
public class AccountMicroService {
public static void main(String[] args) {
// Tell server to look for registration.properties or registration.yml
System.setProperty("spring.config.name", "AccountMicroServiceClient");
SpringApplication.run(AccountMicroService.class, args);
}
}
//Config
# Spring properties
spring.application.name=account-web-service
spring.freemarker.enabled=false
eureka.instance.leaseRenewalIntervalInSeconds: 5
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
zuul.routes.account-web-service.path=/accounts/**
zuul.routes.account-web-service.serviceId=account-web-service
zuul.routes.account-web-service.stripPrefix=false
使用 SDR 的客户端代码:
//@HystrixCommand(fallbackMethod="test")
public ResponseEntity<PagedResources<AccountResource>> findAll(Pageable pageable) throws RestClientException, URISyntaxException {
return restTemplate.exchange(serviceUrl + "/accounts" + "?page={page}&size={size}&sort={sort}", HttpMethod.GET,
null, new ParameterizedTypeReference<PagedResources<AccountResource>>() {
}, pageable.getPageNumber(), pageable.getPageSize(), pageable.getSort());
}
这样子,根本不行。也没有过滤。如果我更改它并在微服务中使用它,那么它会不断将指向微服务的所有内容发送到客户端,甚至是来自客户端的请求,最终会出现循环然后出现错误(超时、负载均衡器已满、电路断开...) 通常是这样的:com.netflix.zuul.exception.ZuulException:转发错误。
编辑 2: 我终于让 Zuul 开始工作并了解它的作用。现在,当我访问我的新网关应用程序时,它将请求转发到我的微服务,并且生成的超媒体现在指向该网关,这正是我想要的。
问题最初是架构问题,后来变成了编码问题,但现在我想清楚了这些技术。
这杯茶了解 Zuul 做什么、何时以及如何做。 @Ryan Baxter 指南和这两篇文章:https://dzone.com/articles/microservice-architecture-with-spring-cloud-and-do and https://spring.io/guides/gs/routing-and-filtering/ 完成这项工作的基础知识。我将根据我所做的和我理解的内容编辑一个答案。
如果您使用来自 Spring Cloud Netflix 的 Zuul 作为您的负载均衡器,它会自动添加 X-Forwarded-Host
header,Spring HATEOAS 会遵守。当 header 存在时,HATEOAS 将使用 header 中的主机值生成链接。如果您不使用 Zuul,则必须配置负载均衡器以添加 header.
好的,我所做的只是使用 Zuul(正如@Ryan 所建议的)。事情是,最初,我有点没有得到它的方法。这是一个很小的架构问题,在用我添加到问题底部的两篇文章对@Ryan 的建议进行了内部化之后,终于把它们搞定了(希望如此)。
首先:现在我的项目正在使用 Zuul(通过按照我的两个链接建议创建网关应用程序)并且在其类路径中同时具有 Hybrix 和 Ribbon,Zuul 必须将它们连接在一起并变成一个多功能代理网关具有断路器和负载平衡特性。
其次:我摆脱了那个 rest 模板负载均衡器,因为:a) 获取微服务超媒体的响应,因为我没有转发端口 header,这几乎使 Zuul 的代理功能无效;和 b) 因为 Zuul 充当网关代理负载均衡器,所以我确实认为使用 rest 模板再次使用 ribbon 有点样板。
现在,我的应用是这样的:用户 -> {[Zuul 的网关 -> Spring Data REST 微服务(由 Zuul 转发)] <- Eureka}。
我会接受@Ryan 的回答,因为他准确地指出了发生的事情以及我最终做了什么来解决我的问题。不过,我也在回答,因为我认为对我最终所做的事情的进一步解释可能对另一个 SO 用户有帮助。
如果我的解决方案有问题,请评论。