路由网关的主要目的是为了让所有的微服务对外只有一个接口,我们只需访问一个网关地址,即可由网关将所有的请求代理到不同的服务中。Spring Cloud是通过Zuul来实现的,支持自动路由映射到在Eureka Server上注册的服务。Spring Cloud提供了注解@EnableZuulProxy来启用路由代理。
Zuul为使用Java语言的接入层服务提供API网关服务,既可以根据配置反向代理指定的接口,也可以根据服务发现自动配置。Zuul提供了类似于iptables的处理机制,来帮助我们实现验证权鉴、日志等,请求工作流如下所示:
创建SpringBoot项目,然后引入依赖spring-cloud-starter-zuul、spring-cloud-starter-eureka,如果不是通过指定serviceId的方式,eureka依赖不需要,但是为了对服务集群细节的透明性,还是用serviceId来避免直接引用url的方式。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
在Application类中添加注解EnableZuulProxy
@EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } }
在application.properties中配置Zuul应用的基础信息,如:应用名、服务端口等。
spring.application.name=api-gateway
server.port=5555
1. 服务路由
通过服务路由的功能,我们在对外提供服务的时候,只需要通过暴露Zuul中配置的调用地址就可以让调用方统一的来访问我们的服务,而不需要了解具体提供服务的主机信息了。
a. 通过url直接映射,我们可以如下配置:
# routes to url
zuul.routes.api-a-url.path=/api-a-url/**
zuul.routes.api-a-url.url=http://localhost:2222/
该配置,定义了,所有到Zuul的中规则为: /api-a-url/** 的访问都映射到 http://localhost:2222/ 上,也就是说当我们访问 http://localhost:5555/api-a-url/add?a=1&b=2 的时候,Zuul会将该请求路由到: http://localhost:2222/add?a=1&b=2 上。
其中,配置属性zuul.routes.api-a-url.path中的api-a-url部分为路由的名字,可以任意定义,但是一组映射关系的path和url要相同,下面讲serviceId时候也是如此。
• b. 通过serviceId映射,通过url映射的方式对于Zuul来说,并不是特别友好,Zuul需要知道我们所有为服务的地址,才能完成所有的映射配置。而实际上,我们在实现微服务架构是,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,我们就可以实现对serviceId的映射。例如,我们可以如下配置:
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=service-A
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=service-B
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
针对我们在准备工作中实现的两个微服务service-A和service-B,定义了两个路由api-a和api-b来分别映射。另外为了让Zuul能发现service-A和service-B,也加入了eureka的配置。
接下来,我们将eureka-server、service-A、service-B以及这里用Zuul实现的服务网关启动起来,在eureka-server的控制页面中,我们可以看到分别注册了service-A、service-B以及api-gateway
尝试通过服务网关来访问service-A和service-B,根据配置的映射关系,分别访问下面的url
http://localhost:5555/api-a/add?a=1&b=2 :通过serviceId映射访问 service-A中的add服务
http://localhost:5555/api-b/add?a=1&b=2 :通过serviceId映射访问service-B中的add服务
http://localhost:5555/api-a-url/add?a=1&b=2 :通过url映射访问service-A中的add服务
推荐使用serviceId的映射方式,除了对Zuul维护上更加友好之外,serviceId映射方式还支持了断路器,对于服务故障的情况下,可以有效的防止故障蔓延到服务网关上而影响整个系统的对外服务
2. 服务过滤
ZuulFilter是Zuul中核心组件,通过继承该抽象类,覆写几个关键方法达到自定义调度请求的作用,这里filter不是Java web中的filter,不要混淆.
在完成了服务路由之后,我们对外开放服务还需要一些安全措施来保护客户端只能访问它应该访问到的资源。所以我们需要利用Zuul的过滤器来实现我们对外服务的安全控制。
在服务网关中定义过滤器只需要继承 ZuulFilter 抽象类实现其定义的四个抽象函数就可对请求进行拦截与过滤。
自定义过滤器的实现,需要继承 ZuulFilter ,需要重写实现下面四个方法:
1. filterType :返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
(1)pre :可以在请求被路由之前调用
(2)routing :在路由请求时候被调用
(3)post :在routing和error过滤器之后被调用
(4)error :处理请求时发生错误时被调用
2. filterOrder :通过int值来定义过滤器的执行顺序
3. shouldFilter :返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。
4. run :过滤器的具体逻辑。需要注意,这里我们通过 ctx.setSendZuulResponse(false) 令zuul过滤该请求,不对其进行路由,然后通过 ctx.setResponseStatusCode(401) 设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过 ctx.setResponseBody(body) 对返回body内容进行编辑等。
所有评论(0)