本文,咱们将学习 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,便可出现如下界面: