springcloud 路由网关 - zuul

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;
            }
        }

猜你喜欢

转载自blog.csdn.net/CHS007chs/article/details/83504961