微服务网关实战——Spring Cloud Gateway

2023-11-03

导读

作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用。本文对Spring Cloud Gateway常见使用场景进行了梳理,希望对微服务开发人员提供一些帮助。

微服务网关SpringCloudGateway

1.概述

Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。

2.核心概念

网关提供API全托管服务,丰富的API管理功能,辅助企业管理大规模的API,以降低管理成本和安全风险,包括协议适配、协议转发、安全策略、防刷、流量、监控日志等贡呢。一般来说网关对外暴露的URL或者接口信息,我们统称为路由信息。如果研发过网关中间件或者使用过Zuul的人,会知道网关的核心是Filter以及Filter Chain(Filter责任链)。Sprig Cloud Gateway也具有路由和Filter的概念。下面介绍一下Spring Cloud Gateway中几个重要的概念。

  • 路由。路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组Filter组成。如果断言路由为真,则说明请求的URL和配置匹配

  • 断言。Java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于http request中的任何信息,比如请求头和参数等。

  • 过滤器。一个标准的Spring webFilter。Spring cloud gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理

如上图所示,Spring cloudGateway发出请求。然后再由Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway web handler。Handler再通过指定的过滤器链将请求发送到我们实际的服务执行业务逻辑,然后返回。

快速入门

以Spring Boot框架开发为例,启动一个Gateway服务模块(以Consul作为注册中心),一个后端服务模块。client端请求经gateway服务把请求路由到后端服务。

前提条件:

  • Consul:版本1.5.0。

  • Spring bot:版本2.1.5。

  • Spring cloud:版本Greenwich.SR1。

  • Redis:版本5.0.5。

1.微服务开发

这里以使用Spring Boot框架开发微服务为例,启动一个服务并注册到Consul。

引入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
 

注册服务到Consul,配置文件配置如下:

spring:
  application:
    name: service-consumer
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        service-name: service-consumer
 

如下定义RestController,发布HTTP接口。

@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    private UserService userService;
    @GetMapping(value = "/info")
    public User info() {
        return userService.info();
    }
}
 

注:此为服务端配置,经Gateway把请求路由转发到该服务上。

2.网关配置
创建一个Gateway服务,引入以下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
 

启动类配置如下:

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

Spring Cloud Gateway对client端请求起到路由功能,主要配置如下:

server:
  port: 8098
spring:
  application:
    name: service-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true   
          lower-case-service-id: true  
    consul:
      host: 127.0.0.1 #注册gateway网关到consul
      port: 8500
      discovery:
        service-name: service-gateway

此时使用http://localhost:8089/service-consumer/user/info访问服务,网关即可对服务进行路由转发,把请求转发到具体后端服务上。此时,url中使用的url前缀service-consumer,是后端服务在Consul注册的服务名称转为小写字母以后的字符串。

最佳实践

01

Gateway网关配置

本文第二部分开发规范中定义了网关进行路由转发的配置,除了上述配置方式还可以使用下面的方式进行配置:


gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
      - id: service_consumer
        uri: lb://service-consumer
        predicates:
        - Path= /consumer/**
        filters:
        - StripPrefix=1

在上面的配置中,配置了一个Path的predicat,将以/consumer/**开头的请求都会转发到uri为lb://service-consumer的地址上,lb://service-consumer(注册中心中服务的名称)即service-consumer服务的负载均衡地址,并用StripPrefix的filter 在转发之前将/consumer去掉。同时将spring.cloud.gateway.discovery.locator.enabled改为false,如果不改的话,之前的http://localhost:8081/service-consumer/user/info这样的请求地址也能正常访问,因为这时为每个服务创建了2个router。

本文第二部分和本节一共讲述了两种配置方式,两种配置都可以实现请求路由转发的功能。参数spring.cloud.gateway.discovery.locator.enabled为true,表明Gateway开启服务注册和发现的功能,并且Spring Cloud Gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。spring.cloud.gateway.discovery.locator.lowerCaseServiceId是将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了)。

gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

02

Gateway跨域访问


Spring Cloud Gateway还针对跨域访问做了设计,可以使用以下配置解决跨域访问问题:

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET
            allowHeaders:
            - Content-Type

在上面的示例中,允许来自https://docs.spring.io的get请求进行访问,并且表明服务器允许请求头中携带字段Content-Type。

03

Gateway 过滤器


Spring Cloud Gateway的filter生命周期不像Zuul那么丰富,它只有两个:“pre”和“post”:

  • pre:这种过滤器在请求被路由之前调用。可以利用这个过滤器实现身份验证、在集群中选择请求的微服务、记录调试的信息。

  • post:这种过滤器在路由到服务器之后执行。这种过滤器可用来为响应添加HTTP Header、统计信息和指标、响应从微服务发送给客户端等。

Spring Cloud gateway的filter分为两种:GatewayFilter和Globalfilter。GlobalFilter会应用到所有的路由上,而Gatewayfilter将应用到单个路由或者一个分组的路由上。

利用Gatewayfilter可以修改请求的http的请求或者是响应,或者根据请求或者响应做一些特殊的限制。更多时候可以利用Gatewayfilter做一些具体的路由配置。

下面的配置是AddRequestParameter Gatewayfilter的相关配置。

spring:
  application:
    name: service-gateway
  cloud:
    gateway:
     discovery:
        locator:
         enabled: true
     routes:
     - id: parameter_route
      uri: http://localhost:8504/user/info
      filters:
      - AddRequestParameter=foo, bar
      predicates:
      - Method=GET
 

上述配置中指定了转发的地址,设置所有的GET方法都会自动添加foo=bar,当请求符合上述路由条件时,即可在后端服务上接收到Gateway网关添加的参数。

另外再介绍一种比较常用的filter,即StripPrefix gateway filter。

配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: stripprefixfilter
        uri: lb://service-consumer
        predicates:
        - Path=/consumer/**
        filters:
        - StripPrefix=1

当client端使用http://localhost:8098/consumer/user/info路径进行请求时,如果根据上述进行配置Gateway会将请求转换为http://localhost:8098/service-consumer/user/info。以此作为前端请求的最终目的地。

04

Gateway请求匹配


Gateway网关可以根据不同的方式进行匹配进而把请求分发到不同的后端服务上。

通过header进行匹配,把请求分发到不同的服务上,配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://baidu.com
        predicates:
        - Header=X-Request-Id, \d+

通过curl测试:curl http://localhost:8080 -H "X-Request-Id:666666",返回页面代码证明匹配成功。

如果是以Host进行匹配,配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://baidu.com
        predicates:
        - Host=**.baidu.com

通过curl http://localhost:8098 -H "Host: www.baidu.com"进行测试,返回页面代码即转发成功。


可以通过POST、GET、PUT、DELTE等不同的方式进行路由:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://baidu.com
        predicates:
        - Method=GET
 

通过 curl http://localhost:8098 进行测试,返回页面代码即表示成功。

上述是单个匹配进行路由,如果把多个匹配合在一起进行路由,必须满足所有的路有条件才会进行路由转发。

05

Gateway熔断


Spring Cloud Gateway也可以利用Hystrix的熔断特性,在流量过大时进行服务降级,同时项目中必须加上Hystrix的依赖。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>    

配置后,Gateway将使用fallbackcmd作为名称生成HystrixCommand对象进行熔断处理。如果想添加熔断后的回调内容,需要添加以下配置:

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: lb://consumer-service
        predicates:
        - Path=/consumer/**
        filters:
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:/fallback
        - StripPrefix=1
hystrix:  
  command:
    fallbackcmd:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 5000 #超时时间,若不设置超时时间则有可能无法触发熔断
 

上述配置中给出了熔断之后返回路径,因此,在Gateway服务模块添加/fallback路径,以作为服务熔断时的返回路径。

@RestController
public class GatewayController {
    @RequestMapping(value = "/fallback")
    public String fallback(){
        return "fallback nothing";
    }
}

fallbackUri: forward:/fallback配置了 fallback 时要会调的路径,当调用 Hystrix 的 fallback 被调用时,请求将转发到/fallback这个 URI,并以此路径的返回值作为返回结果。

06

Gateway重试路由器


通过简单的配置,Spring Cloud Gateway就可以支持请求重试功能。

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://localhost:8504/user/info
        predicates:
        - Path=/user/**
        filters:
        - name: Retry
          args:
            retries: 3
            status: 503
        - StripPrefix=1

Retry GatewayFilter通过四个参数来控制重试机制,参数说明如下:

  • retries:重试次数,默认值是 3 次。

  • statuses:HTTP 的状态返回码,取值请参考:org.springframework.http.HttpStatus。

  • methods:指定哪些方法的请求需要进行重试逻辑,默认值是 GET 方法,取值参考:org.springframework.http.HttpMethod。

  • series:一些列的状态码配置,取值参考:org.springframework.http.HttpStatus.Series。符合的某段状态码才会进行重试逻辑,默认值是 SERVER_ERROR,值是 5,也就是 5XX(5 开头的状态码),共有5个值。

使用上述配置进行测试,当后台服务不可用时,会在控制台看到请求三次的日志,证明此配置有效。

07

Gateway 限流操作


Spring Cloud Gateway本身集成了限流操作,Gateway限流需要使用Redis,pom文件中添加Redis依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

配置文件中配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: rate_limit_route
        uri: lb://service-consumer
        predicates:
        - Path=/user/**
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: "#{@hostAddrKeyResolver}"
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 3
        - StripPrefix=1
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        service-name: service-gateway
        instance-id: service-gateway-233

redis:
host: localhost
port: 6379

 

在上面的配置问价中,配置了Redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:

  • BurstCapacity:令牌桶的总容量。

  • replenishRate:令牌通每秒填充平均速率。

  • Key-resolver:用于限流的解析器的Bean对象的名字。它使用SpEL表达式#{@beanName}从Spring容器中获取bean对象。

注意:filter下的name必须是RequestRateLimiter。

Key-resolver参数后面的bean需要自己实现,然后注入到Spring容器中。KeyResolver需要实现resolve方法,比如根据ip进行限流,则需要用hostAddress去判断。实现完KeyResolver之后,需要将这个类的Bean注册到Ioc容器中。还可以根据uri限流,同hostname限流是一样的。例如以ip限流为例,在gateway模块中添加以下实现:


public class HostAddrKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
    public HostAddrKeyResolver hostAddrKeyResolver() {
        return new HostAddrKeyResolver();
    }
}

把该类注入到spring容器中:

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
    @Bean
    public HostAddrKeyResolver hostAddrKeyResolver(){
        return new HostAddrKeyResolver();
    }
}

基于上述配置,可以对请求基于ip的访问进行限流。

08

自定义Gatewayfilter

Spring Cloud Gateway内置了过滤器,能够满足很多场景的需求。当然,也可以自定义过滤器。在Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。

下面的例子实现了Gatewayfilter,它可以以log日志的形式记录每次请求耗费的时间,具体实现如下:

public class RequestTimeFilter implements GatewayFilter, Ordered {
    private static final Log log = LogFactory.getLog(GatewayFilter.class);
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                    if (startTime != null) {
                        log.info("请求路径:"+exchange.getRequest().getURI().getRawPath() + "消耗时间: " + (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

上述代码中定义了自己实现的过滤器。Ordered的int getOrder()方法是来给过滤器定优先级的,值越大优先级越低。还有一个filter(ServerWebExchange exchange, GatewayFilterChain chain)方法,在该方法中,先记录了请求的开始时间,并保存在ServerWebExchange中,此处是一个“pre”类型的过滤器。然后再chain.filter()的内部类中的run()方法中相当于"post"过滤器,在此处打印了请求所消耗的时间。

接下来将该过滤器注册到router中,代码如下。

 @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/user/**")
                        .filters(f -> f.filter(new RequestTimeFilter())
                                .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                        .uri("http://localhost:8504/user/info")
                        .order(0)
                        .id("customer_filter_router")
                )
                .build();
    }

除了上述代码的方式配置我们自定义的过滤器的方式之外,也可以在application.yml文件中直接配置,这里不再赘述。

启动程序,通过curl http://localhost:8098/user/info控制台会打印出请求消耗时间,日志如下:

....
2019-05-22 15:13:31.221  INFO 19780 --- [ctor-http-nio-4] o.s.cloud.gateway.filter.GatewayFilter   : 请求路径:/user/info消耗时间: 54ms
...
2019-05-22 16:46:23.785  INFO 29928 --- [ctor-http-nio-1] o.s.cloud.gateway.filter.GatewayFilter   : 请求路径:/user/info3消耗时间: 5ms
....

09

自定义GlobalFilter


Spring Cloud Gateway根据作用范围分为GatewayFilter和GlobalFilter,二者区别如下:

  • GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。

  • GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。

在上一小节中定义的是Gatewayfilter,下面实现的是Globalfilter:

public class TokenFilter implements GlobalFilter, Ordered {
    Logger logger= LoggerFactory.getLogger( TokenFilter.class );
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            logger.info( "token 为空,无法进行访问." );
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

上述代码实现了Globalfilter,具体逻辑是判断请求中是否含参数token,如果没有,则校验不通过,对所有请求都有效。如果含有token则转发到具体后端服务上,如果没有则校验不通过。

通过curl http://localhost:8098/user/info进行访问,因为路径中不含有参数token,则无法通过校验,打印日志如下:

2019-05-22 15:27:11.078  INFO 5956 --- [ctor-http-nio-1] com.song.gateway.TokenFilter             : token 为空,无法进行访问.
...

通过curl http://localhost:8098/user/info?token=123进行访问时,则可以获取到后端服务返回结果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

微服务网关实战——Spring Cloud Gateway 的相关文章

  • 用Flutter实现GaiaControl BLE OTA升级功能,支持Android/IOS

    代码基本移植官方GaiaControl Demo 支持RWCP 断点续传 设置蓝牙mtu 协议 这里主要分析GAIA CSR ble ota的过程 协议等等 希望对你有所帮助 这里对蓝牙服务特性订阅都不谈 读者自行了解 Gaia 是CSR
  • DM8锁查询及解决

    锁模拟 session1 与 session2同时对表t2的col1 200的列进行更新 但不提交 session1 SQL gt create table t1 col1 int SQL gt create table t2 col1 i
  • Select For update语句浅析

    Select forupdate语句是我们经常使用手工加锁语句 通常情况下 select语句是不会对数据加锁 妨碍影响其他的DML和DDL操作 同时 在多版本一致读机制的支持下 select语句也不会被其他类型语句所阻碍 借助for upd

随机推荐

  • chi square-卡方分布的定义及性质

    chi square 卡方分布的定义及性质 摘要 2 chi 2 2分布 卡方分布 的定义 g
  • Anchor-Free即插即用

    点击下方卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 后台回复 多模态综述 获取论文 后台回复 ECCV2022 获取ECCV2022所有自动驾驶方向论文 后台回复 领域综述 获取自动驾驶全栈近80篇综述论文 Anchor
  • 做什么副业比较靠谱,这五个正规项目,记得收藏

    人这一生不易 每个阶段都会有压力和烦恼 尤其是成年人 上有老下有小的 生活的重担都在一个人身上 压得人喘不过气 生活的方方面面都需要钱 仅靠工资已经很难维持一家人的开支了 所以很多人打算利用业余时间做点副业 来增加收入 可是不知道做什么 哪
  • Roaming\npm\node_modules\nrm\node_modules\open\index.js:38

    nrm1 2 1版本安装遇到的问题 C Users Cwqiang gt nrm ls C Users Cwqiang AppData Roaming npm node modules nrm node modules open index
  • ViewModel 的基本用法

    文章目录 ViewModel简介 ViewModel 的基本用法 向ViewModel传递参数 ViewModel简介 ViewModel 应该算是Jetpack 中最重要的组件之一了 其实Android 平台上之所以会出现注入MVP MV
  • 统一软件开发过程(RUP)分析

    什么是RUP RUP Rational Unified Process 统一软件开发过程 统一软件过程是一个面向对象且基于网络的程序开发方法论 RUP是风险驱动的 基于Use Case技术的 以架构为中心的 迭代的 可配置的软件开发流程 R
  • 谈谈头文件

    一 头文件的定义 C语言中常常使用预处理指令 include 把另一个文件的内容复制到源文件当中 被复制的文件就是头文件 其后缀名为 h 二 头文件的作用 1 在多文件的工程当中 头文件用于全局变量 外部函数的声明 其本身不包含程序的实现代
  • Type Conversion and Function Overloading___CH_8

    8 1 Implicit type conversion coercion What happens when a type conversion is invoked When a type conversion is invoked w
  • 使用Xpose突破安卓App禁止截屏限制

    WindowManager LayoutParams FLAG SECURE标志的app 这里需要注意下支付宝 网上银行类的app不建议拦截 像支付宝里的付款码 商家拿到后 直接就能扣费 不需要用户这边的确认 所以其实这款应用是可以用在多个
  • HNUCM-OJ 1401: 递归求解

    http acm hnucm edu cn JudgeOnline problem php id 1401 题目描述 使用递归编写一个程序 求以下数列的前n项 s 1 1 2 1 3 1 4 1 5 1 6 1 n 输入 多组数据输入 每组
  • HDF5 使用介绍

    1 HDF5介绍 HDF 是用于存储和分发科学数据的一种自我描述 多对象文件格式 HDF 是由美国国家超级计算应用中心 NCSA 创建的 以满足不同群体的科学家在不同工程项目领域之需要 HDF 可以表示出科学数据存储和分布的许多必要条件 H
  • 第二节:微信小程序静态页面开发初体验

    根据上一节了解到的小程序知识 尝试完成一个入门的demo项目 记录下过程 供日后参考 第一个页面 打算做一个新闻信息展示的静态页面 首先展示一下最后完成的效果 页面很简单 分成三个部分 页面标题 中间的轮播图和下面的图文信息 轮播图的实现
  • 云演CTF: 007.blog

    云演CTF 007 blog 作者 admin 时间 2021 05 28 分类 信息收集 打开就是登录界面 直接 123456 出现弹窗 还以为是js验证 想多了 CTRL u打开源码 function var login login l
  • 复原ip地址--回溯算法

    LeetCode 复原IP地址 给定一个只包含数字的字符串 复原它并返回所有可能的IP地址格式 有效的IP地址正好由四个整数 每个整数位于0到255之间组成 整数之间用 分隔 示例 输入 25525511135 输出 255 255 11
  • 径向基函数

    注意核函数是一回事 径向基函数是另一回事 核函数表示的是高维空间里由于向量内积而计算出来的一个函数表达式 后面将见到 而径向基函数是一类函数 径向基函数是一个它的值 y 只依赖于变量 x 距原点距离的函数 即 也可以是距其他某个中心点的距离
  • 我使用的Vim插件

    2023年9月5日 周二下午 为了方便以后还原自己的Vim插件配置 于是写这篇博客来记录一下 不定期更新 目录 语法检查Syntastic 文件树The NERD tree 自动补全括号auto pairs 超轻量级自动补全vim auto
  • 交换机端口安全实验

    文章目录 一 实验的背景与目的 二 实验拓扑 三 实验需求 四 实验解法 1 PC配置IP地址部分 2 在SW1上开启802 1X身份验证 3 创建一个用户身份验证的用户 用户名为wangdaye 密码为123456 4 创建一个端口隔离组
  • 死锁,死锁必要条件及处理策略

    大自然的搬运工 完美分割线 多线程中 常见的一种问题除了竞态条件外就是死锁 那什么是死锁呢 死锁就是 是指两个或两个以上的进程在执行过程中 因争夺资源而造成的一种互相等待的现象 若无外力作用 它们都将无法推进下去
  • 图片在div中居中

    一 方法1 html代码 div class content img src img 举手yeah png div css代码 content display flex justify content center align items
  • 微服务网关实战——Spring Cloud Gateway

    导读 作为Netflix Zuul的替代者 Spring Cloud Gateway是一款非常实用的微服务网关 在Spring Cloud微服务架构体系中发挥非常大的作用 本文对Spring Cloud Gateway常见使用场景进行了梳理