Gateway网关详解及实践---SpringCloud组件(五)

news2024/10/6 2:37:08

Gateway网关详解及实践

  • 一 简介
    • 1.1介绍
    • 1.2.GateWay核⼼概念
    • 1.3.GateWay核心功能
  • 二Gateway入门案例
    • 2.1 gateway依赖
    • 2.2 gateway配置
    • 2.3 gateway测试
  • 三Gateway面向服务的路由
    • 3.1.门案例问题
    • 3.2.gateway-server结合eureka步骤
      • 3.2.1.添加Eureka客户端依赖
      • 3.2.2.添加Eureka配置
      • 3.2.3修改映射配置
      • 3.2.4.启动测试
    • 3.3局部过滤器
      • 3.3.1.Hystrix
        • 1)引入Hystrix的依赖
        • 2)开启Hystrix,添加@EnableHystrix
        • 3)定义降级处理规则
        • 4)定义降级的处理函数
        • 5)测试
      • 3.3.2.路由前缀
        • 1)问题演示
        • 2)去除路由前缀
    • 3.4全局过滤器
      • 3.4.1.GlobalFilter接口
      • 3.4.2.过滤器顺序
      • 3.4.3.自定义过滤器
        • 3.4.3.1.登录拦截器(实现GlobalFilter接口方式)
        • 3.4.3.2.多过滤器演示(lambda表达式)
    • 3.5.网关限流
      • 3.5.1.令牌桶算法原理
      • 3.5.2.Gateway中限流
        • 1) 引入redis
        • 2) 配置过滤条件key
        • 3) 配置桶参数
      • 3.8.3.测试
  • 源码地址

一 简介

1.1介绍

Spring Cloud GateWay是Spring Cloud的⼀个全新项⽬,⽬标是取代Netflix Zuul, 它基于Spring5.0+SpringBoot2.0+WebFlux(基于⾼性能的Reactor模式响应式通信 框架Netty,异步⾮阻塞模型)等技术开发,性能⾼于Zuul,官⽅测试,GateWay是 Zuul的1.6倍,旨在为微服务架构提供⼀种简单有效的统⼀的API路由管理⽅式。
Spring Cloud GateWay不仅提供统⼀的路由⽅式(反向代理)并且基于 Filter(定义 过滤器对请求过滤,完成⼀些功能) 链的⽅式提供了⽹关基本的功能,例如:鉴权、 流量控制、熔断、路径重写、⽇志监控等。

⽹关在架构中的位置

在这里插入图片描述

1.2.GateWay核⼼概念

Zuul1.x 阻塞式IO 2.x 基于Netty
Spring Cloud GateWay天⽣就是异步⾮阻塞的,基于Reactor模型

⼀个请求—>⽹关根据⼀定的条件匹配—匹配成功之后可以将请求转发到指定的服务地址;⽽在这个过程中,我们可以进⾏⼀些⽐较具体的控制(限流、⽇志、⿊⽩名 单)

  • 路由(route): ⽹关最基础的部分,也是⽹关⽐较基础的⼯作单元。路由由⼀个ID、⼀个⽬标URL(最终路由到的地址)、⼀系列的断⾔(匹配条件判断)Filter过滤器(精细化控制) 组成。如果断⾔为true,则匹配该路由。
  • 断⾔(predicates):参考了Java8中的断⾔java.util.function.Predicate,开发⼈员可以匹配Http请求中的所有内容(包括请求头、请求参数等)(类似于 nginx中的location匹配⼀样),如果断⾔与请求相匹配则路由。
  • 过滤器(filter):⼀个标准的Spring webFilter,使⽤过滤器,可以在请求之前或者之后执⾏业务逻辑。

来⾃官⽹的⼀张图
在这里插入图片描述
其中,Predicates断⾔就是我们的匹配条件,⽽Filter就可以理解为⼀个⽆所不能的拦截器,有了这两个元素,结合⽬标URL,就可以实现⼀个具体的路由转发。

1.3.GateWay核心功能

在这里插入图片描述

  • 路由:gateway加入后,一切请求都必须先经过gateway,因此gateway就必须根据某种规则,把请求转发到某个微 服务,这个过程叫做路由。
  • 权限控制:请求经过路由时,我们可以判断请求者是否有请求资格,如果没有则进行拦截。
  • 限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

二Gateway入门案例

源码地址
Gateway的路由功能,基本步骤如下:

  1. 创建SpringBoot工程gateway_server,引入网关依赖
  2. 编写启动类
  3. 编写基础配置:服务端口,应用名称
  4. 编写路由规则
  5. 启动网关服务进行测试

在这里插入图片描述

2.1 gateway依赖

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

2.2 gateway配置

server:
  port: 10010
spring:
  application:
    name: gateway-server # 服务名
  cloud:
    gateway:
      routes: #路由规则的列表
        - id: consumer-service # 当前路由的唯一标识
          uri: http://127.0.0.1:8080 # 路由的目标微服务地址
          predicates: # 断言,定义请求的匹配规则
            - Path=/consumer/** # Path代表按照路径匹配的规则,/consumer/**是指路径必须以/consumer开头

2.3 gateway测试

启动下列三个服务
在这里插入图片描述
当我们访问http://localhost:10010/consumer/depart/get/1时,首先会进入网关服务,断言判断符合=/consumer/**,因此请求会被代理到http://localhost:8080/consumer/depart/get/1
在这里插入图片描述

三Gateway面向服务的路由

3.1.门案例问题

在这里插入图片描述
在入门案例中路由的目标地址是写死的,在微服务的情况下,可能目标服务是个集群那么这样做显然不合理。我们应该根据服务的名称去Eureka注册中心查找 服务对应的所有实例列表,并且对服务列表进行负载均衡才对!

案例模块
在这里插入图片描述

3.2.gateway-server结合eureka步骤

feign-consumer-8080 feign-eureka-server feign-provider-8081这三个模块沿用了之前旧的部分,具体搭建步骤不在赘述。着重讲解gateway-server结合eureka。

3.2.1.添加Eureka客户端依赖

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

3.2.2.添加Eureka配置

将gateway服务注到eure中

eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka/
  instance:
    prefer-ip-address: true #偏好使用ip地址,而不是host主机名
    ip-address: 127.0.0.1
    instance-id: ${spring.application.name}.${eureka.instance.ip-address}.${server.port}

3.2.3修改映射配置

因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

server:
  port: 10010

eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka/
  instance:
    prefer-ip-address: true #偏好使用ip地址,而不是host主机名
    ip-address: 127.0.0.1
    instance-id: ${spring.application.name}.${eureka.instance.ip-address}.${server.port}

spring:
  application:
    name: gateway-server # 服务名
  cloud:
    gateway:
      routes: #路由规则的列表,可以有多个
        - id: feign-consumer # 当前路由的唯一标识
          uri: lb://feign-consumer  # 路由的目标微服务,lb:代表负载均衡,feign-consumer:代表服务id
          predicates: # 断言,定义请求的匹配规则
            - Path=/consumer/** # Path代表按照路径匹配的规则,/consumer/**是指路径必须以/consumer开头

这里修改了uri的路由方式:

  • lb:负载均衡的协议,将来会使用Ribbon实现负载均衡
  • feign-consumer:服务的id

在这里插入图片描述

3.2.4.启动测试

  • 启动模块

    在这里插入图片描述

  • 查看eureka客户端

    在这里插入图片描述

  • 访问并观察结果

    当我们访问http://localhost:10010/consumer/depart/get/1时,首先会进入网关服务,断言判断符合=/consumer/**,因此请求会被代理到http://localhost:8080/consumer/depart/get/1
    在这里插入图片描述

    在这里插入图片描述

3.3局部过滤器

GatewayFilter Factories是Gateway中的局部过滤器工厂,作用于某个特定路由,允许以某种方式修改传入的HTTP请求或返回的HTTP响应。在这里插入图片描述

3.3.1.Hystrix

网关做请求路由转发,如果被调用的请求阻塞,需要通过Hystrix来做线程隔离和熔断,防止出现故障。

1)引入Hystrix的依赖

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

2)开启Hystrix,添加@EnableHystrix

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

3)定义降级处理规则

可以通过default-filter来配置,会作用于所有的路由规则。

spring:
  application:
    name: gateway-server # 服务名
  cloud:
    gateway:
      routes: #路由规则的列表,可以有多个
        - id: feign-consumer # 当前路由的唯一标识
          uri: lb://feign-consumer  # 路由的目标微服务,lb:代表负载均衡,feign-consumer:代表服务id
          predicates: # 断言,定义请求的匹配规则
            - Path=/consumer/** # Path代表按照路径匹配的规则,/consumer/**是指路径必须以/consumer开头
      default-filters: # 默认过滤项
        - name: Hystrix # 指定过滤工厂名称(可以是任意过滤工厂类型)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径
hystrix:
    command:
      default:
        execution.isolation.thread.timeoutInMilliseconds: 1000 # 失败超时时长
  • default-filters:默认过滤项,作用于所有的路由规则
    • name:过滤工厂名称,这里指定Hystrix,意思是配置Hystrix类型
    • args:配置过滤工厂的配置
      • name:Hystrix的指令名称,用于配置例如超时时长等信息
      • fallbackUri:失败降级时的跳转路径
        在这里插入图片描述

4)定义降级的处理函数

定义一个controller,用来编写失败的处理逻辑:

@RestController
public class FallbackController {

    @RequestMapping(value = "/fallbackTest")
    public Map<String, String> fallBackController() {
        Map<String, String> response = new HashMap<>();
        response.put("code", "502");
        response.put("msg", "服务超时");
        return response;
    }
}

5)测试

重启gateway,不启动consumer,访问http://localhost:10010/consumer/depart/get/1一秒后观察结果发现走了超时方法
在这里插入图片描述
在这里插入图片描述

3.3.2.路由前缀

1)问题演示

在这里插入图片描述

我们之前用/consumer/**这样的映射路径代表feign-consumer这个服务。因此请求feign-consumer服务的一切路径要以/consumer/**开头

比如,访问:localhost:10010/consumer/depart/get/1会被代理到:http://localhost:8080/consumer/depart/get/1

现在,我们在feign-consumer中的controller中定义一个新的接口:

@RestController
@RequestMapping("/test/depart")
public class TestController {
    @GetMapping("/get/{id}")
    public DepartVO getHandle(@PathVariable("id") int id) {
        DepartVO departVO = new DepartVO();
        departVO.setId(id);
        departVO.setName("测试名称");
        return departVO;
    }
}

这个接口的路径是/test/depart/get/1,并不是以/consumer/开头。当访问:localhost:10010/test/depart/get/时,并不符合映射路径,因此会得到404.

无论是 /consumer/**还是/test/**都是feign-consumer中的一个controller路径,都不能作为网关到feign-consumer的映射路径。

因此我们需要定义一个额外的映射路径,例如:/feign-consumer,配置如下

# 路由前缀配置
spring:
  application:
    name: gateway-server # 服务名
  cloud:
    gateway:
      routes: #路由规则的列表,可以有多个
        - id: feign-consumer # 当前路由的唯一标识
          uri: lb://feign-consumer  # 路由的目标微服务,lb:代表负载均衡,feign-consumer:代表服务id
          predicates: # 断言,定义请求的匹配规则
            - Path=/feign-consumer/** # Path代表按照路径匹配的规则,feign-consumer:代表服务id。/feign-consumer/**是指路径必须以/feign-consumer
      default-filters: # 默认过滤项
        - name: feign-consumer # 指定过滤工厂名称(可以是任意过滤工厂类型)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径

那么问题来了:

当我们访问::localhost:10010/feign-consumer/consumer/depart/get/1,映射路径/feign-consumer指向用户服务,会被代理到:http://localhost:8080/feign-consumer/consumer/depart/get/1

当我们访问:localhost:10010/feign-consumer/test/depart/get/1,映射路径/feign-consumer指向用户服务,会被代理到:http://localhost:8080/feign-consumer/test/depart/get/1

而在feign-consumer中,无论是/feign-consumer/consumer/depart/get/1还是/feign-consumer/test/depart/get/1都是错误的,因为多了一个 /feign-consumer

这个 /feign-consumer 是gateway中的映射路径,不应该被代理到微服务,怎办吧?

2)去除路由前缀

解决思路很简单,当我们访问http://localhost:10010/feign-consumer/consumer/depart/get/1时,网关利用 /feign-consumer 这个映射路径匹配到了用户微服务,请求代理时,只要把 /feign-consumer 这个映射路径去除不就可以了吗。

恰好有一个过滤器:StripPrefixFilterFactory可以满足我们的需求。

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#the-stripprefix-gatewayfilter-factory

我们修改刚才的路由配置:
在这里插入图片描述

# 路由前缀配置
spring:
  application:
    name: gateway-server # 服务名
  cloud:
    gateway:
      routes: #路由规则的列表,可以有多个
        - id: feign-consumer # 当前路由的唯一标识
          uri: lb://feign-consumer  # 路由的目标微服务,lb:代表负载均衡,feign-consumer:代表服务id
          predicates: # 断言,定义请求的匹配规则
            - Path=/feign-consumer/** # Path代表按照路径匹配的规则,feign-consumer:代表服务id。/feign-consumer/**是指路径必须以/feign-consumer
          filters:
            - StripPrefix=1
      default-filters: # 默认过滤项
        - name: feign-consumer # 指定过滤工厂名称(可以是任意过滤工厂类型)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径

此时,网关做路由的代理时,就不会把/feign-consumer作为目标请求路径的一部分了。
当我们访问::localhost:10010/feign-consumer/consumer/depart/get/1,会被代理到:http://localhost:8080/consumer/depart/get/1

当我们访问:localhost:10010/feign-consumer/test/depart/get/1,映射路径/feign-consumer指向用户服务,会被代理到:http://localhost:8080/test/depart/get/1

访问测试
在这里插入图片描述

3.4全局过滤器

全局过滤器Global Filter 与局部的GatewayFilter会在运行时合并到一个过滤器链中,并且根据org.springframework.core.Ordered来排序后执行,顺序可以通过getOrder()方法或者@Order注解来指定。

3.4.1.GlobalFilter接口

全局过滤器的顶级接口:
在这里插入图片描述
实现接口,就要实现其中的filter方法,在方法内部完成过滤逻辑,其中的参数包括:

  • ServerWebExchange:一个类似于Context的域对象,封装了Request、Response等服务相关的属性

    在这里插入图片描述

  • GatewayFilterChain:过滤器链,用于放行请求到下一个过滤器

    在这里插入图片描述

3.4.2.过滤器顺序

通过添加@Order注解,可以控制过滤器的优先级,从而决定了过滤器的执行顺序。

一个过滤器的执行包括"pre""post"两个过程:
GlobalFilter.filter()方法中编写的逻辑属于pre阶段
在使用GatewayFilterChain.filter().then()的阶段,属于Post阶段

优先级最高的过滤器,会在pre过程的第一个执行,在post过程的最后一个执行,如图:

在这里插入图片描述

我们可以在pre阶段做很多事情,诸如:

  • 登录状态判断
  • 权限校验
  • 请求限流等

3.4.3.自定义过滤器

定义过滤器只需要实现GlobalFilter即可,不过我们有多种方式来完成:

  • 方式一:定义过滤器类,实现GlobalFilter接口
  • 方式二:通过@Configuration类结合lambda表达式

3.4.3.1.登录拦截器(实现GlobalFilter接口方式)

现在,通过自定义过滤器,模拟一个登录校验功能,逻辑非常简单:

  • 获取用户请求参数中的 access-token 参数
  • 判断是否为"admin"
    • 如果不是,证明未登录,拦截请求
    • 如果是,证明已经登录,放行请求

代码如下:

@Order(0) // 通过注解声明过滤器顺序
@Component
public class LoginFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取token
        String token = exchange.getRequest().getQueryParams().toSingleValueMap().get("access-token");
        // 判断请求参数是否正确
        if(StringUtils.equals(token, "admin")){
            // 正确,放行
            return chain.filter(exchange);
        }
        // 错误,需要拦截,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 结束任务
        return exchange.getResponse().setComplete();
    }
}

在这里插入图片描述
测试:

  • 带错误参数的情况:
    http://localhost:10010/feign-consumer/consumer/depart/get/1在这里插入图片描述

  • 带正确参数的情况:
    http://localhost:10010/feign-consumer/consumer/depart/get/1?access-token=admin在这里插入图片描述

3.4.3.2.多过滤器演示(lambda表达式)

@Configuration
public class FilterConfiguration {

    @Bean
    @Order(-2)
    public GlobalFilter globalFilter1(){
        return ((exchange, chain) -> {
            System.out.println("过滤器1的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("过滤器1的post阶段!");
            }));
        });
    }

    @Bean
    @Order(-1)
    public GlobalFilter globalFilter2(){
        return ((exchange, chain) -> {
            System.out.println("过滤器2的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("过滤器2的post阶段!");
            }));
        });
    }

    @Bean
    @Order(0)
    public GlobalFilter globalFilter3(){
        return ((exchange, chain) -> {
            System.out.println("过滤器3的pre阶段!");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("过滤器3的post阶段!");
            }));
        });
    }
}

http://localhost:10010/feign-consumer/consumer/depart/get/1?access-token=admin
在这里插入图片描述

3.5.网关限流

网关除了请求路由、身份验证,还有一个非常重要的作用:请求限流。当系统面对高并发请求时,为了减少对业务处理服务的压力,需要在网关中对请求限流,按照一定的速率放行请求。

在这里插入图片描述

常见的限流算法包括:

  • 计数器算法
  • 漏桶算法
  • 令牌桶算法

3.5.1.令牌桶算法原理

SpringGateway中采用的是令牌桶算法,令牌桶算法原理:

  • 准备一个令牌桶,有固定容量,一般为服务并发上限
  • 按照固定速率,生成令牌并存入令牌桶,如果桶中令牌数达到上限,就丢弃令牌。
  • 每次请求调用需要先获取令牌,只有拿到令牌,才继续执行,否则选择选择等待或者直接拒绝。

在这里插入图片描述

3.5.2.Gateway中限流

SpringCloudGateway是采用令牌桶算法,其令牌相关信息记录在redis中,因此我们需要安装redis,并引入Redis相关依赖。

1) 引入redis

引入Redis有关依赖:

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

注意:这里不是普通的redis依赖,而是响应式的Redis依赖,因为SpringGateway是基于WebFlux的响应式项目。

在application.yml中配置Redis地址:

spring:
  redis:
    host: localhost

2) 配置过滤条件key

Gateway会在Redis中记录令牌相关信息,我们可以自己定义令牌桶的规则,例如:

  • 给不同的请求URI路径设置不同令牌桶
  • 给不同的登录用户设置不同令牌桶
  • 给不同的请求IP地址设置不同令牌桶

Redis中的一个Key和Value对就是一个令牌桶。因此Key的生成规则就是桶的定义规则。SpringCloudGateway中key的生成规则定义在KeyResolver接口中:

public interface KeyResolver {

	Mono<String> resolve(ServerWebExchange exchange);

}

这个接口中的方法返回值就是给令牌桶生成的key。API说明:

  • Mono:是一个单元素容器,用来存放令牌桶的key
  • ServerWebExchange:上下文对象,可以理解为ServletContext,可以从中获取request、response、cookie等信息

比如上面的三种令牌桶规则,生成key的方式如下:

  • 给不同的请求URI路径设置不同令牌桶,示例代码:

    return Mono.just(exchange.getRequest().getURI().getPath());// 获取请求URI
    
  • 给不同的登录用户设置不同令牌桶

    return exchange.getPrincipal().map(Principal::getName);// 获取用户
    
  • 给不同的请求IP地址设置不同令牌桶

    return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());// 获取请求者IP
    

这里我们选择最后一种,使用IP地址的令牌桶key。

我们在config中定义一个类,配置一个IpKeyResolver 的Bean实例:

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class IpKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

3) 配置桶参数

另外,令牌桶的参数需要通过yaml文件来配置,参数有2个:

  • replenishRate:每秒钟生成令牌的速率,基本上就是每秒钟允许的最大请求数量

  • burstCapacity:令牌桶的容量,就是令牌桶中存放的最大的令牌的数量

完整配置如下:

spring:
  application:
    name: ly-gateway
  cloud:
    gateway:
      default-filters: # 默认过滤项
      - StripPrefix=1 # 去除路由前缀
      - name: Hystrix # 指定过滤工厂名称(可以是任意过滤工厂类型)
        args: # 指定过滤的参数
          name: fallbackcmd  # hystrix的指令名
          fallbackUri: forward:/hystrix/fallback # 失败后的跳转路径
      - name: RequestRateLimiter #请求数限流 名字不能随便写
        args:
          key-resolver: "#{@ipKeyResolver}" # 指定一个key生成器
          redis-rate-limiter.replenishRate: 2 # 生成令牌的速率
          redis-rate-limiter.burstCapacity: 2 # 桶的容量

这里配置了一个过滤器:RequestRateLimiter,并设置了三个参数:

  • key-resolver"#{@ipKeyResolver}"是SpEL表达式,写法是#{@bean的名称},ipKeyResolver就是我们定义的Bean名称

  • redis-rate-limiter.replenishRate:每秒钟生成令牌的速率

  • redis-rate-limiter.burstCapacity:令牌桶的容量

这样的限流配置可以达成的效果:

  • 每一个IP地址,每秒钟最多发起2次请求
  • 每秒钟超过2次请求,则返回429的异常状态码

3.8.3.测试

我们快速在浏览器多次访问http://localhost:10010/feign-consumer/consumer/depart/get/1?access-token=admin,就会得到一个错误:
在这里插入图片描述

429:代表请求次数过多,触发限流了。

源码地址

源码地址
05-gateway-eureka

上一篇:Hystrix详解及实践—SpringCloud组件(四)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL进阶-MySQL体系结构和常见存储引擎的比较

本文介绍MySQL体系结构以及存储引擎&#xff08;InnoDB、MyISAM、Memory&#xff09;的比较 文章目录 MySQL 体系结构连接层服务层连接池系统管理和控制工具SQL接口解析器查询优化器缓存 引擎层存储层 存储引擎InnoDBMyISAMMemory区别 MySQL 体系结构 连接层 最上层是一些客户…

Rust 正式发布八周年纪念日 2023.5.15

图源&#xff1a;维基百科 目录 Rust 1. Rust的特点 1.1 安全性 1.2 并发性 1.3 性能 1.4 代码可读性 2. 使用场景 2.1 系统编程 2.2 Web开发 2.3 游戏开发 3. 与其他语言的对比 4. 代码示例 1. Hello, World! 2. 简单的函数 3. 变量绑定和变量类型推断 4. 结…

dayday60-120

目录 60 申论强化361 申论强化462 模考大赛错题63 言语真题164 言语真题65 言语真题366 判断真题1676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 60 申论强化3 1.公文题的格式不允许丢…

Vector - CAPL - CAN消息自动化设置

目录 canGetDataLength -- 获取CAN消息有效负载长度 代码示例 canOutputErrorFrame -- 模拟发送错误帧消息 代码示例 isStdId & isExtId -- 判断报文是标准帧&扩展帧 代码示例 mkExtId -- 将标准帧转换为扩展帧 代码示例 valOfId -- CAN消息的长值标识符 代…

基于SpringBoot的原创歌曲平台的设计与实现

背景 原创歌曲分享平台&#xff0c;为了随时随地查看原创歌曲分享信息提供了便捷的方法&#xff0c;更重要的是大大的简化了管理员管理原创歌曲分享信息的方式方法&#xff0c;更提供了其他想要了解原创歌曲分享信息及运作情况以及挑选方便快捷的可靠渠道。相比于传统原创歌曲…

六级备考33天|CET-SET6|六级口语|备考手册|考试样题|热门话题

目录 样题 1 The benefits of college athletics 2 The adverse impact of cheating on examinations 3 My view on working part-time during ones college years 4 Should students take part in extracurricular activities 5 If you choose overseas studies 6 The…

基于stm32物联网开发板(3)--SYN6288语音模块

基于stm32物联网开发板(3)–SYN6288语音模块 1.SYN6288语音模块展示示例 SYN6288语音模块 2.概述 SYN6288-A语音合成模块是一款性价比更高&#xff0c;效果更自然的一款中高端语音合技术 。 SYN6288-A通过异步串口接收待合成的文本&#xff0c;实现文本到声音&#xff08;TTS&…

SPL即将取代SQL?

先来看看两者的定义。 SQL&#xff1a;结构化查询语言。 SPL&#xff1a;结构化数据计算语言。 既然是比较文章&#xff0c;那必然是要突出一方的优势。 以下是SQL的痛点以及SPL的优点&#xff1a; 1.SQL缺乏离散性&#xff0c;集合化不彻底&#xff1b;SPL离散与集合充分结…

如何删除 Docker 镜像、容器和卷?

Docker 是一款常用的容器化平台&#xff0c;通过 Docker 可以将应用程序打包成一个独立的容器&#xff0c;方便地在不同的环境中运行。随着 Docker 的广泛使用&#xff0c;删除 Docker 镜像、容器和卷的操作也变得非常重要。在本文中&#xff0c;我们将介绍如何删除 Docker 镜像…

Python消费Kafka与优化

一.背景与问题 之前使用kafka-python库进行消费数据处理业务逻辑&#xff0c;但是没有深入里面的一些细节&#xff0c;导致会遇到一些坑。正常普通我们常见的一个消费者代码:(假设topic的分区数是20个) from kafka import KafkaConsumerbootstrap_servers [localhost:9092] g…

vim命令大全,非常详细,强烈建议收藏!

Vim是一款常用的文本编辑器&#xff0c;具有强大的功能和高度的可定制性。在本文中&#xff0c;我们将详细介绍Vim的常用命令&#xff0c;并提供相关的示例。如果您是初学者或已经熟练使用Vim&#xff0c;这篇文章都能为您提供帮助。 基本命令 以下是一些基本的Vim命令&#x…

一文足矣:Unity行为树

目录 前言 unity行为树简介 一个简单的敌人AI 正文 个人对行为树的理解 有限状态机与行为树 基本框架 BTNode DataBase 行为树入口 行为树的事件GraphEvent 发送事件 监听事件 脚本发送事件 行为树的管理&操作 一、操作单颗树 二、管理所有树 自定义Task任务 …

python字符串的三种定义方式

之前我们讲过 一些字符串的定义 但当时是说 被双引号包裹的就是字符串 其实并不是特别严谨 这个叫双引号的定义方式 也没错 也只有字符串会被双引号包裹 但还有其他的定义方式 这里 还是先说答案 三种定义方式分别是 单引号定义 双引号定义 三引号定义 参考代码如下 #单引定义…

《点云处理算法》——GROR配准

GROR配准方法&#xff08;实时性挺好&#xff09; 一、 效果展示二、VS运行2.1 github源码下载2.2 编译运行 三、后续集成 一、 效果展示 二、VS运行 最近和小伙伴交流&#xff0c;他发现一个好用的配准方法&#xff0c;放在这里实现一下 2.1 github源码下载 gror 2.2 编译…

hexo,typecho,wordpress,hugo的官网下载及介绍

Typecho Typecho是一个轻量级的PHP博客系统&#xff0c;它的优点在于易于安装、使用和管理。Typecho使用MySQL数据库来存储文章和评论&#xff0c;同时支持主题和插件的自定义。Typecho适用于个人博客、技术博客等&#xff0c;因为它的易用性和可扩展性较高。 WordPress Word…

分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之手动实现

分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器之手动实现 目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器之手动实现 实现任务阶段1- 创建Tomcat, 并启动 说明: 分析代码实现 修改…

Android源码之Application与Activity创建时机分析

前言 我们知道App进程是由SystemServer启动的Android启动流程 那App对应的Application以及第一个Activity又是如何创建的呢&#xff1f; 源码分析(API 30为例) 我们从ActivityThread.main函数入手&#xff1b; public static void main(String[] args) {...ActivityThread t…

第八章结构型模式—装饰者模式

文章目录 装饰者模式解决的问题概念结构 案例使用装配者进行改进 使用场景JDK源码分析 静态代理和装饰者的区别 结构型模式描述如何将类或对象按某种布局组成更大的结构&#xff0c;有以下两种&#xff1a; 类结构型模式&#xff1a;采用继承机制来组织接口和类。对象结构型模式…

【Linux】volatile | SIGCHLD | 多线程概念

文章目录 1. volatile编译器优化 2.SIGCHLD信号验证SIGCHLD的存在 3. 多线程多线程概念理解概念什么是多线程调度成本低局部性原理 什么叫做进程 1. volatile 在vscode中&#xff0c;创建signal.c文件 故意在while中没有写代码块&#xff0c;让编译器认为在main中&#xff0c;…

爬虫+可视化 | 动态展示2020东京奥运会奖牌世界分布

文章目录 前言1. 导入模块2. 数据爬取3. 地图展示 3.1 2020东京奥运会奖牌数世界分布3.2 2020东京奥运会金牌世界分布3.3 2020东京奥运会金、银、铜世界分布 前言 2020东京奥运会已落下帷幕&#xff0c;中国军团共获得88枚奖牌&#xff0c;其中38枚金牌、32枚银牌、18枚铜牌…