本文,咱們將學習 Spring Cloud的另外一個組件:zuul,它提供微服務的網關功能,即中轉站,經過它提供的接口,能夠轉發不一樣的服務。在學習 zuul 以前,咱們先接着上一篇的代碼,來看看服務提供者是如何提供服務的。spring
在服務提供者的 module 下建立 HelloController 類,添加內容以下:api
@RestController public class HelloController { @RequestMapping("index") public String index(){ return "Hello World!"; } }
而後分別啓動服務註冊中心和服務提供者,瀏覽器輸入:http://localhost:8762/index,便可看見以下畫面:
瀏覽器
在實際的項目中,一個項目可能會包含不少個服務,每一個服務的端口和 IP 均可能不同。那麼,若是咱們以這種形式提供接口給外部調用,代價是很是大的。從安全性上考慮,系統對外提供的接口應該進行合法性校驗,防止非法請求,若是按照這種形式,那每一個服務都要寫一遍校驗規則,維護起來也很麻煩。安全
這個時候,咱們須要統一的入口,接口地址所有由該入口進入,而服務只部署在局域網內供這個統一的入口調用,這個入口就是咱們一般說的服務網關。服務器
Spring Cloud 給咱們提供了這樣一個解決方案,那就是 zuul,它的做用就是進行路由轉發、異常處理和過濾攔截。下面,我將演示若是使用 zuul 建立一個服務網關。app
建立 gateway 工程負載均衡
在父項目上右鍵 -> New -> Module,建立一個名爲 gateway 的工程,在其 pom.xml 中,加入以下依賴:ide
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> </dependencies>
建立 Application 啓動類,並增長 @EnableZuulProxy 註解:微服務
@SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
最後添加 application.yml 配置文件,內容以下:post
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8080 spring: application: name: gateway zuul: routes: api: path: /api/** serviceId: eurekaclient
咱們能夠看到,服務網關的配置多了幾項,具體含義以下。
而後咱們啓動服務註冊中心、服務提供者、服務網關,訪問地址:http://localhost:8080/api/index,咱們能夠看到和以前的界面徹底同樣。其實只要引入了 zuul,它就會自動幫咱們實現反向代理和負載均衡。配置文件中的地址轉發其實就是一個反向代理,那它如何實現負載均衡呢?
咱們修改服務提供者的 Controller 以下:
RestController public class HelloController { @Value("${server.port}") private int port; @RequestMapping("index") public String index(){ return "Hello World!,端口:"+port; } }
從新啓動。而後再修改服務提供者的端口爲8673,再次啓動它(切記:原先啓動的不要中止),訪問地址:http://localhost:8761,咱們能夠看到 eurekaclient 服務有兩個地址:
再不斷訪問地址:http://localhost:8080/api/index,能夠看到交替出現如下界面:
由此能夠得出,當一個服務啓動多個端口時,zuul 服務網關會依次請求不一樣端口,以達到負載均衡的目的。
服務攔截
前面咱們提到,服務網關還有個做用就是接口的安全性校驗,這個時候咱們就須要經過 zuul 進行統一攔截,zuul 經過繼承過濾器 ZuulFilter 進行處理,下面請看具體用法。
新建一個類 ApiFilter 並繼承 ZuulFilter:
@Component public class ApiFilter extends ZuulFilter { @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { //這裏寫校驗代碼 return null; } }
其中:
下面,咱們來作一個簡單的安全驗證:
@Override public Object run() { //這裏寫校驗代碼 RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String token = request.getParameter("token"); if(!"12345".equals(token)){ context.setSendZuulResponse(false); context.setResponseStatusCode(401); try { context.getResponse().getWriter().write("token is invalid."); }catch (Exception e){} } return null; }
啓動 gateway,在瀏覽器輸入地址:http://localhost:8080/api/index,能夠看到如下界面:
再經過瀏覽器輸入地址:http://localhost:8080/api/index?token=12345,能夠看到如下界面:
錯誤攔截
在一個大型系統中,服務是部署在不一樣的服務器下面的,咱們不免會遇到某一個服務掛掉或者請求不到的時候,若是不作任何處理,服務網關請求不到會拋出500錯誤,對用戶是不友好的。
咱們爲了提供用戶的友好性,須要返回友好性提示,zuul 爲咱們提供了一個名叫 ZuulFallbackProvider 的接口,經過它咱們就能夠對這些請求不到的服務進行錯誤處理。
新建一個類 ApiFallbackProvider 而且實現 ZuulFallbackProvider 接口:
Component public class ApiFallbackProvider implements ZuulFallbackProvider{ @Override public String getRoute() { return "eurekaclient"; } @Override public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 200; } @Override public String getStatusText() throws IOException { return "{code:0,message:\"服務器異常!\"}"; } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream(getStatusText().getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; }
其中,getRoute 方法返回要處理錯誤的服務名,fallbackResponse 方法返回錯誤的處理規則。
如今開始測試這部分代碼,首先停掉服務提供者 eurekaclient,再重啓 gateway,請求地址:http://localhost:8080/api/index?token=12345,便可出現如下界面: