Spring Cloud 整合 Zuul
上一篇博客讲了Spring Cloud + 消费者(两种负载均衡) +服务者 +熔断器,接下来我们继续上一篇博客内容整合一下Spring Cloud Zuul。在这提供一下我上一篇博客地址:
springcloud整合消费者+服务提供者+熔断器
我们先来看看下面这个基础架构:
在该架构中,我们的服务集群包含内部服务S而vice A和Service B,他们都会像 Eureka集群进行注册和订阅。Open Service是一个对外的RESTful API服务,它通过F5,Nginx等网络设备或工具实现对各个微服务的路由与负载均衡,并公开给外部的客户端调用。
这样的架构是当客户端应用某个功能的时候往往会发出一些对微服务获取资源的请求到后端,这些请求通过F5,Nginx等设备的路由和负载均衡分配后,被转发到各个不同的服务实例上。要为了让这些设施能够正确的路由分配,运维人员需要手动维护这些路由规则和服务实例列表,如果有实例的增加和删除,也需要手动的去修改这些实例信息,在规模比较大的时候,太过于复杂。这时候API网关就应用而生了。
Spring Cloud Zuul
对于路由规则和服务实例维护的问题,Spring Cloud Zuul通过了Spring Cloud Eureka整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获取了所有的其他微服务的实例信息。
这样的设计非常巧妙的将服务治理体系中维护实例的信息利用起来,使得将维护服务实例的工作交给了服务治理框架自动完成,不需要人工介入。而对于路由规则的维护,Zuul会默认通过以服务名作为ContextPath的方式来创建路由映射。
理论咋就扯到这里为止,直接快速入门:
跟上一篇博客一样,创建一个module建一个springboot项目,导入相关依赖,我这里就直接把pom文件依赖晒出来了
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
看看我们这个项目结构(是在昨天的项目上直接加的哦,可以看我上一篇博客,地址:上一篇连接)
看application.properties文件类容:
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.service-url.defaultZone=http://localhost:1111/eureka/
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
这个hello-service和feign-consumer都是服务名称,上面所说的Zuul会默认通过以服务名作为ContextPath的方式来创建路由映射就是这个道理
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/ 注册中心地址
主启动类:
@EnableZuulProxy
@SpringCloudApplication
public class ApiGetewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGetewayApplication.class, args);
}
}
接下来我们启动一下几个服务:
然后可以看到:
接下来我们可以通过服务网关访问hello-service和feign-consumer这两个服务了,根据映射的配置关系发送一下请求:
http://localhost:5555/api-a/hello
该url符合/api-a/**的配置规则,由api-a路由负责转发,该路由映射的serviceId为hello-service,最终请求会转发到hello-service的某个实例上面去。
通过面向服务的路由配置方式,我们不需要再为各个路由维护微服务应用的具体实例的位置,而是通过简单的path和serviceId的映射组合,使得维护工作变得非常简单,这完全归功于Spring Cloud Eureka的服务发现机制,是的API网关服务可以自动化完成服务的实例清单维护,完美的解决了对路由映射实例的维护问题。
请求过滤
Zuul可以在API网关上通过定义过滤器来实现对请求的拦截和过滤,实现方法很简单,只需要继承ZuulFilter抽象类并实现4个抽象方法就可以。
在这个过滤器中看HttpServletRequest中是否有accessToken参数,有就进行路由,没有就过滤。
AccessFilter代码:
public class AccessFilter extends ZuulFilter {
/**
* @Description: 过滤器的类型,决定过滤器在请求的哪个生命周期中执行,pre代表会在请求被路由之前执行
* @Author: gaolei
* @CreateDate: 2019/8/15 0015 11:27
*@PARAM:
*/
@Override
public String filterType() {
return "pre";
}
/**
* @Description: 过滤器的执行顺序,当请求在一个阶段存在多个过滤器的时候,需要根据该方法返回的值来依次执行
* @Author: gaolei
* @CreateDate: 2019/8/15 0015 11:30
*@PARAM:
*/
@Override
public int filterOrder() {
return 0;
}
/**
* @Description: 判断该过滤器是否需要执行
* @Author: gaolei
* @CreateDate: 2019/8/15 0015 11:32
*@PARAM:
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* @Description: 过滤器的具体逻辑
* @Author: gaolei
* @CreateDate: 2019/8/15 0015 11:32
*@PARAM:
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx=RequestContext.getCurrentContext();
HttpServletRequest request=ctx.getRequest();
Object accessToken=request.getParameter("accessToken");
if(accessToken == null){
//不对其路由
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
return null;
}
}
在实现了自定义的过滤器之后,它并不会直接生效的,还需要为他创建具体的bean才能启动该过滤器,启动类的代码为:
@EnableZuulProxy
@SpringCloudApplication
public class ApiGetewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGetewayApplication.class, args);
}
@Bean
public AccessFilter accessFilter(){
return new AccessFilter();
}
}
再次访问http://localhost:5555/api-a/hello:
再访问:http://localhost:5555/api-a/hello?accessToken=token