一、Spring Cloud网关是什么
在微服务框架下,单体应用会被切割成多个微服务,如果将所有的微服务直接对外暴露会引发如下类似问题:
- 客户端需求和每个微服务暴露的细粒度API不匹配。
- 部分服务使用的协议不是Web友好协议。可能使用Thrift二进制RPC,也可能使用AMQP消息传递协议。
- 微服务难以重构。如果合并两个服务,或者将一个服务拆分成两个或更多服务,这类重构就非常困难了。
- 对客户端的访问进行安全性验证,防止爬虫,以及限制IP在一定时间内的请求数。
解决上述问题的方法是使用API网关,API网关是一个微服务框架的唯一入口。从设计模式上来看,它与外观模式类似。下图是一个微服务框架图,其中API网关结合有鉴权和授权认证的业务逻辑。
二 Spring Cloud网关Zuul
目前Spring Cloud网关可采用的方案和框架包括有Zuul、Nginx、Spring Cloud Gateway和Linkerd。
Zuul是Netflix开源的微服务网关,可以和Eureka、Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,通过过滤器可以完成以下功能:
- 1.身份认证和安全:识别每一个资源的验证要求、审查和监控
- 2.动态路由:动态将请求路由到不同后端集群
- 3.压力测试:组建增加指定集群流量,检测性能
- 4.负载分配:为不同负载类型分配对应容器量
- 5.静态响应处理:边缘位置响应,避免转发到内部集群
- 6.多区域弹性
下面将从Zuul结合其它组件,来介绍Zuul的应用。
2.1 Zuul和Eureka的结合使用
在构建的项目中添加依赖,pom.xml文件中添加如下如下内容
pom.xml的配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
<!--整合达到安全控制-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
application.yml文件配置
server:
port: 10900 #程序启动端口,tomcat端口(可自定义)
spring:
application:
name: api-gateway-zuul #应用名称(别名)
eureka:
client:
serviceUrl:
defaultZone: http://user:user@localhost:8888/eureka
instance:
prefer-ip-address: true #显示 ip
security: #配置访问 zuul 的 routes 的时候使用
user:
name: zuul
password: zuul
zuul:
routes: #配置路由映射
provider-user: /route-map1/** #给指定的服务做映射, 当前配置给provider-user添加映射地址为/route-map1
Zuul模块的代码
在Zuul模块的代码中添加注解@EnableZuulProxy和@EnableDiscoveryClient。使用@EnableZuulProxy将这个类指示为Zuul Server, @EnableDiscoveryClient将这个类指示为Eureka的客户端。在部分Zuul版本中,@EnableZuulProxy可能包含@EnableDiscoveryClient注解。
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
Eureka模块的代码
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaApplication.class).web(true).run(args);
}
}
客户端模块的代码
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class ControllerApplicationA {
public static void main(String[] args) {
new SpringApplicationBuilder(ControllerApplicationA.class).web(true).run(args);
}
}
上述代码就实现了,客户端和Zuul网关代码向Eureka注册,并通过访问Zuul来访问客户端的大概框架。
2.2 Zuul中的过滤器(filter)
Zuul过滤器(filter)是Zuul的两大核心功能之一(另一核心功能是路由器router),过滤器(filter)可以在真正进行请求之前进行必要的业务操作,比如说验证。从业务逻辑上来看,Zuul本身也是一系列过滤器集成,Zuul提供了四种类型过滤器:前置过滤器,路由过滤器,错误过滤器和简单过滤器。Zuul过滤器最核心的接口为IZuulFilter,该接口的定义非常简单,参考如下:
/*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.zuul;
import com.netflix.zuul.exception.ZuulException;
/**
* BAse interface for ZuulFilters
*
* @author Mikey Cohen
* Date: 10/27/11
* Time: 3:03 PM
*/
public interface IZuulFilter {
/**
* a "true" return from this method means that the run() method should be invoked
*
* @return true if the run() method should be invoked. false will not invoke the run() method
*/
boolean shouldFilter();
/**
* if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
*
* @return Some arbitrary artifact may be returned. Current implementation ignores it.
* @throws ZuulException if an error occurs during execution.
*/
Object run() throws ZuulException;
}
抽象类ZuulFilte,ZuulFilter实现了接口IZuulFilter,在接口的基础上添加了抽象方法
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
*
* @return A String representing that type
*/
abstract public String filterType();
/**
* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not
* important for a filter. filterOrders do not need to be sequential.
*
* @return the int order of a filter
*/
abstract public int filterOrder();
该抽象类的方法解释如下:
filterType:定义过滤器的类型,常见的有pre(预处理阶段)post(请求原始服务之前)error(发生错误以后)route(请求原始服务之后)
自定义过滤器
实现用户名验证
package com.zhibo.springcloud.zuul;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class ValidateUserZuulFilter extends ZuulFilter {
/**
*
* @return
*/
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String loginName = request.getParameter("loginName");
if (loginName == null || !"admin".equals(loginName)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(500);
Gson gson = new GsonBuilder().create();
requestContext.addZuulResponseHeader("content-type", "application/json;charset=utf-8");
requestContext.setResponseBody(gson.toJson(new ResponseEntity("没有登录名", HttpStatus.CONFLICT)));
return null;
}
return null;
}
}
在Zuul模块的代码中,注册进行生效,代码如下:
@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
}
三、总结
上述对spring cloud zuul在功能上进行简介,在zuul的实际使用方面,比如结合Eureka模块的使用和过滤器方面,用代码进行了说明。但是上述并没有覆盖到Zuul的所有知识点,后续有时间在做整理。