springcloud 路由网关 - zuul
阅读数:132 评论数:0
跳转到新版页面分类
python/Java
正文
spring cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、ngnix),再到达服务网关(zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改变配置。
简介
zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分。
zuul有以下功能:
(1)Authentication(认证)
(2)Insights
(3)Stress Testing(压力测试)
(4)Canary Testing(金丝雀测试)
(5)Dynamic Routing(动态路由)
(6)Service Migration(服务迁移)
(7)Load Shedding(负载调度)
(8)Security(案例)
(9)Static Response handling(静态响应处理)
(10)Active traffic management
zuul中默认实现的filter
类型 | 顺序 | 过滤器 | 功能 |
pre | -3 | ServletDetectionFilter | 标记处理Servlet的类型 |
pre | -2 | Servlet3oWrapperFilter | 包装HttpServletRequests请求 |
pre | -1 | FormBodyWrapperFilter | 包装请求体 |
route | 1 | DebugFilter | 标记调试标志 |
route | 5 | PreDecorationFilter | 处理请求上下文供后续使用 |
route | 10 | RibbonRoutingFilter | serviceId请求转发 |
route | 100 | SimpleHostRoutingFilter | url请求转发 |
route | 500 | SendForwardFilter | forward请求转发 |
post | 0 | SendErrorFilter | 处理有错误的请求响应 |
pos | 1000 | SendResponseFilter | 处理正常的请求响应 |
可以在application.yml中配置需要禁用的filter,格式 :
zuul:
FormBodyWrapperFilter:
pre:
disable: true
自定义Filter
实现自定义Filter,需要继承ZuulFilter的类,并覆盖其中的4个方法。
public class MyFilter extends ZuulFilter {
@Override
String filterType() {
return "pre"; //定义filter的类型,有pre、route、post、error四种
}
@Override
int filterOrder() {
return 10; //定义filter的顺序,数字越小表示顺序越高,越先执行
}
@Override
boolean shouldFilter() {
return true; //表示是否需要执行该filter,true表示执行,false表示不执行
}
@Override
Object run() {
return null; //filter需要执行的具体操作
}
}
实践
1、在原有的工程上,创建一个的新的工程service-zuul
2、其pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.forezp</groupId>
<artifactId>service-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-zuul</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.forezp</groupId>
<artifactId>sc-f-chapter5</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
</project>
3、在其入口application类加上注解@EnableZuulProxy,开启zuul的功能
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class ServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run( ServiceZuulApplication.class, args );
}
}
4、加上配置文件application.yml加上以下配置代码
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
服务过滤
zuul不仅只是路由,并且还能过滤,做一些安全验证。
@Component
public class MyFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(MyFilter.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();
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
log.info("ok");
return null;
}
}
1、fiterType: 返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤喊器类型,具体如下:
(1)pre: 路由之前
我们可以利用这种过滤器实现身份验证、在集群中执行请求的微服务,记录调用信息等。
(2)routing:路由之时
(3)post:路由之后
(4)error:发送错误调用
2、filterOrder: 过滤顺序
3、shouldFilter:这里可以写逻辑判断,是否要过滤。
4、run:过滤器的具体逻辑
5、访问http://localhost:8769/api-a/hi?name=xxx
6、访问http://localhost:8769/api-a/hi?name=xxx&token=22
zuul的高可用
因为外部请求到后端服务的流量都会经过zuul,故而在生产环境中,我们一般都需要部署高可用的zuul以避免单点故障。
1、zuul客户端也注册到了eureka server上
这种情况下,zuul的高可用非常简单,只需将多个zuul节点注册到eureka server上,就可实现zuul的高可用。此时,zuul的高可用与其他微服务的高可用没什么区别。
当zuul客户端也注册到eureka server上时,只需部署多个zuul节点即可实现其高可用。zuul客户端会自动从eureka server中查询zuul server的列表,并使用ribbon负载均衡的请求zuul集群。
2、zuul客户端未注册到eureka server上
现实中,这种场景往往更常见,例如,zuul客户端是一个手机app,我们不可能让所有手机端都注册到eureka server上,这种情况下,我们可借助一个额外的负载均衡器来实现zuul的高可用,例如nginx、haproxy、f5等。
问题
1、跨域访问
正常情况下,跨域是这样的:
微服务配置跨域+zuul不配置=有跨域问题
微服务配置+zuul配置=有跨域问题
微服务不配置+zuul不配置=有跨域问题
微服务不配置+zuul配置=ok
(1)zuul配置忽略头部信息
zuul:
ignored-headers: Access-Control-Allow-Origin,H-APP-Id,Token,APPToken
(2)跨域配置
@Component
@Configuration
public class GateWayCorsConfig
{
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);//允许cookie跨域
//允许访问的头信息,*表示全部
corsConfiguration.addAllowedHeader("*");
//允许向服务器提交请求的URI,*表示全部允许,
corsConfiguration.addAllowedOrigin("*");
//允许提交请求的方法,*表示全部允许
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
只要网关配置好了,底下的服务不需要配置。
2、中文乱码
解决办法:
(1)注意编码,全站api和前端全部使用utf-8,zuul中强制编码为utf-8.
spring:
http:
encoding:
charset: UTF-8
enabled: true
force: true
3、post json中获取参数token
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String method = request.getMethod();
String token = null;
if(method.equalsIgnoreCase("GET")){
token = request.getParameter("token");
}else if(method.equalsIgnoreCase("POST")){
try {
InputStream inputStream = request.getInputStream();
String body = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
JSONObject jsonObject = JSONObject.parseObject(body);
token = jsonObject.getString("token");
}catch (Exception e){
e.printStackTrace();
token = null;
}
}
配置
1、zuul.max.host.connections
这个配置已经被两个配置项替换: zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections,默认是200,另一个是20.
2、zuul timeout
如果使用service discovery,可以通过ribbon.ReadTimeout(默认5s)和ribbon.ConnectTimeout(默认2s)设置。
如果通过URL,可以通过zuul.host.connect-timeout-millis和zuul.host.socket-timeot-millis设置。