SpringCloud中如何使用Zuul路由網(wǎng)關(guān),相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
公司主營業(yè)務(wù):網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出翁源免費(fèi)做網(wǎng)站回饋大家。
網(wǎng)關(guān)可以將所有的api統(tǒng)一暴露,保護(hù)其他服務(wù)api接口和信息,以防止被外界直接調(diào)用
網(wǎng)關(guān)可以做身份認(rèn)證和權(quán)限認(rèn)證,防止非法請求操作API,保護(hù)服務(wù)
網(wǎng)關(guān)可以實(shí)現(xiàn)監(jiān)控功能,實(shí)時(shí)日志輸出,對請求進(jìn)行記錄
網(wǎng)關(guān)可以實(shí)現(xiàn)流量監(jiān)控,便于在高流量情況下對服務(wù)進(jìn)行降級
Zuul可以和Ribbon、Eureka相結(jié)合,實(shí)現(xiàn)智能路由和負(fù)載均衡
通過一定策略,把請求流量分發(fā)到集群狀態(tài)的多個(gè)實(shí)例中
可以將API從內(nèi)部接口中分離出來,方便單測
Zuul基于Servlet實(shí)現(xiàn),通過自定義的ZuulServlet來對請求進(jìn)行控制,Zuul中有一系列的過濾器,這些過濾器是同樣是Zuul的實(shí)現(xiàn)關(guān)鍵,請求發(fā)起和響應(yīng)期間,通過這些過濾器實(shí)現(xiàn)Zuul的功能。具體有以下四個(gè):
PRE過濾器:在請求路由到具體的服務(wù)之前執(zhí)行,用途:安全驗(yàn)證(身份校驗(yàn),參數(shù)校驗(yàn)、ip黑白名單);
ROUTING過濾器 :在請求的服務(wù)到具體的微服務(wù)實(shí)例時(shí)執(zhí)行,用途:進(jìn)行網(wǎng)絡(luò)請求(默認(rèn)使用HttpClient);
POST過濾器:在請求路由到微服務(wù)之后執(zhí)行,用途:統(tǒng)計(jì)信息,回傳響應(yīng);
ERROR過濾器:在其他過濾器發(fā)生錯(cuò)誤的時(shí)候執(zhí)行,用途:保證請求能夠正確響應(yīng);
ZuulFilter中的方法有以下四個(gè),繼承ZuulFilter并且重寫以下四個(gè)方法即可實(shí)現(xiàn)一個(gè)過濾器。
public String filterType(); 返回該Filter的類型,即如上四種過濾器。
public int filterOrder(); 返回該過濾器的執(zhí)行順序。
public boolean shouldFilter(); 返回該過濾器是否需要執(zhí)行。
public Object run(); 執(zhí)行具體的過濾邏輯。
ZuulServlet
是Zuul的核心Servlet,負(fù)責(zé)初始化ZuulFilter
并且編排這些過濾器,具體代碼在service()
方法中。
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { try { this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse); RequestContext context = RequestContext.getCurrentContext(); context.setZuulEngineRan(); try { this.preRoute(); } catch (ZuulException var13) { this.error(var13); this.postRoute(); return; } try { this.route(); } catch (ZuulException var12) { this.error(var12); this.postRoute(); return; } try { this.postRoute(); } catch (ZuulException var11) { this.error(var11); } } catch (Throwable var14) { this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName())); } finally { RequestContext.getCurrentContext().unset(); } }
RequestContext
是ZuulFilter
中負(fù)責(zé)上下文銜接的角色,其本身是一個(gè)ConcurrentHashMap
,包含Request和Response、routeType、routeHost等上下文需要的對象。
父級項(xiàng)目zuul-test/pom.xml
com.calvin.zuul zuul-test pom 1.0-SNAPSHOT eureka-server user-service zuul-service org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-dependencies Dalston.SR4 pom import
zuul-service/pom.xml
zuul-test com.calvin.zuul 1.0-SNAPSHOT 4.0.0 zuul-service org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-zuul org.springframework.boot spring-boot-maven-plugin
zuul-service/ZuulApplication.java
/** ** 啟動(dòng)類 *
* @author Calvin * @date 2019/10/25 * @since 1.0 */ @SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ZuulServerApplication { public static void main(String[] args) { SpringApplication.run(ZuulServerApplication.class, args); } }
zuul-service/application.yml
spring: application: name: zuul-service server: port: 8030 eureka: client: service-url: defaultZone: http://localhost:8010/eureka/ instance: hostname: localhost zuul: routes: user-api: path: /user/** serviceId: user-service
user-service/pom.xml
zuul-test com.calvin.zuul 1.0-SNAPSHOT 4.0.0 user-service org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin
user-service/application.yml
spring: application: name: user-service server: port: 8020 eureka: client: service-url: defaultZone: http://localhost:8010/eureka/ instance: hostname: localhost
user-service/UserApplication.java
/** ** 啟動(dòng)類 *
* @author Calvin * @date 2019/10/25 * @since 1.0 */ @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
user-service/controller/UserController.java
/** ** * @author Calvin * @date 2019/10/25 * @since */ @RestController public class UserController { /** * 簡單構(gòu)建一個(gè)User */ @GetMapping("/userDetail/{userId}") public SysUser getUserDetail(@PathVariable("userId") String userId){ SysUser sysUser = new SysUser(); sysUser.setId(userId); sysUser.setAge(20); sysUser.setPassword(MD5Utils.digestAsHex("123456")); sysUser.setUsername("Calvin"); //圖片來自百度 sysUser.setHeadImg("https://b-ssl.duitang.com/uploads/item/201510/17/20151017181645_c5hWE.thumb.700_0.jpeg"); return sysUser; } }
依次啟動(dòng)eureka-server, user-server, zuul-server
瀏覽器調(diào)用 http://localhost:8030/user/userDetail/1
從調(diào)用結(jié)果中可以看到我們從zuul-service中調(diào)用了user-service的方法,并且調(diào)用成功。從而證明路由配置可用;
如需配置版本號,我們只需要咱zuul-service/application.yml中添加配置:zuul.prefix=v1
如果需要在Zuul中實(shí)現(xiàn)服務(wù)熔斷,只需要實(shí)現(xiàn)ZuulFallbackProvider
接口,重寫其中兩個(gè)方法,通過getRoute()
方法返回我們需要熔斷的路由,通過fallbackResponse()
方法來重寫熔斷時(shí)執(zhí)行的邏輯。
如下,我們實(shí)現(xiàn)第一個(gè)user-service的熔斷器
/** ** user-service熔斷器 *
* * @author Calvin * @date 2019/10/27 * @since */ @Component public class UserServiceCallback implements ZuulFallbackProvider { @Override public String getRoute() { return "user-service"; } @Override public ClientHttpResponse fallbackResponse() { return new CommonClientResponse(); } }
貼上CommonClientResponse
的代碼,就是針對ClientHttpResponse接口的封裝
/** *封裝的通用返回類
* * @author Calvin * @date 2019/10/27 * @since */ public class CommonClientResponse implements ClientHttpResponse { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 0; } @Override public String getStatusText() throws IOException { return "success"; } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("hello , this is zuul fallback".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }
接下來做個(gè)測試,我們停掉user-service服務(wù),然后再訪問 http://localhost:8030/user/userDetail/1
結(jié)果當(dāng)然是我們定義的熔斷器中返回的內(nèi)容了!
如果需要對其他服務(wù)使用同一個(gè)熔斷器,只需要在
getRoute()
方法中返回通配符return "*"
就可以了 ?
ZuulFilter
是Zuul實(shí)現(xiàn)過濾和網(wǎng)關(guān)的關(guān)鍵,此類有四個(gè)枚舉值,分別代表Zuul中的過濾器類型。如果需要實(shí)現(xiàn)過濾,只需要繼承ZuulFilter
,并且指定其過濾器類型,枚舉值為:
/** * {@link ZuulFilter#filterType()} error type. */ String ERROR_TYPE = "error"; /** * {@link ZuulFilter#filterType()} post type. */ String POST_TYPE = "post"; /** * {@link ZuulFilter#filterType()} pre type. */ String PRE_TYPE = "pre"; /** * {@link ZuulFilter#filterType()} route type. */ String ROUTE_TYPE = "route";
簡單實(shí)現(xiàn)一個(gè)過濾器
/** *header過濾器
* * @author Calvin * @date 2019/10/27 * @since */ @Component public class TokenFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(TokenFilter.class); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); logger.info("request_url : {}, request_headers: {}",request.getRequestURI(), JSON.toJSON(request.getHeaderNames()).toString()); return null; } }
重新啟動(dòng)zuul-service,調(diào)用服務(wù)控制臺已經(jīng)可以輸出如下內(nèi)容:
2019-11-12 22:04:36.726 INFO 58984 --- [nio-8030-exec-4] com.calvin.zuul.filter.TokenFilter : request_url : /user/userDetail/1, request_headers: ["host","connection","cache-control","upgrade-insecure-requests","user-agent","sec-fetch-user","accept","sec-fetch-site","sec-fetch-mode","accept-encoding","accept-language","cookie"]
若需要攔截請求,或者設(shè)置白名單等,在RequestContext中設(shè)置好自己的statusCode等,就可以了
requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(401); ctx.getResponse().getWriter().write("there is no token found,please relogin!")
看完上述內(nèi)容,你們掌握SpringCloud中如何使用Zuul路由網(wǎng)關(guān)的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!