- API网关用来做什么
API网关有点像微服务架构体系中的门面,所有外部客户端访问都需要经过它来进行跳读和过滤。API网关可以实现请求路由、负载平衡、校验过滤等功能,还能聚合服务治理框架、熔断机制、服务等
- 路由规则与服务实例的维护
Spring Cloud Zuul通过与Spring Cloud Eureka进行整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获得所有其他微服务的实例信息。
- 签名校验、登陆校验
校验逻辑在本质上与微服务应用自身的业务没有多大的关系,所以一般将它们独立成一个单独的服务。被独立出来以后,并不是给每个微服务调用,而是在API网关服务上进行统一调用来对微服务接口做前置过滤,以实现对微服务接口的拦截和校验。
- 构建网关
创建一个SpringBoot工程
1.pom.xml
<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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
添加了zuul的依赖。spring-cloud-starter-zuul依赖集成了sprng-cloud-starter-hystrix、spring-cloud-starter-ribbon、spring-boot-starter-actuator,zuul-core,相当于又进一步进行了封装
2.启动类
@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {public static void main(String[] args) { new SpringApplicationBuilder(ApiGatewayApplication.class).web(true).run(args); }
}
@EnableZuulProxy:开启Zuul的API网关服务功能3.配置文件
spring.application.name=api-gateway server.port=5555 zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.serviceId=hello-service zuul.routes.api-b.path=/api-b/** zuul.routes.api-b.serviceId=feign-consumer eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
采用的面向服务的路由。让路由的path不是映射具体的url,而是映射到某个具体的服务,而具体的url则是交给Eureka的服务发现机制去自动维护。
针对之前的两个微服务,在配置文件中分别定义了api-a和api-b路由来映射它们。
启动程序,向浏览器输入http://localhost:5555/api-a/hello将被映射到
http://localhost:5555/hello-service/hello的某个实例上
想浏览器输入http://localhost:5555/api-b/feign-consumer2将被映射到http://localhost:5555/feign–consumer/feign-consumer2的某个实例上
- 请求过滤
为了在API网关中实现对客户端请求的校验,可以在API网关上定义过滤器来实现对请求的拦截和过滤。只要继承ZuulFilter抽象类并实现它定义的4个抽象方法就能完成对请求的拦截和过滤
1.定义简单的一个过滤器AccessFilter.java
public class AccessFilter extends ZuulFilter { private static Logger logger = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); logger.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); Object accessToken = request.getParameter("accessToken"); if (accessToken == null) { logger.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } logger.info("access token ok"); return null; } }
方法里内容介绍
1.filterType:过滤器的类型,它决定过滤器在请求的哪个生命周期中执行,这里为pre,在请求被路由之前执行
2.filterOrder:过滤器的执行顺序
3.shouldFilter:判断该过滤器是否需要被执行,这里返回true,将会对所有的请求生效,实际应用中会利用函数来指定过滤器的有效范围。
4.run:过滤器的具体逻辑。 ctx.setSendZuulResponse(false),让zuul过滤该请求不对其进行路由。ctx.setResponseStatusCode(401);设置返回的错误码。2.在实现了自定义过滤器之后不会直接生效,需要为其创建具体的bean才能启动该过滤器。修改启动类,创建过滤器的bean
@Bean public AccessFilter accessFilter() { return new AccessFilter(); }
重新启动,此时重新访问http://localhost:5555/api-a/hello、http://localhost:5555/api-b/feign-consumer2将报错。因为这时accessToken内容为空 。访问http://localhost:5555/api-a/hello?accessToken=token将返回hello world….
最后附完整代码地址:https://github.com/TiantianUpup/SpringCloudZuulDemo