在微服務架構中,可能有許多API服務和與API通訊的UI元件很少。 到目前為止,許多基于微服務的應用程式仍使用整體系統将其作為單個子產品建構的整體式前端。 您可能會選擇使用微前端,在該微前端中,UI也被分解為與API通訊以擷取相關資料的多個微服務。 無需讓UI知道我們所有的微服務細節,我們可以提供一個統一的代理接口,該接口将根據URL模式将調用委派給各種微服務。 在本文中,我們将學習如何使用Spring Cloud Zuul Proxy建立API網關 。
使用Spring Boot和Spring Cloud的微服務
- 第1部分:MicroServices:Spring Boot和Spring Cloud概述
- 第2部分:MicroServices:使用Spring Cloud Config和Vault進行配置管理
- 第3部分:MicroServices:Spring Cloud Service注冊和發現
- 第4部分:微服務:使用Netflix Hystrix的Spring Cloud Breaker
- 第5部分:MicroServices:Spring Cloud Zuul代理作為API網關
在這篇文章中,我們将學習:
- 為什麼我們需要API網關?
- 使用Spring Cloud Zuul代理實作API網關
- 使用Zuul過濾器解決橫切關注點
為什麼我們需要API網關?
API網關 (又名Edge服務)為一組微服務提供了統一的接口,是以用戶端無需了解微服務内部的所有詳細資訊。 但是,在微服務體系結構中使用API網關模式有一些優缺點。
優點:
- 為客戶提供更輕松的界面
- 可用于防止将内部微服務結構暴露給用戶端
- 允許重構微服務,而無需強制用戶端重構消耗邏輯
- 可以集中解決諸如安全性,監視,速率限制等跨領域問題
缺點:
- 如果不采取适當措施使其高度可用,則可能會成為單點故障
- 各種微服務API的知識可能會滲入API網關
使用Spring Cloud Zuul代理實作API網關
Spring Cloud提供類似于Nginx的 Zuul代理,可用于建立API網關。
讓我們建立一個前端UI子產品“ shoppingcart-ui”作為SpringBoot應用程式,該應用程式還充當Zuul代理。 使用Web , Config Client , Eureka Discovery , Zuul啟動器建立一個SpringBoot項目,并使用@EnableZuulProxy注釋主入口點類。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
ShoppingcartUiApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ShoppingcartUiApplication {
public static void main(String[] args) {
SpringApplication.run(ShoppingcartUiApplication.class, args);
}
}
當我們也使用Eureka Discovery時,來自具有URL模式/ service-id / **的代理的請求将被路由到在Eureka Server中注冊的服務ID為service-id的服務 。
例如:從UI應用程式,如果我們向http:// localhost:8080 / catalog-service / products送出請求,則它将在Service Registry中查找ServiceID “ catalog-service” ,并将帶有URL / products的請求發送給可用的目錄服務執行個體。
為此,我們需要在Eureka服務注冊中心注冊“ shoppingcart-ui”。
bootstrap.properties
spring.application.name=shoppingcart-ui
server.port=8080
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
現在,通過這種配置,我們可以使用jQuery從目錄服務中擷取産品資訊,如下所示:
$.ajax({
url: '/catalog-service/products'
})
.done(function(data) {
this.products = data;
}.bind(this));
在這裡,從UI應用程式中,我們正在調用http:// localhost:8080 / catalog-service / products。 假設向目錄服務注冊了ServiceID“目錄服務”并在端口8181上運作,則此請求将轉發到http:// host:8181 / products。 但是,UI完全不知道實際的目錄服務在哪裡運作,其主機名端口号等。
我們還可以為URL使用通用字首,例如“ / api” ,我們希望Zuul通過設定zuul.prefix屬性來為其代理。
zuul.prefix=/api
現在,我們可以通過UI在http:// localhost:8080 / api / catalog-service / products上請求擷取産品。 預設情況下,Zuul将删除字首并轉發請求。
您還可以如下自定義服務的路徑映射:
zuul.routes.catalogservice.path=/catalog/**
zuul.routes.catalogservice.serviceId=catalog-service
通過此配置,您可以使用URL http:// localhost:8080 / api / catalog / products,該URL将通過serviceId catalog-service轉發到服務。
預設情況下,将向Eureka Server注冊的所有服務都将公開。 您可以使用zuul.ignored-services屬性來禁用此行為,并僅公開顯式配置的服務。
zuul.ignored-services=*
zuul.routes.catalogservice.path=/catalog/**
zuul.routes.catalogservice.serviceId=catalog-service
zuul.routes.orderservice.path=/orders/**
zuul.routes.orderservice.serviceId=order-service
通過這種配置,隻有catalog-service , order-service通過Zuul代理公開,而不會通過庫存服務公開。
使用Zuul過濾器解決橫切關注點
由于Zuul充當了我們所有微服務的代理,是以我們可以使用Zuul服務來實作一些跨領域的問題,例如安全性,速率限制等。一種常見的用例是将Authentication标頭轉發到所有下遊服務。
通常在微服務中,我們将使用OAuth服務進行身份驗證和授權。 用戶端通過身份驗證後,OAuth服務将生成一個令牌,該令牌應包含在對其他微服務的請求中,是以無需分别為每個服務都對用戶端進行身份驗證。 我們可以使用Zuul過濾器來實作這樣的功能。
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
public class AuthHeaderFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (request.getAttribute("AUTH_HEADER") == null) {
//generate or get AUTH_TOKEN, ex from Spring Session repository
String sessionId = UUID.randomUUID().toString();
ctx.addZuulRequestHeader("AUTH_HEADER", sessionId);
}
return null;
}
}
我們使用RequestContext.addZuulRequestHeader()将AUTH_HEADER添加為請求标頭,該标頭将轉發到下遊服務。 我們需要将其注冊為Spring bean。
@Bean
AuthHeaderFilter authHeaderFilter() {
return new AuthHeaderFilter();
}
您可以在https://github.com/sivaprasadreddy/spring-boot-microservices-series上找到本文的源代碼。
翻譯自: https://www.javacodegeeks.com/2018/03/microservices-part-5-spring-cloud-zuul-proxy-as-api-gateway.html