首页> 博客> SpringCloud入门系列之API网关
17 07 2020

初识API网关

使用API网关有点

  • 统一访问出入口,微服务对前台透明
  • 安全,过滤,流控等API管理功能
  • 利于监控、方便管理

API网关产品

  • Netflix Zuul
    • Zuul 是Netflix开源的一个API网关, 核心实现是Servlet
    • Spring Cloud内置Zuul 1.x
    • Zuul 1.x 核心实现是Servlet,采用同步方式通信
    • Zuul 2.x 基于Netty Server,提供异步通信
  • Spring Cloud Gateway
    • Spring Cloud Gateway,是Spring自己的项目
    • Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式
    • Gateway基于Spring 5.0与Spring WebFlux开发,采用Reactor响应式设计
一、Zuul入门使用
1.1、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 https://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.1.15.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.codesofun</groupId>
    <artifactId>zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuul</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
1.2、启动类添加注解@EnableZuulProxy

网管Zulul也是一个微服务客户端,所以入口类上也需要添加@EnableEurekaClient@EnableDiscoveryClient

package com.codesofun.zuul;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}
1.3、添加application.properties配置
spring.application.name=zuul-proxy
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#微服务实例接口映射路径
zuul.routes.book-service-api.path=/bs/**
#微服务实例ID
zuul.routes.book-service-api.service-id=book-service

zuul.routes.member-service-api.path=/ms/**
zuul.routes.member-service-api.service-id=member-service

zuul.routes.message-service-api.path=/sms/**
zuul.routes.message-service-api.service-id=message-service

#端口一般为80 或 443
server.port=443
二、Zuul负载均衡与服务降级
2.1、负载均衡

Spring Cloud Zuul内置Ribbon,与标准配置相同,局部配置如下:

#局部设置网关与 book-service 微服务通信负载均衡策略
book-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

全局配置可以放在bean 配置类中交给IOC容器管理,主要配置如下:

    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
2.2、配置服务降级

Spring Cloud Zuul内置Hystrix,服务降级需要实现接口FallbackProvider,如下:

package com.codesofun.zuul.fallback;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @ClassName BookServiceFallBack
 * @Description Book-service 服务降级
 * @Author mozhijun
 * @Date 2020/7/8 15:43
 * @Version 1.0
 **/
@Component
public class BookServiceFallBack implements FallbackProvider {

    /**
     * 设置为哪个微服务提供降级
     */
    @Override
    public String getRoute() {
        return "book-service";
    }

    /**
     * 描述: 降级处理逻辑
     * @Author mozhijun
     * @Date 15:45 2020/7/8
     * @param route 服务ID
     * @param cause 抛出的异常信息
     * @return org.springframework.http.client.ClientHttpResponse 服务降级response
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {

            /**
             * 响应状态码
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            /**
             * 数字状态
             */
            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            /**
             * 状态文本说明
             */
            @Override
            public String getStatusText() throws IOException {
                return "ok";
            }

            /**
             * 关闭的回收触发,一般用于资源的释放
             */
            @Override
            public void close() {

            }

            /**
             * 响应给客户端的内容
             */
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("Book Service is unavailable".getBytes());
            }

            /**
             * 响应头信息
             */
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.TEXT_HTML);
                return headers;
            }
        };
    }
}

getRoute()中返回指定微服务的实例名称,设置所有微服务默认降级回调,这里只需要返回*即可

    /**
     * 设置为哪个微服务提供降级
     */
    @Override
    public String getRoute() {
        return "book-service";
    }
三、基于RateLimit实现网关限流

微服务网关流量控制

  • 微服务网关是应用入口,必须对入口流量进行控制

  • RateLimit是Spring Cloud Zuul的限流组件

    开源网站地址https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

  • RateLimit采用令牌桶算法实现限流

RateLimit使用步骤

3.1、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 https://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.1.15.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.codesofun</groupId>
    <artifactId>zuul-ratelimit</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuul-ratelimit</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</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-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
3.2、配置存储组件

这里存储使用关系型数据库mysql,orm框架使用jpa,添加配置如下:

spring.application.name=zuul-proxy
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
############ zuul ############
#微服务实例接口映射路径
zuul.routes.book-service-api.path=/bs/**
#微服务实例ID
zuul.routes.book-service-api.service-id=book-service

zuul.routes.member-service-api.path=/ms/**
zuul.routes.member-service-api.service-id=member-service

zuul.routes.message-service-api.path=/sms/**
zuul.routes.message-service-api.service-id=message-service
#开启zuul限流,默认false
zuul.ratelimit.enabled=true
# 对应存储类型(用来统计存储统计信息)
zuul.ratelimit.repository=jpa
########## 数据源 #########
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=mzj584wanlhy
spring.jpa.database-platform=org.hibernate.dialect.MySQL57InnoDBDialect
spring.jpa.show-sql=true
#局部设置网关与 book-service 微服务通信负载均衡策略
book-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#端口一般为80 或 443
server.port=443

创建表rate

CREATE TABLE rate (
  rate_key VARCHAR(255) NOT NULL,
  remaining BIGINT,
  remaining_quota BIGINT,
  reset BIGINT,
  expiration TIMESTAMP,
  PRIMARY KEY(rate_key)
);
3.3、配置限流策略
######默认限流策略全局配置: 对同一个url请求 5秒内最多10次,其余会被限流###########
#每个刷新时间窗口对应的请求数量限制
zuul.ratelimit.default-policy-list[0].limit=10
#每个刷新时间窗口对应的请求时间限制(秒)
zuul.ratelimit.default-policy-list[0].quota=1000
# 刷新时间窗口的时间,默认值 (秒)
zuul.ratelimit.default-policy-list[0].refresh-interval=5
#限流方式
zuul.ratelimit.default-policy-list[0].type=url

####在zuul与book-service通信时,每2秒内,只允许2个请求访问,其余进来的请求会被限制访问,并返回状态码429#####
# 每秒允许多少个请求
zuul.ratelimit.policy-list.message-service-api[0].limit=2
# 刷新时间(单位秒)
zuul.ratelimit.policy-list.message-service-api[0].refresh-interval=2
#限流方式: 可选user(用户限制) origin(请求限制) url(对同一个url访问限制) http_method
zuul.ratelimit.policy-list.message-service-api[0].type=origin

zuul.ratelimit.policy-list.member-service-api[0].limit=2
zuul.ratelimit.policy-list.member-service-api[0].refresh-interval=2
zuul.ratelimit.policy-list.member-service-api[0].type=origin

zuul.ratelimit.policy-list.book-service-api[0].limit=2
zuul.ratelimit.policy-list.book-service-api[0].refresh-interval=2
zuul.ratelimit.policy-list.book-service-api[0].type=origin
四、Zuul自定义过滤器

自定义过滤器,需要继承com.netflix.zuul.ZuulFilter类,这里模拟一个TOKEN校验的过滤器,具体如下:

package com.codesofun.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName SecurityFilter
 * @Description 自定义安全过滤器
 * @Author mozhijun
 * @Date 2020/7/9 16:58
 * @Version 1.0
 **/
@Component
public class SecurityFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 执行顺序
     */
    @Override
    public int filterOrder() {
        return 6;
    }

    /**
     * 当前过滤器是否被执行
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //获取上下文
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            //对该请求禁止路由,也就是禁止访问下游服务
            context.setSendZuulResponse(false);
            //状态码
            context.setResponseStatusCode(403);
            context.setResponseBody("{\"code\":403,\"message\":\"Token is unavailable\"}");
            return null;
        }
        //校验Token正确性代码
        context.setSendZuulResponse(true);
        context.setResponseStatusCode(200);
        return null;
    }
}

详细代码见仓库:https://gitee.com/xmlvhy/springcloud-learn 参考链接:http://www.itlaoqi.com/

类似文章

  1. SpringCloud入门系列之配置中心
  2. SpringCloud入门系列之微服务之间的通信
  3. SpringCloud入门系列之服务链路追踪Sleuth&Zipkin
  4. SpringCloud入门系列之Eureka注册中心
  5. Redis 客户端常用命令

评论区

| 0 评论

还没有评论,快来抢沙发吧!