温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringCloud中如何使用Zuul路由网关

发布时间:2021-08-03 11:24:04 来源:亿速云 阅读:165 作者:Leah 栏目:大数据

SpringCloud中如何使用Zuul路由网关,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

一、Zuul的作用

1. 网关功能

  • 网关可以将所有的api统一暴露,保护其他服务api接口和信息,以防止被外界直接调用

  • 网关可以做身份认证和权限认证,防止非法请求操作API,保护服务

  • 网关可以实现监控功能,实时日志输出,对请求进行记录

  • 网关可以实现流量监控,便于在高流量情况下对服务进行降级

2. 智能路由

  • Zuul可以和Ribbon、Eureka相结合,实现智能路由和负载均衡

  • 通过一定策略,把请求流量分发到集群状态的多个实例中

  • 可以将API从内部接口中分离出来,方便单测

二、Zuul的工作原理

Zuul基于Servlet实现,通过自定义的ZuulServlet来对请求进行控制,Zuul中有一系列的过滤器,这些过滤器是同样是Zuul的实现关键,请求发起和响应期间,通过这些过滤器实现Zuul的功能。具体有以下四个:

  1. PRE过滤器:在请求路由到具体的服务之前执行,用途:安全验证(身份校验,参数校验、ip黑白名单);

  2. ROUTING过滤器 :在请求的服务到具体的微服务实例时执行,用途:进行网络请求(默认使用HttpClient);

  3. POST过滤器:在请求路由到微服务之后执行,用途:统计信息,回传响应;

  4. ERROR过滤器:在其他过滤器发生错误的时候执行,用途:保证请求能够正确响应;

ZuulFilter中的方法有以下四个,继承ZuulFilter并且重写以下四个方法即可实现一个过滤器。

  1. public String filterType(); 返回该Filter的类型,即如上四种过滤器。

  2. public int filterOrder(); 返回该过滤器的执行顺序。

  3. public boolean shouldFilter(); 返回该过滤器是否需要执行。

  4. public Object run(); 执行具体的过滤逻辑。

ZuulServlet是Zuul的核心Servlet,负责初始化ZuulFilter并且编排这些过滤器,具体代码在service()方法中。

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        try {
            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                this.preRoute();
            } catch (ZuulException var13) {
                this.error(var13);
                this.postRoute();
                return;
            }

            try {
                this.route();
            } catch (ZuulException var12) {
                this.error(var12);
                this.postRoute();
                return;
            }

            try {
                this.postRoute();
            } catch (ZuulException var11) {
                this.error(var11);
            }
        } catch (Throwable var14) {
            this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

RequestContextZuulFilter中负责上下文衔接的角色,其本身是一个ConcurrentHashMap,包含Request和Response、routeType、routeHost等上下文需要的对象。

三、项目实战

(1)项目搭建
  • 父级项目zuul-test/pom.xml

<groupId>com.calvin.zuul</groupId>
<artifactId>zuul-test</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
    <module>eureka-server</module>
    <module>user-service</module>
    <module>zuul-service</module>
</modules>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • zuul-service/pom.xml

<parent>
    <artifactId>zuul-test</artifactId>
    <groupId>com.calvin.zuul</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>zuul-service</artifactId>

<dependencies>
    <!-- Springboot支持-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--SpringCloud支持 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • zuul-service/ZuulApplication.java

/**
 * <p>
 *     启动类
 * </p>
 * @author Calvin
 * @date 2019/10/25
 * @since 1.0
 */
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerApplication.class, args);
    }
}
  • zuul-service/application.yml

spring:
  application:
    name: zuul-service
server:
  port: 8030
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8010/eureka/
  instance:
    hostname: localhost
zuul:
  routes:
    user-api:
      path: /user/**
      serviceId: user-service
  • user-service/pom.xml

<parent>
    <artifactId>zuul-test</artifactId>
    <groupId>com.calvin.zuul</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • user-service/application.yml

spring:
  application:
    name: user-service
server:
  port: 8020
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8010/eureka/
  instance:
    hostname: localhost
  • user-service/UserApplication.java

/**
 * <p>
 *      启动类
 * </p>
 * @author Calvin
 * @date 2019/10/25
 * @since 1.0
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class UserApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}
  • user-service/controller/UserController.java

/**
 * <p> </p>
 *
 * @author Calvin
 * @date 2019/10/25
 * @since
 */
@RestController
public class UserController {

    /**
     * 简单构建一个User
     */
    @GetMapping("/userDetail/{userId}")
    public SysUser getUserDetail(@PathVariable("userId") String userId){
        SysUser sysUser = new SysUser();
        sysUser.setId(userId);
        sysUser.setAge(20);
        sysUser.setPassword(MD5Utils.digestAsHex("123456"));
        sysUser.setUsername("Calvin");
        //图片来自百度
        sysUser.setHeadImg("https://b-ssl.duitang.com/uploads/item/201510/17/20151017181645_c5hWE.thumb.700_0.jpeg");
        return sysUser;
    }
}
(2)智能路由

依次启动eureka-server, user-server, zuul-server
浏览器调用 http://localhost:8030/user/userDetail/1 SpringCloud中如何使用Zuul路由网关

从调用结果中可以看到我们从zuul-service中调用了user-service的方法,并且调用成功。从而证明路由配置可用;
如需配置版本号,我们只需要咱zuul-service/application.yml中添加配置:zuul.prefix=v1

(3)故障熔断

如果需要在Zuul中实现服务熔断,只需要实现ZuulFallbackProvider接口,重写其中两个方法,通过getRoute()方法返回我们需要熔断的路由,通过fallbackResponse()方法来重写熔断时执行的逻辑。

如下,我们实现第一个user-service的熔断器

/**
 * <p>
 *    user-service熔断器
 * </p>
 *
 * @author Calvin
 * @date 2019/10/27
 * @since
 */
@Component
public class UserServiceCallback implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "user-service";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        return new CommonClientResponse();
    }
}

贴上CommonClientResponse的代码,就是针对ClientHttpResponse接口的封装

/**
 * <p>封装的通用返回类 </p>
 *
 * @author Calvin
 * @date 2019/10/27
 * @since
 */
public class CommonClientResponse implements ClientHttpResponse {


    @Override
    public HttpStatus getStatusCode() throws IOException {
        return HttpStatus.OK;
    }

    @Override
    public int getRawStatusCode() throws IOException {
        return 0;
    }

    @Override
    public String getStatusText() throws IOException {
        return "success";
    }

    @Override
    public void close() {
    }

    @Override
    public InputStream getBody() throws IOException {
        return new ByteArrayInputStream("hello , this is zuul fallback".getBytes());
    }

    @Override
    public HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
    }
}

接下来做个测试,我们停掉user-service服务,然后再访问 http://localhost:8030/user/userDetail/1 SpringCloud中如何使用Zuul路由网关
结果当然是我们定义的熔断器中返回的内容了!

如果需要对其他服务使用同一个熔断器,只需要在getRoute()方法中返回通配符 return "*"就可以了  

网关测试

ZuulFilter是Zuul实现过滤和网关的关键,此类有四个枚举值,分别代表Zuul中的过滤器类型。如果需要实现过滤,只需要继承ZuulFilter,并且指定其过滤器类型,枚举值为:

	/**
	 * {@link ZuulFilter#filterType()} error type.
	 */
	String ERROR_TYPE = "error";

	/**
	 * {@link ZuulFilter#filterType()} post type.
	 */
	String POST_TYPE = "post";

	/**
	 * {@link ZuulFilter#filterType()} pre type.
	 */
	String PRE_TYPE = "pre";

	/**
	 * {@link ZuulFilter#filterType()} route type.
	 */
	String ROUTE_TYPE = "route";

简单实现一个过滤器

/**
 * <p> header过滤器 </p>
 *
 * @author Calvin
 * @date 2019/10/27
 * @since
 */
@Component
public class TokenFilter extends ZuulFilter {


    private static final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        logger.info("request_url : {}, request_headers: {}",request.getRequestURI(), JSON.toJSON(request.getHeaderNames()).toString());
        return null;
    }
}

重新启动zuul-service,调用服务控制台已经可以输出如下内容:

2019-11-12 22:04:36.726  INFO 58984 --- [nio-8030-exec-4] com.calvin.zuul.filter.TokenFilter       : request_url : /user/userDetail/1, request_headers: ["host","connection","cache-control","upgrade-insecure-requests","user-agent","sec-fetch-user","accept","sec-fetch-site","sec-fetch-mode","accept-encoding","accept-language","cookie"]

若需要拦截请求,或者设置白名单等,在RequestContext中设置好自己的statusCode等,就可以了

requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
ctx.getResponse().getWriter().write("there is no token found,please relogin!")

看完上述内容,你们掌握SpringCloud中如何使用Zuul路由网关的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI