系列源碼地址
該文參考來自 程序猿DD 的Spring Cloud 微服務實戰一書,該文是作為閱讀了 spring cloud Zuul 一章的讀書筆記。書中版本比較老,我選擇了最新穩定版的 spring cloud Greenwich.SR2 版本,該版本較書中版本有些變動。非常感謝作者提供了這么好的學習思路,謝謝!文章也參考了 Spring-cloud-netflix 的官方文檔。
api網關是什么、路由是微服務體系結構的一個組成部分。例如 /
可能映射到你的 web應用程序,/api/users
映射到用戶服務,/api/shop
映射到商店服務。 Zuul
是一個來自 Netflix
的基于JVM
的路由器和服務端負載均衡器。DD 書里這樣一段話描述的非常好。“API 網關是一個更為智能的應用服務器,它的定義類似于面向對象設計模式中的 Facade 模式,它的存在就像是整個微服務架構系統的門面一樣,所有的外部客戶端訪問都需要經過它來進行調度和過濾。”
新建 api-gateway
工程
<dependencies><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></dependencies>
@EnableZuulProxy
@SpringCloudApplication
public class Application {public static void main(String[] args) {new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.SERVLET).run(args);}
}
@EnableZuulProxy
注解開啟 Zuul 的 API 網關服務功能。
spring:application:name: api-gateway
server:port: 60010
eureka:client:service-url:defaultZone: http://localhost:1112/eureka/
zuul:routes:api-a:# 配置對路徑 /api-a/ 的所有訪問將會路由到 hello-service 服務上path: /api-a/**serviceId: hello-serviceapi-b:# 配置對路徑 /api-b/ 的所有訪問將會路由到 feign-consumer 服務上path: /api-b/**serviceId: feign-consumer
運行工程,訪問地址 /api-b/feign-consumer
。
以上是我們基于服務的路由方式,那么對于傳統的路由,我們應該怎樣配置呢?
zuul:routes:api-a-url:# 配置對路徑 /api-a-url/ 的所有訪問將會路由到 http://localhost:8080/provider/ 服務上path: /api-a-url/**url: http://localhost:8080/provider/api-b-url:# 配置對路徑 /api-b-url/ 的所有訪問將會路由到 http://localhost:8082/ 服務上path: /api-b-url/**serviceId: http://localhost:8082/
apicloud vue,該配置和上面基于服務的路由配置是一樣的,訪問地址/api-b-url/feign-consumer
。
Spring Cloud創建了一個嵌入式 Zuul代理,以簡化UI應用程序希望對一個或多個后端服務進行代理調用的常見用例的開發。該特性對于用戶界面代理到所需的后端服務非常有用,從而避免了對所有后端獨立管理 CORS 和身份驗證問題的需要。
要啟用它,請使用@EnableZuulProxy
注釋Spring引導主類。這樣做會導致本地調用被轉發到適當的服務。按照慣例,具有用戶ID的服務接收位于/users的代理的請求(去掉前綴)。代理使用Ribbon
來定位要通過服務發現轉發到的服務實例。所有請求都在一個hystrix
命令中執行,因此失敗將出現在hystrix
指標中。一旦電路打開,代理服務器就不會試圖聯系服務。
Zuul Starter
并不包含服務發現客戶端,所以基于服務路由的方式,我們需要引入服務發現客戶端的依賴包。
簡單的路由方式并不會作為 HystrixCommand
執行,也不會用 Ribbon
來負載均衡多個 URL 。為了實現這一目標,我們可以使用靜態的服務器列表來指定 serviceId,如下:
zuul:routes:echo:path: /myusers/**serviceId: myusers-servicestripPrefix: truehystrix:command:myusers-service:execution:isolation:thread:timeoutInMilliseconds: ...myusers-service:ribbon:NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerListlistOfServers: https://example1.com,http://example2.comConnectTimeout: 1000ReadTimeout: 3000MaxTotalHttpConnections: 500MaxConnectionsPerHost: 100
udp服務器,默認情況下,使用 @EnableZuulProxy
注解后,將啟用兩個額外的端點(需要通過management.endpoints.web.exposure.include=routes,filters
對外暴露):
routes
使用 GET 請求訪問 /actuator/routes
,將返回路由映射列表:
{"/api-a/**": "hello-service","/api-b/**": "feign-consumer","/api-a-url/**": "http://localhost:8080/provider/","/api-b-url/**": "http://localhost:8082/","/feign-consumer/**": "feign-consumer","/eureka-server-node1/**": "eureka-server-node1","/hello-service/**": "hello-service","/eureka-server-node2/**": "eureka-server-node2"
}
你會發現,這里包含了我們未在配置文件中配置的路徑映射。原因是引入服務發現客戶端后,會有服務的自動映射。我們可以通過 zuul.ignored-services='*'
來忽略它,對于我們額外的路由配置,并不會被忽略。
為我們的配置文件添加完該配置后,再次訪問路由端點:
{"/api-a/**":"hello-service","/api-b/**":"feign-consumer","/api-a-url/**":"http://localhost:8080/provider/","/api-b-url/**":"http://localhost:8082/"
}
SpringCloud?額外的,我們可以通過 ?format=details
或者 /routes/details
來獲取路由詳細信息:
GET /routes/details.
{"/api-a/**": {"id": "api-a","fullPath": "/api-a/**","location": "hello-service","path": "/**","prefix": "/api-a","retryable": false,"customSensitiveHeaders": false,"prefixStripped": true},"/api-b/**": {"id": "api-b","fullPath": "/api-b/**","location": "feign-consumer","path": "/**","prefix": "/api-b","retryable": false,"customSensitiveHeaders": false,"prefixStripped": true},"/api-a-url/**": {"id": "api-a-url","fullPath": "/api-a-url/**","location": "http://localhost:8080/provider/","path": "/**","prefix": "/api-a-url","retryable": false,"customSensitiveHeaders": false,"prefixStripped": true},"/api-b-url/**": {"id": "api-b-url","fullPath": "/api-b-url/**","location": "http://localhost:8082/","path": "/**","prefix": "/api-b-url","retryable": false,"customSensitiveHeaders": false,"prefixStripped": true}
}
通過 POST
請求訪問 /routes
端點將強制刷新已經存在的路由,我們可以通過 endpoints.routes.enabled = false
來禁用該端點。
filters
GET
請求訪問 /filters
端點將返回過濾器類型映射。
{"error": [{"class": "org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter","order": 0,"disabled": false,"static": true}],"post": [{"class": "org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter","order": 1000,"disabled": false,"static": true}],"pre": [{"class": "org.springframework.cloud.netflix.zuul.filters.pre.DebugFilter","order": 1,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter","order": -1,"disabled": false,"static": true}, {"class": "com.duofei.filter.AccessFilter","order": 0,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter","order": -2,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter","order": -3,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter","order": 5,"disabled": false,"static": true}],"route": [{"class": "org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter","order": 100,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter","order": 10,"disabled": false,"static": true}, {"class": "org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilter","order": 500,"disabled": false,"static": true}]
}
api網關聚合。其中,com.duofei.filter.AccessFilter
是我自定義的過濾器,代碼如下:
public class AccessFilter extends ZuulFilter {private static Logger log = LoggerFactory.getLogger(AccessFilter.class);@Overridepublic String filterType() {return "pre";}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext currentContext = RequestContext.getCurrentContext();HttpServletRequest request = currentContext.getRequest();log.info("send {} request to {}",request.getMethod(),request.getRequestURL().toString());String accessToken = request.getParameter("accessToken");if (accessToken == null) {log.warn("access token is empty");currentContext.setSendZuulResponse(false);currentContext.setResponseStatusCode(401);return null;}log.info("access token ok");return null;}
}
還需要在啟動類中將該類添加到 Spring環境中:
@Beanpublic AccessFilter accessFilter(){return new AccessFilter();}
遷移現有應用程序或 API
時的一種常見模式是“扼殺”舊端點,用不同的實現緩慢地替換它們。Zuul
代理在這方面是一個有用的工具,因為您可以使用它來處理來自舊端點的所有流量,但將一些請求重定向到新端點。
application.yml
zuul:routes:first:path: /first/**url: https://first.example.comsecond:path: /second/**url: forward:/secondthird:path: /third/**url: forward:/3rdlegacy:path: /**url: https://legacy.example.com
在這個示例中,我們扼殺了 legacy
遺留程序;/first/**
中的路徑將映射到外部 URL 的新服務中,/second/**
的路徑將被轉發,因此我們可以在本地處理它,(像普通的 @RequestMapping
), /third/**
中的路徑也被轉發,但前綴不同(例如/third/foo
將被轉發到/3rd/foo
)。
網關、以上來自于 Spring 官方文檔,我對文檔中提到的功能做了實踐,在此做下記錄。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态