SpringCloud-Alibaba-Gateway-Sentinel

SpringCloud-Alibaba-Gateway+Sentinel

网关简介

大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用。

这样的架构,会存在着诸多的问题:

  • 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
  • 认证复杂,每个服务都需要独立认证。
  • 存在跨域请求,在一定场景下处理相对复杂。

上面的这些问题可以借助API网关来解决。

所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。

添加上API网关之后,系统的架构图变成了如下所示:

我们也可以观察下,我们现在的整体架构图:

在业界比较流行的网关,有下面这些:

Ngnix+lua:使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用,lua是一种脚本语言,可以来编写一些简单的逻辑, nginx支持lua脚本

Kong:基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。

Zuul :Netflflix开源的网关,功能丰富,使用JAVA开发,易于二次开发 问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx

Spring Cloud Gateway:Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。

注意:SpringCloud alibaba技术栈中并没有提供自己的网关,我们可以采用Spring Cloud Gateway来做网关

Gateway简介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。

优点:

性能强劲:是第一代网关Zuul的1.6倍

功能强大:内置了很多实用的功能,例如转发、监控、限流等

设计优雅,容易扩展

缺点:

  • 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
  • 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
  • 需要Spring Boot 2.0及以上的版本,才支持

假设你后台部署了几百个服务,现在有个前端兄弟,人家请求是直接从浏览器那儿发过来的。打个比方:人家要请求一下库存服务,你难道还让人家记着这服务的名字叫做inventory-service?部署在5台机器上?就算人家肯记住这一个,你后台可有几百个服务的名称和地址呢?难不成人家请求一个,就得记住一个?你要这样玩儿,那真是友谊的小船,说翻就翻!

上面这种情况,压根儿是不现实的。所以一般微服务架构中都必然会设计一个网关在里面,像android、ios、pc前端、微信小程序、H5等等,不用去关心后端有几百个服务,就知道有一个网关,所有请求都往网关走,网关会根据请求中的一些特征,将请求转发给后端的各个服务。

而且有一个网关之后,还有很多好处,比如可以做统一的降级、限流、认证授权、安全,等等。

这样你就不用在所以服务中去重复干这些事情了,只需要在网关中统一管理

Gateway基本配置介绍

就介绍常用的:

  1. id: 当前路由配置的唯一标识

  2. uri: 服务的访问路径
    可以是http://127.0.0.1:10010/ 也可以是 http://baidu.com/

    还可以是lb://服务名称(需要开启服务发现)

  3. predicates 断言( 就是路由转发要满足的条件) 以下配置选择一个就行

    1. Path(配置对于请求路径的匹配规则) 多个参数用逗号隔开

      - Path = /aa/**,/bb/**
      

      或者

      - Path=/red/{segment},/blue/{segment}
      

      例如匹配/red/1或/red/blue或/blue/green

    2. 时间相关 在什么时间段能访问 (美国时间)

      • After 设置在xxx时间之后可以访问
      ```yaml
      - After = 2017-01-20T17:42:47.789-07:00[America/Denver]
      ```
      
      • Before 设置在xx时间之前可以访问

        - Before = 2017-01-20T17:42:47.789-07:00[America/Denver]
        
      • Between 设置时间段内可以访问

        - Between = 2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver]
        

    5.**Filters ** 路径过滤

    1. 添加前缀 (用不多)

      比如:原来的请求路径是: http://127.0.0.1:10010/feign/user2

      访问的时候http://127.0.0.1:10010/user2就行了 省略/feign 就行了

      但是实际上会在你请求的路径上自动给你在前面补上http://127.0.0.1:10010/feign/user2

      - PrefixPath=/feign
      
    2. 去除前缀 (用的比较多-一般配合predicates的- Path使用 )

      比如: 原来访问的时候http://127.0.0.1:10010/abc/feign/user1

      那么实际上会变成http://127.0.0.1:10010/feign/user1

      - StripPrefix=1  #StripPrefix=1代表就是去掉前1个  StripPrefix=2 代表就是去掉前2个 以此类推
      
    3. 组合使用

      原访问路径 http://127.0.0.1:10010/abc/user1/

                predicates: #允许的路由地址
                  - Path=/abc/user1/**
                filters: #路径过滤
                  - StripPrefix=1  #删除前缀/abc
                  - PrefixPath=/feign #添加前缀 /feign         
      

      经过代理处理后实际访问的路径http://127.0.0.1:10010/feign/user1/

跨域配置

spring:
  cloud:
	gateway:
      globalcors:  # 跨域配置
        cors-configurations:
          '[/**]':
            allow-credentials: true #允许携带认证信息Cookie
            # 我们可以通 过域名的方式 也可以通过指定ip  还可以 allowed-origins: "*"  #放行所有跨域请求
            allowed-origins: "*"
            allowed-headers: "*"   #允许所有请求头
            allowed-methods: "*"   #允许所有请求方式
            max-age: 86400  # 86400 秒,也就是 24 小时 在有效时间内,浏览器无须为同一请求再次发起预检请求,可以减少发送请求的次数,减少系统部分压力。

开启gateway-nacos服务发现

spring:
  cloud:	
    gateway:
      discovery:
        locator:
          enabled: true # 让gateway可以发现nacos中的微服务

然后就可以使用lb://服务名称 的方式

配置sentinel服务

参考 SpringCloud-Alibaba之Sentinel-流量监控 这篇文章 只需要把jar包运行就行了

Gateway+sentinel项目

项目结构

Maven

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
    </properties>

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

    <!--     Springboot 版本要高于 spring-cloud-alibaba 否则报错-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <dependencies>

        <!-- spring-cloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR9</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
<!--        在网关中不能添加 web服务依赖,因为网关服务就是代理各大服务的作用 而不是业务服务端 否则报错  -->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-web</artifactId>-->
<!--            <version>2.4.1</version>-->
<!--        </dependency>-->


        <!--        将服务注册到Nacos里-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>


        <!--gateway网关 版本必须和Springboot 一致否则会出现版本问题-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
<!--     alibaba-gateway网关限流  -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            <version>1.8.1</version>
        </dependency>
        
        <!--      sentinel 流量监控控制台   -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

    </dependencies>

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

application.yml

server:
  port: 21212

spring:
  application:
    name: nacos-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.81.100:80 #注册中心地址
    sentinel:
      transport:
        dashboard: 192.168.81.129:8888 #sentinel的地址
        port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
    gateway:
      globalcors:  # 跨域配置
        cors-configurations:
          '[/**]':
            allow-credentials: true #允许携带认证信息Cookie
            # 我们可以通 过域名的方式 也可以通过指定ip  还可以 allowed-origins: "*"  #放行所有跨域请求
            allowed-origins: "*"
            allowed-headers: "*"   #允许所有请求头
            allowed-methods: "*"   #允许所有请求方式
            max-age: 86400  # 86400 秒,也就是 24 小时 在有效时间内,浏览器无须为同一请求再次发起预检请求,可以减少发送请求的次数,减少系统部分压力。
      discovery:
        locator:
          enabled: true # 让gateway可以发现nacos中的微服务
      routes:
        - id: path_route_1    # 当前路由的标识, 要求唯一
          uri:  lb://nacos-consumer # lb指的是从nacos中按照名称获取微服务,并遵循负载均
          predicates:       # 断言(就是路由转发要满足的条件)
            - Path=/api/consumer/**    # 配置Path路由  使用 http://localhost:21212/api 进行访问即可
          filters:          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=2  # 转发之前去掉2层路径  比如 http://localhost:21212/api/consumer 转发之后http://localhost:21212/
        - id: path_route_2    # 当前路由的标识, 要求唯一
          uri:  lb://nacos-provider  # lb指的是从nacos中按照名称获取微服务,并遵循负载均
          predicates:       # 断言(就是路由转发要满足的条件)
            - Path=/api/provider/**    # 配置Path路由  使用 http://localhost:21212/api 进行访问即可
          filters:          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=2  # 转发之前去掉2层路径  比如 http://localhost:21212/api/provider/ 转发之后http://localhost:21212/

GatewayConfig

package com.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

@Configuration
public class GatewayConfig {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfig(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }



    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        //分别给指定的资源 配置限流
        //  GatewayFlowRule 资源名称,对应路由id     setCount  限流阈值   setIntervalSec 统计时间窗口,单位是秒,默认是 1 秒
        //简单来说就是     如果Count=1    IntervalSec=1  那么一秒钟只允许访问一次   (用于测试)
        // 如果Count=5    IntervalSec=2  那么2秒钟只允许访问5次   (用于测试)
        // 如果Count=60   IntervalSec=3  那么3秒钟只允许访问60次 也就是1秒20次  一般用于生产(一般够了)
        // 如果是商城网站那么Count和IntervalSec 这个需要计算平均每天每秒最大的请求量是多少然后在设置
        int count=1;
        int intervalSec=1;

        //设置指定路由的限流
        rules.add(new GatewayFlowRule("path_route_1").setCount(count).setIntervalSec(intervalSec));
        rules.add(new GatewayFlowRule("path_route_2").setCount(count).setIntervalSec(intervalSec));

        GatewayRuleManager.loadRules(rules);

    }





    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 429);
                map.put("message", "1s内请求次数过多-接口被限流了");
                return ServerResponse.status(HttpStatus.OK).
                        contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }



}

在这里我们把每一个服务的总限流都配置了 ,如果想要单独配置服务的某一个路径…的具体限流或者降级…

我们可以访问: http://192.168.81.129:8888/ sentinel的控制台来具体配置

其他配置方式 参考 SpringCloud-Alibaba之Sentinel-流量监控 这篇文章里有

NacosGatewayApplication

package com.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
public class NacosGatewayApplication {

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

}

测试

运行我们之前的 nacos-consumer 服务和 nacos-provider服务

http://localhost:21212/api/consumer/echo/Hello 正确

http://localhost:21212/echo/Hello 错误

http://localhost:21212/api/provider/echo/Hello 正确

http://localhost:21212/echo/Hello 错误

网关集群

利用nginx,给多个网关做代理轮询模式 , 然后nginx在集群就行了

点赞 -收藏加 -关注
便于以后复习和收到最新内容
有其他问题在评论区讨论-或者私信我-收到会在第一时间回复
感谢,配合,希望我的努力对你有帮助^_^
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡安民

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值