源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

一、zuul简介

1.1 API 网关

api 网关是整个微服务系统的门面,所有的外部访问需要通过网关进行调度和过滤。它实现了请求转发、负载均衡、校验过滤、错误熔断、服务聚合等功能。

下图是直观的显示api Gateway 在微服务网关中的作用(图片引用自spring boot 官网)。

1.2 zuul

spring cloud 中提供了基础Net flix Zuul 实现的网关组件,这就是Zuul,它除了实现负载均衡、错误熔断、路由转发等功能,还能与spring 其他组件无缝配合使用。

二、项目结构

spring-cloud-feign用例已经实现通过feign实现服务间的调用,且提供了两个业务服务单元(consumer、producer),可以方便直观的测试zuul的路由、负载均衡、和错误熔断等功能,所以本用例在其基础上进行zuul的整合。

  • common: 公共的接口和实体类;
  • consumer: 服务的消费者,采用feign调用产品服务;
  • producer:服务的提供者;
  • eureka: 注册中心;
  • zuul: api网关。

聚合项目目录如下:

zuul 项目目录如下:

三、构建api 网关 zuul

3.1 引入依赖

主要的依赖是 spring-cloud-starter-netflix-zuul

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.8.RELEASE</version>
        <relativePath/>
    </parent>

    <artifactId>zuul</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--zuul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient

@EnableZuulProxy会自动设置Zuul服务器端点并在其中开启一些反向代理过滤器,以便将请求转发到后端服务器。

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }

}

3.3 指定注册中心、配置网关的路由规则

zuul 需要指定注册中心的地址,zuul 会从eureka获取其他微服务的实例信息,然后按照指定的路由规则进行请求转发。

server:
  port: 8090
# 指定服务命名
spring:
  application:
    name: zuul
# 指定注册中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8010/eureka/
# 网关的路由
zuul:
  routes:
    xxxx: #这个地方的值是可以任意的字符串
      path: /producer/**
      serviceId: producer
    consumer:
      path: /consumer/**
      serviceId: consumer

3.4 启动eureka、producer、consumer、zuul服务,访问 localhost:8090/consumer/sell/product

四、错误熔断

4.1 zuul 默认整合了 hystrix ,不用导入其他额外依赖

4.2 创建 CustomZuulFallbackProvider并实现FallbackProvider 接口,同时用@Component声明为spring 组件,即可实现熔断时候的回退服务

/**
 * @author : heibaiying
 * @description : zuul 的熔断器
 */
@Component
public class CustomZuulFallbackProvider implements FallbackProvider {

    /*
     * 定义熔断将用于哪些路由的服务
     */
    @Override
    public String getRoute() {
        return "consumer";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {

            /**
             * 返回响应的HTTP状态代码
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.SERVICE_UNAVAILABLE;
            }

            /**
             * 返回HTTP状态代码
             */
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.SERVICE_UNAVAILABLE.value();
            }

            /**
             * 返回响应的HTTP状态文本
             */
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            /**
             * 将消息正文作为输入流返回
             */
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("商城崩溃了,请稍后重试!".getBytes());
            }

            /**
             * 将消息正文作为输入流返回
             */
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return httpHeaders;
            }
        };
    }
}

正确返回了内容、同时返回的http状态码也和我们设置的一样。

五、zuul 过滤器

创建自定义过滤器继承自CustomZuulFilter,当我们访问网关的时候,如果判断session 中没有对应的 code,则跳转到我们自定义的登录页面。

/**
 * @author : heibaiying
 * @description : 自定义filter过滤器
 */

@Component
public class CustomZuulFilter extends ZuulFilter {

    /**
     * 返回过滤器的类型
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 返回过滤器的优先级顺序
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 从此方法返回“true”意味着应该调用下面的 run()方法
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * ZuulFilter的核心校验方法
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String code = (String)request.getSession().getAttribute("code");
        if (StringUtils.isEmpty(code)){
            // 设置值为false 不将请求转发到对应的服务上
            currentContext.setSendZuulResponse(false);
            // 设置返回的状态码
            currentContext.setResponseStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value());
            HttpServletResponse response = currentContext.getResponse();
            try {
                // 跳转到登录页面
                response.sendRedirect("/index");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

index.ftl:

<!doctype html>
<html lang="en">
<head>
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    <input name="username" type="text">
    <button id="btn">输入临时用户名后登录!</button>
</form>
</body>
</html>

六、负载均衡

zuul 默认集成了ribbon 实现了负载均衡。只要启动多个实例即可查看到负载均衡的效果。

这里我们直接在idea 中启动多个实例来测试:

负载均衡测试结果:

七、附:关于版本问题可能导致的 zuul 启动失败

如果出现以下错误导致启动失败,是 spring boot 版本不兼容导致的错误,Finchley SR2版本 spring cloud 中的 zuul 和 spring boot 2.1.x 版本存在不兼容。如果出现这个问题,则将 spring boot 将至 2.0.x 的版本即可,用例中采用的是 2.0.8 版本。在实际的开发中应该严格遵循spring 官方的版本依赖说明。

APPLICATION FAILED TO START

---

Description:

The bean 'counterFactory', defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulCounterFactoryConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulMetricsConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

spring cloud 版本说明

Release Train Boot Version
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

更多组件的版本说明可以在spring cloud overview 页面查看。

源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

spring cloud 系列第6篇 —— zuul 服务网关 (F版本)的更多相关文章

  1. Spring Cloud系列(五):服务网关Zuul

    在前面的篇章都是一个服务消费者去调用一个服务提供者,但事实上我们的系统基本不会那么简单,如果真的是那么简单的业务架构我们也没必要用Spring Cloud,直接部署一个Spring Boot应用就够了 ...

  2. spring cloud 系列第1篇 —— eureka 服务的注册与发现 (F版本)

    源码仓库地址:https://github.com/heibaiying/spring-samples-for-all 一.eureka 简介 Spring Cloud Eureka使用Netflix ...

  3. spring cloud系列教程第一篇-介绍

    spring cloud系列教程第一篇-介绍 前言: 现在Java招聘中最常见的是会微服务开发,微服务已经在国内火了几年了,而且也成了趋势了.那么,微服务只是指spring boot吗?当然不是了,微 ...

  4. Spring cloud系列教程第二篇:支付项目父工程图文搭建

    Spring cloud系列教程第二篇:支付项目父工程图文搭建 在讲解spring cloud相关的技术的时候,咱们就模拟订单支付这个流程来讲讲 在这个支付模块微服务搭建过程中,上面的这些技术,都会融 ...

  5. Spring Cloud系列教程第九篇-Eureka自我保护机制

    Spring Cloud系列教程第九篇-Eureka自我保护机制 本文主要内容: 1:自我保护介绍 2:导致原因分析 3:怎么禁止自我保护 本文是由凯哥(凯哥Java:kagejava)发布的< ...

  6. Spring Cloud系列(一):服务注册中心

    一.Spring Cloud简介 Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线).分布式系统的协调导致了样 ...

  7. Spring Cloud架构教程 (三)服务网关(基础)

    通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了.比如下图所示: alt 我们使用Spring Cloud Netflix中的Eureka实 ...

  8. spring cloud 系列第7篇 —— sleuth+zipkin 服务链路追踪 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.简介 在微服务架构中,几乎每一个前端的请求都会经过多个服务单元协调来提 ...

  9. spring cloud 系列第5篇 —— hystrix+turbine 服务的熔断与监控 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.hystrix 简介 1.1 熔断器 在分布式系统中,由于服务之间相互 ...

随机推荐

  1. 在codefirst迁移的时候 为新增的属性添加一个默认值

    在模型中新增一个属性之后 通过add-migration ConsulationMsg_add_IsDel命令之后 会生成一个 日期+ConsulationMsg_add_IsDel的类文件 打开类文 ...

  2. vector删,erase和remove难怪--【STL】

    供vector使用容器.通常只是一个简单的遍历查找,其他操作已执行,这不是,今天,稍有不慎. erase方法的操作是将此时的节点删除,然后指向被删除节点的下一个: 如对数据1 6 6 4 7; #in ...

  3. Matlab Tricks(二十五) —— 二维图像的 shuffle

    比如对于 mnist (手写字符图像),每幅图像的像素点为 28*28,所以有: perm = randperm(28*28); % 重排列 mnist.train_images = reshape( ...

  4. 对scrollTop的研究

    本文主要从原生 JS以及jquery来说明scrollTop是如何实现的,以及一些技巧,以及在PC端和移动端使用的差异. 首先用代码表示下如何回到顶部的简单原理 <!doctype html&g ...

  5. Coverage数据拓扑

    什么是Coverage?   Coverage数据模型源于ESRI公司1981年推出的第一个商业GIS软件——ArcInfo.也被称为地理相关数据模型(Georelational Data Model ...

  6. It&#39;s about trust

    所有问题, 最后,它归结为:信任. 你能相信别人? 我不是Low esteem.相反,我有信心.问题是.我有一个别人缺乏信任的. Daria家长们说,伪君子:我们必须相信你. Daria马上问:然后, ...

  7. vb.net版本房收费系统改造

    房费制开始重建的最终版本. 前几天.刚刚看完三层的视频,在视频中具体的解说了一个登录功能.天真的我,当时以为三层结构是那么的简单,所以我草草地做完总结之后,就非常快就開始机房收费系统的重构了.但是谁想 ...

  8. Codeforces Round #263 (Div. 2) proA

    称号: A. Appleman and Easy Task time limit per test 1 second memory limit per test 256 megabytes input ...

  9. WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参

    原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataC ...

  10. 通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)

    原文:通通玩blend美工(6)下--仿iPhone滚动选择器的ListBox(交互逻辑) 上一篇我们已经把界面画出来了,这篇我们就来制作交互的逻辑吧.上一篇的电梯: http://www.cnblo ...