爲何要有服務網關?在使用微服務架構時,一個客戶端的業務需求可能會調用多個服務的接口,例如一次購物,須要調用商品服務,下單的時候要調用訂單服務(服務劃分更細的話還要調用更多的微服務)。客戶端直接跟各個服務通訊會有一些問題或者說弊端,客戶端的調用會複雜,更重要的是會有跨域請求問題和複雜的權限控制認證java
爲了對外服務的安全性,不得不在原有的服務接口上作有關權限控制的校驗邏輯,而這些權限相關的邏輯應該要把它從各個服務中抽離出來,做爲外部調用和各個服務之間的負載均衡器。服務網關就是這樣一個角色,對外統一 Rest API 接口,對內服務路由、負載均衡,同時還提供身份認證安全和監控功能git
Zuul 是 Netflix 開源的服務網關,它的核心就是一系類的過濾器,經過一系列的過濾器在請求的各個階段進行處理,具體的路由器功能見 Spring Cloud Zuul 過濾器github
這裏使用 Eureka Server 做爲服務註冊中心web
spring: application: name: eureka-server server: port: 8761 eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
另外準備一個服務 product-service
spring
spring: application: name: product-service server: port: 8071 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
建立 Spring Boot 工程命名爲 zuul-gateway
做爲服務網關,添加以下依賴json
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在主類上添加註解 @EnableZuulProxy
開啓 Zuul 代理,這個代理會使用 Ribbon 獲取註冊服務的實例,同時還整合了 Hystrix 實現容錯,全部請求都會在 Hystrix 命令中執行。api
@SpringBootApplication @EnableZuulProxy public class GatewayZuulApplication { public static void main(String[] args) { SpringApplication.run(GatewayZuulApplication.class, args); } }
@EnableCircuitBreaker @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(ZuulProxyMarkerConfiguration.class) public @interface EnableZuulProxy { }
配置信息,添加 eureka 註冊中心的地址跨域
spring: application: name: gateway-zuul server: port: 8090 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
到這裏一個簡單的服務網關就完成了,只是簡單吧把網關服務註冊到 Eureka,Spring Cloud Zuul 整合了 Eureka,會提供默認的服務路由功能,默認狀況下,Zuul 會代理全部註冊到 Eureka Server 上的微服務安全
gateway-zuul 的請求 /{serviceId}/**
會轉發到對應註冊在 Eureka Server 上對應 serviceId 的服務 /**
上
啓動 eureka-server、product-service、zuul-gateway,而後訪問 http://localhost:8090/product-service/product/1 ,該請求會被轉發到 product-service 的 /product/1
架構
另外想要跳過一些服務能夠設置 zuul.ignoredServices
,要得到對路由的更細粒度控制,能夠單獨指定路徑和 serviceId
zuul: ignoredServices: '*' routes: product: path: /product/** serviceId: product-service
上面設置的是除 product 服務以外的服務都忽略,而訪問 product-service 服務的路徑爲 /product/**
參考代碼見:demo
@EnableZuulProxy
註解配合 Spring Boot Actuator,Zuul 會暴露額外的兩個管理端點:Routes
和 Filters
。分別是關於路由和過濾器的端點(過濾器的端點在這裏介紹 Spring Cloud Zuul 過濾器)
spring-cloud-starter-netflix-zuul
已經依賴了 spring-boot-starter-actuator
,因此上面的工程已經包含了路由管理的功能。關於路由端點的路徑爲 /routes
和 /routes/details
訪問路徑 http://localhost:8090/actuator/routes
{ "/product/**": "product-service" }
訪問路徑 http://localhost:8090/actuator/routes/details,能夠查看路由的詳細信息
{ "/product/**": { "id": "product", "fullPath": "/product/**", "location": "product-service", "path": "/**", "prefix": "/product", "retryable": false, "customSensitiveHeaders": false, "prefixStripped": true } }
訪問404是由於沒有暴露端點,能夠設置 management.endpoints.web.exposure.include: '*'
默認狀況下,Zuul 網關會代理全部註冊到 Eureka Server 上的服務,但咱們能夠經過配置來讓其只代理其中一部分的服務或者是本身控制 URL。Zuul 的路由配置很是的靈活
自定義訪問路徑。zuul.routes.{serviceId}={costomUrl}
zuul: routes: product-service: /product/**
忽略指定服務,多個用逗號隔開,忽略所有用 *
zuul: ignored-services: product-service1,product-service2
zuul: ignored-services: '*' routes: product-service: /product/** # 忽略全部服務,只路由 product-service
同時指定微服務的 serviceId 和對應路徑
zuul: ignoredServices: '*' routes: product: # 只是一個路由名稱 service-id: product-service path: /product/**
同時指定地址和訪問路徑
zuul: ignoredServices: '*' routes: product: url: http://localhost:8081/ path: /product/**
添加路由前綴
zuul: perfix: /api strip-perfix: false routes: product-service: /product/** # /api/product/** -> /api/**
zuul: routes: product-service: /product/** strip-perfix: false # /product/** -> /product/**
忽略某些路徑
zuul: ignoredPatterns: /**/admin/** #忽略全部包含 /admin/ 的路徑 routes: product-service: /product/**
在 Spring Cloud 中,Zuul 已經默認整合了 Hystrix,關於 Hystrix 介紹和監控面板能夠見 Spring Cloud 斷路器 Hystrix。咱們使用上面的例子
eureka-server
、product-service
、zuul-gateway
http://localhost:8090/actuator/hystrix.stream
,能夠看到以下信息由上圖能夠看出,Zuul 的 Hystrix 監控粒度爲服務,而不是接口。
接下來咱們能夠關閉服務 product-service
,而後再訪問接口 http://localhost:8090/product/product/1
爲 Zuul 添加回退
爲 Zuul 添加回退,須要實現 FallbackProvider 接口,須要指定回退用於的路由ID,並提供一個 ClientHttpResponse 做爲回退返回
public class ProductFallbackProvider implements FallbackProvider { @Override public String getRoute() { return "product-service"; } @Override public ClientHttpResponse fallbackResponse(String route, final Throwable cause) { if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { return response(HttpStatus.INTERNAL_SERVER_ERROR); } } private ClientHttpResponse response(final HttpStatus status) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return status; } @Override public int getRawStatusCode() throws IOException { return status.value(); } @Override public String getStatusText() throws IOException { return status.getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("fallback".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
若是您想爲全部路由提供默認的回退,您能夠建立一個類型爲 FallbackProvider 的bean,並讓 getRoute 方法返回
*
或null
重啓服務網關,訪問接口 http://localhost:8090/product/product/1