深入探索Spring Cloud Gateway:微服务网关的最佳实践

优质博文:IT-BLOG-CN

Spring Cloud Gateway作为Spring Cloud框架的第二代网关,在功能上要比Zuul更加的强大,性能也更好。随着Spring Cloud的版本迭代,Spring Cloud官方有打算弃用Zuul的意思。在笔者调用了Spring Cloud Gateway的使用和功能上,Spring Cloud Gateway替换掉Zuul的成本上是非常低的,几乎可以无缝切换。Spring Cloud Gateway几乎包含了Zuul的所有功能。

一、网关定义

API网关是一个反向路由,屏蔽内部细节,为调用者提供统一入口,接收所有调用者请求,通过路由机制转发到服务实例。API网关是一组“过滤器Filter”集合,可以实现一系列与核心业务无关的横切面功能,如安全认证、限流熔断、日志监控

网关在系统中所处的位置:

二、快速开始

网关启动步骤(代码演示):

【1】添加依赖

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

【2】配置文件

spring:
  cloud:
    gateway:
      discovery: # 启用通过服务发现自动创建路由。
        locator:
          enabled: true
      routes:
        - id: example_route   #路由ID,根据业务自行定义
          uri: lb://example-service  #目标服务的地址,这里使用 lb:// 前缀来表示负载均衡。可以是 HTTP(s) URI 或其他协议的 URI - http://bin.org:80/get。
          predicates:
            - Path=/example/** #predicates: 谓词数组,用于匹配请求。常见的谓词包括 Path、Method、Header 等。
          filters:   #过滤器数组,用于在请求被转发到目标服务之前和之后进行处理。常见的过滤器包括 AddRequestHeader、StripPrefix、RewritePath 等。
            - AddRequestHeader=X-Example, ExampleValue #过滤器会在请求头中添加 X-Example,值为 ExampleValue。
            - StripPrefix=1 #过滤器会移除路径中的第一个前缀。例如,请求路径 /example/test 会变成 /test。
        - id: rate_limited_route
          uri: http://ratelimited.org
          predicates:
            - Path=/ratelimited/**
          filters:
            - RequestRateLimiter=redis-rate-limiter # 限流:通过 RequestRateLimiter 过滤器实现
        - id: retry_route
          uri: http://retry.org
          predicates:
            - Path=/retry/**
          filters:
            - Retry=5 #重试:通过 Retry 过滤器实现。
      default-filters: #default-filters 是全局过滤器数组,适用于所有路由。这个过滤器会在所有响应中添加 X-Response-Default 头,值为 Default。
        - AddResponseHeader=X-Response-Default, Default
      globalcors: #globalcors 用于配置全局的 CORS(跨域资源共享)设置。
        corsConfigurations: #corsConfigurations: 定义 CORS 配置的路径模式。
          '[/**]': #匹配所有路径。
            allowedOrigins: "*" #允许的源,* 表示允许所有源。
            allowedMethods: #允许的 HTTP 方法,包括 GET、POST、DELETE 和 PUT。
              - GET
              - POST
              - DELETE
              - PUT

三、Spring Cloud GateWay 架构图

客户端向Spring Cloud Gateway发出请求。 在Gateway Handler Mapping中找到请求相对匹配路由(这个时候就用到predicate),则将其发送到Gateway web handler处理。 handler处理请求时会经过一系列的过滤器链。 过滤器链被虚线划分的原因是过滤器链可以在发送代理请求之前或之后执行过滤逻辑。 先执行所有pre过滤器逻辑,然后进行代理请求。 在发出代理请求之后,收到代理服务的响应之后执行post过滤器逻辑。这跟Zuul的处理过程很类似。在执行所有pre过滤器逻辑时,往往进行了鉴权、限流、日志输出等功能,以及请求头的更改、协议的转换;转发之后收到响应之后,会执行所有post过滤器的逻辑,在这里可以响应数据进行了修改,比如响应头、协议的转换等。在上面的处理过程中,有一个重要的点就是将请求和路由进行匹配,这时候就需要用到predicate,它是决定了一个请求走哪一个路由。

四、SpringColoud GateWay 核心组件

集合上面的配置和架构图进行说明

【1】Route路由: Gateway的基本构建模块,它由ID、目标URL、断言集合和过滤器集合组成。如果聚合断言结果为真,则匹配到该路由。

Route路由-动态路由实现原理: 配置变化Apollo + 服务地址实例变化NacosSpring Cloud Gateway通过RouteDefinitionLocatorRouteRefreshListener等组件实现动态路由。

先看下配置信息,方便后面原理的理解:SpringCloudGateway bootstrap.yml的配置如下:

spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
    apollo:
      bootstrap:
        enabled: true
      meta: ${APOLLO_META:localhost:8080}

application.yml的配置如下:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
apollo:
  bootstrap:
    namespaces: application # 1、登录 Apollo 控制台。 2、创建一个新的配置,例如 application.yml。 3、内容就是上看配置的SpringCloud Gateway 配置的路由信息

1、RouteDefinitionLocatorSpring Cloud Gateway启动时,会通过RouteDefinitionLocatorApollo加载初始的路由定义。
2、DiscoveryClientRouteDefinitionLocator:使用Nacos进行服务发现,从Nacos获取动态路由定义。
3、RouteDefinitionRepository:加载的路由定义会存储在RouteDefinitionRepository中,供后续路由匹配使用。
4、RouteRefreshListener:监听路由定义的变化事件(如配置更新、服务实例变化等)。当监听到路由定义变化事件时,触发路由刷新操作,更新网关的路由规则,重新加载并应用新的路由配置。

GatewayHandlerMapping根据预先配置的路由信息和请求的属性(如路径、方法、头部信息等)来确定哪个路由与请求匹配。它使用谓词Predicates来进行匹配判断。

【2】Predicate断言: 这是一个Java 8 Function Predicate。输入类型是Spring Framework ServerWebExchange。允许开发人员匹配来自HTTP请求的任何内容,例如Header或参数。Predicate接受一个输入参数,返回一个布尔值结果。Spring Cloud Gateway内置了许多Predict,这些Predict的源码在org.springframework.cloud.gateway.handler.predicate包中,如果读者有兴趣可以阅读一下。现在列举各种 Predicate如下图:

在上图中,有很多类型的Predicate,比如说时间类型的 Predicated[AfterRoutePredicateFactory BeforeRoutePredicateFactory BetweenRoutePredicateFactory],当只有满足特定时间要求的请求会进入到此Predicate中,并交由Router处理;Cookie类型的CookieRoutePredicateFactory,指定的Cookie满足正则匹配,才会进入此Router。以及hostmethodpathquerparamremoteaddr类型的Predicate,每一种Predicate都会对当前的客户端请求进行判断,是否满足当前的要求,如果满足则交给当前请求处理。如果有很多个Predicate,并且一个请求满足多个Predicate,则按照配置的顺序第一个生效。

Predicate 断言配置:

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]
            - Host=**.foo.org
            - Path=/headers
            - Method=GET
            - Header=X-Request-Id, \d+
            - Query=foo, ba.
            - Query=baz
            - Cookie=chocolate, ch.p

在上面的配置文件中,配置了服务的端口为8080,配置spring cloud gateway相关的配置,id标签配置的是routerid,每个router都需要一个唯一的iduri配置的是将请求路由到哪里,本案例全部路由到https://www.baidu.com

Predicates After=2017-01-20T17:42:47.789-07:00[America/Denver] 会被解析成PredicateDefinition对象name =After ,args= 2017-01-20T17:42:47.789-07:00[America/Denver]。需要注意的是PredicatesAfter这个配置,遵循契约大于配置的思想,它实际被 AfterRoutePredicateFactory这个类所处理,这个After就是指定了它的Gateway web handler类为AfterRoutePredicateFactory,同理,其他类型的Predicate也遵循这个规则。当请求的时间在这个配置的时间之后,请求会被路由到指定的URL。跟时间相关的Predicates还有 Before Route Predicate FactoryBetween Route Predicate Factory,读者可以自行查阅官方文档,再次不再演示。

Query=baz Query的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。经过测试发现只要请求汇总带有baz参数即会匹配路由[localhost:8080?baz=x&id=2],不带baz参数则不会匹配。

Query=foo, ba.:这样只要当请求中包含foo属性并且参数值是以 ba开头的长度为三位的字符串才会进行匹配和路由。使用curl测试,命令行输入:curl localhost:8080?foo=bab测试可以返回页面代码,将foo的属性值改为babx再次访问就会报404,证明路由需要匹配正则表达式才会进行路由。

Header=X-Request-Id, \d+:使用curl测试,命令行输入:curl http://localhost:8080 -H "X-Request-Id:88" 则返回页面代码证明匹配成功。将参数-H "X-Request-Id:88"改为-H "X-Request-Id:spring"再次执行时返回404证明没有匹配。

【3】Filter过滤器:方案一:写死在代码中

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        //openapi路由转发
        .route("openapi_route", p -> p.path( "/openapi/**").filters(f->f.removeRequestHeader("Expect"))
        .uri("lb://order-openapi-service"))
        .build();
}

方案二:配置文件yml

# gateway 的配置形式
 routes:
  - id: order-service #路由ID,没有规定规则但要求唯一,建议配合服务名。
    uri: lb://order-service
    predicates:
      - Path=/order/**
    filters:
      - ValidateCodeGatewayFilter

Filter过滤器:Filter按处理顺序Pre Filter / Post Filter

Filter按作用范围分为: GlobalFilter全局过滤器。GatewayFilter 指定路由的过滤器。
Filter过滤器-扩展自定义Filter Filter支持通过spi扩展。实现GatewayFilterOrdered接口。
Filter方法: 过滤器处理逻辑。getOrder:定义优先级,值越大优先级越低。

全局过滤器示例: 创建一个全局过滤器类,实现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 is empty..." );
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
      // // 过滤器的执行顺序,值越小优先级越高
        return -100;
    }
}

自定义路由过滤器示例: 创建自定义的路由过滤器,可以实现GatewayFilter接口:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyCustomFilter extends AbstractGatewayFilterFactory<MyCustomFilter.Config> {

    public MyCustomFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 前置过滤逻辑
            System.out.println("Custom Pre Filter executed");
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // 后置过滤逻辑
                System.out.println("Custom Post Filter executed");
            }));
        };
    }

    public static class Config {
        // 配置属性
    }
}

在配置文件中使用自定义过滤器:

spring:
  cloud:
    gateway:
      routes:
      - id: my_route
        uri: http://httpbin.org:80
        predicates:
        - Path=/get
        filters:
        - name: MyCustomFilter

五、Gateway 限流

Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,适用RedisLua脚本实现了令牌桶链接的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,Lua脚本在如下图所示的文件夹中:

以案例的形式来讲解如何在SpringCloud Gateway中使用内置的限流过滤器工厂来实现限流。首先在工程的pom文件中引入Gateway的起步依赖和RedisReactive依赖,代码如下:

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

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

在配置文件中做以下的配置:

server:
  port: 8081
spring:
  cloud:
    gateway:
      routes:
      - id: limit_route
        uri: lb://PRODUCTCENTOR   # PRODUCTCENTOR是注册到注册中心的服务名,格式为 lb://服务名
        predicates:
        - Path=/**
        filters:
        - StripPrefix=1
        - name: RequestRateLimiter  #拦截器,会对上述的请求进行拦击
          args:
            key-resolver: '#{@hostAddrKeyResolver}'
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 3
  application:
    name: gateway-limiter
  redis:
    host: localhost
    port: 6379
    database: 0

过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view

在上面的配置文件,指定程序的端口为8081,配置了Redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:
【1】burstCapacity:令牌桶总容量。
【2】replenishRate:令牌桶每秒填充平均速率。
【3】key-resolver:用于限流的键的解析器的Bean对象的名字。它使用SpEL表达式根据#{@beanName}Spring容器中获取Bean对象。

KeyResolver需要实现resolve方法,比如根据Hostname进行限流,则需要用hostAddress去判断。实现完KeyResolver之后,需要将这个类的Bean注册到Ioc容器中。

public class HostAddrKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

}

@Bean
public HostAddrKeyResolver hostAddrKeyResolver() {
    return new HostAddrKeyResolver();
}

可以根据URL去限流,这时KeyResolver代码如下:

public class UriKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getURI().getPath());
    }

}

@Bean
public UriKeyResolver uriKeyResolver() {
    return new UriKeyResolver();
}

也可以以用户的维度去限流:

// 省略部分代码
@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

jmeter进行压测,配置10 thread去循环请求lcoalhost:8081,循环间隔1s。从压测的结果上看到有部分请求通过,由部分请求失败。通过redis客户端去查看redis中存在的key。如下:

127.0.0.1:6379> keys *
1> "request_rate_limiter.<127.0.0.1>.timestamp"
2> "request_rate_limiter.<127.0.0.1>.tokens"

可见,RequestRateLimiter是使用Redis来进行限流的,并在redis中存储了2key。关注这两个key含义可以看lua源代码。

六、Zuul 与 Spring Cloud Gateway 比较

优点缺点
Gateway1、线程开销小2、使用轻量级 Netty 异步IO实现通信3、支持各种长连接,WebSocket4、Spring 官方推荐,重点支持,功能较 Zuul更丰富,支持限流监控等
Zuul1、编码模型简单2、开发调试运维简单

ZuulGateway压测结果: 休眠时间模仿后端请求时间,线程数2000,请求时间360秒=6分钟。配置情况:Gateway默认配置,Zuul网关的Tomcat最大线程数为400hystrix超时时间为100000

休眠时间测试样本,单位=个Zuul/Gateway平均响应时间,单位=毫秒Zuul/Gateway99%响应时间,单位=毫秒Zuul/Gateway错误次数,单位=个Zuul/Gateway错误比例Zuul/Gateway
休眠100ms294134/10593212026/5466136/1774104/00.04%/0%
休眠300ms101194/3999095595/148915056/16901114/01.10%/0%
休眠600ms51732/20126211768/297527217/32032476/04.79%/0%
休眠1000ms31896/12095619359/491446259/51153598/011.28%/0%

测试结果:Gateway在高并发和后端服务响应慢的场景下比Zuul的表现要好

七、SpringCloud GateWay 与 Nginx 组合使用

因为和GateWay相关所以这里介绍一下

NginxSpring Cloud Gateway可以组合使用,以实现高效的负载均衡和网关功能。Nginx通常用于处理静态内容、SSL终止、负载均衡等,而Spring Cloud Gateway主要用于动态路由、过滤和服务网关功能。下面是一个基本的配置示例,展示了如何将NginxSpring Cloud Gateway结合使用。这里主要说下Nginx中的配置:

配置Nginx作为反向代理,将外部请求转发到Spring Cloud Gateway

nginx.conf配置示例

http {
    upstream gateway {  # 定义一个名为 gateway 的上游服务器组,包含 Spring Cloud Gateway 的地址(localhost:8080)。
        server localhost:8080;
    }

    server { # 配置 Nginx 服务器块。
        listen 80;  # 监听80端口。

        location / { #  匹配所有请求。
            proxy_pass http://gateway; # http://gateway: 将请求转发到上游服务器组 gateway。
            proxy_set_header Host $host; # 设置一些头信息,用于保持客户端信息。
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

工作流程:
【1】客户端请求:客户端发送请求到Nginx
【2】Nginx转发:Nginx接收到请求后,将其转发到Spring Cloud Gateway
【3】Spring Cloud Gateway路由:Spring Cloud Gateway根据配置文件中的路由规则,将请求转发到对应的微服务。
【4】微服务响应:微服务处理请求并返回响应,通过Spring Cloud GatewayNginx返回给客户端。

SpringCloud GateWay 与 Nacos 组合使用

bootstrap.yml用于配置Nacos的基本信息:

spring:
  application:
    name: gateway-service #设置网关服务的名称。
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848} #Nacos 服务发现的地址。 ${NACOS_SERVER_ADDR}: 使用环境变量配置 Nacos 服务器地址,方便在不同环境中切换。
      config:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848} #Nacos 配置管理的地址。
        file-extension: yaml

可以将Spring Cloud Gateway的配置放在Nacos配置中心,这样可以实现配置的集中管理和动态更新。在Nacos中创建配置:
【1】登录Nacos控制台。
【2】创建一个新的配置,例如gateway-service.yaml
【3】在配置文件中添加路由规则,例如:

spring:
  cloud:
    gateway:
      routes:
        - id: example-service
          uri: lb://example-service
          predicates:
            - Path=/example/**
        - id: another-service
          uri: lb://another-service
          predicates:
            - Path=/another/**

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Visual Studio Installer Projects 打包 C# WinForms 程序的教程

前言 在开发完成一个 C# WinForms 程序后&#xff0c;打包成安装程序是发布和分发软件的重要步骤之一。通过使用 Visual Studio Installer Projects&#xff0c; 可以轻松创建一个 .exe 或 .msi 格式的安装包供用户安装。本文将详细介绍如何使用 Visual Studio Installer Proj…

网络资源模板--Android Studio 实现简易记事本App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--基于Android studio 实现的简易记事本App 二、项目测试环境 三、项目详情 首页 创建一个空的笔记本列表 mNotebookList。使用该列表和指定的布局资源 item_notebook 创建…

苹果最新论文:LLM只是复杂的模式匹配 而不是真正的逻辑推理

大语言模型真的可以推理吗&#xff1f;LLM 都是“参数匹配大师”&#xff1f;苹果研究员质疑 LLM 推理能力&#xff0c;称其“不堪一击”&#xff01;苹果的研究员 Mehrdad Farajtabar 等人最近发表了一篇论文&#xff0c;对大型语言模型 &#xff08;LLM&#xff09; 的推理能…

2.实现第一个three.js程序

实现第一个three.js程序 1.目标效果 注意一个版本问题&#xff1a;three.js版本并不稳定&#xff0c;几乎每个月都会更新一个小版本&#xff0c;尽可能使用固定版本进行开发&#xff0c;事实上我们入门的话&#xff0c;只掌握其中一个版本即可&#xff0c;如果使用新版本&…

文件与fd

访问文件前&#xff0c;为什么必须要打开文件&#xff1f;/ 打开文件的实质 访问文件前&#xff0c;都必须先打开它&#xff0c; 如fopen 访问文件时&#xff0c;是进程在访问 所以文件必须加载到内存中 我们要访问文件时&#xff0c;一定要通过内存访问 文件没有被打开时&am…

多线程(三):线程等待获取线程引用线程休眠线程状态

目录 1、等待一个线程&#xff1a;join 1.1 join() 1.2 join(long millis)——"超时时间" 1.3 join(long millis&#xff0c;int nanos) 2、获取当前线程的引用&#xff1a;currentThread 3、休眠当前进程&#xff1a;sleep 3.1 实际休眠时间 3.2 sleep的特殊…

SQLI LABS | SQLI LABS 靶场初识

关注这个靶场的其它相关笔记&#xff1a;SQLI LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;SQLI LABS 靶场简介 SQLi-Labs 靶场是一个专门用于学习和测试 SQL 注入漏洞的开源靶场&#xff0c;该靶场提供了多个具有不同漏洞类型和难度级别的 Web 应用程序的环境。这些应用…

C++ | Leetcode C++题解之第477题汉明距离总和

题目&#xff1a; 题解&#xff1a; class Solution { public:int totalHammingDistance(vector<int> &nums) {int ans 0, n nums.size();for (int i 0; i < 30; i) {int c 0;for (int val : nums) {c (val >> i) & 1;}ans c * (n - c);}return …

matlab 相关

1、xcorr 本质上是两个函数做内积运算 相关算法有两种&#xff1a; 在Matlab上既可以 1.用自带的xcorr函数计算互相关&#xff0c;2.通过在频域上乘以共轭复频谱来计算互相关&#xff1b; 网友验证程序 clc;clear;close all; % s1,s2为样例数据 s1 [-0.00430297851562500;-…

[C++ 核心编程]笔记 4.1.2 struct和class的区别

4.1.2 struct和class的区别 在C中 struct和class唯一的区别就在于 默认的访问权限不同 区别: struct 默认权限为公共class 默认权限为私有 #include<iostream> using namespace std;class C1 {int m_A;//默认私有 }; struct C2 {int m_A;//默认共有 };int main() {//s…

【3dgs】Gaussian-SLAM发展关键历程梳理

【3dgs】Gaussian-SLAM 0. 写在前面1. 3D Splatting与SLAM流程2. Splatting SLAM&#xff1a;单目/RGB-D(2024年新作&#xff09;2.1 相机跟踪精度2.2 新视图渲染性能2.3 消融实验 3. Gaussian-SLAM&#xff08;Photo-SLAM&#xff09; Photo-SLAM技术原理详解 ORBSLAM3dGS&am…

超GPT3.5性能,无限长文本,超强RAG三件套,MiniCPM3-4B模型分享

MiniCPM3-4B是由面壁智能与清华大学自然语言处理实验室合作开发的一款高性能端侧AI模型&#xff0c;它是MiniCPM系列的第三代产品&#xff0c;具有4亿参数量。 MiniCPM3-4B模型在性能上超过了Phi-3.5-mini-Instruct和GPT-3.5-Turbo-0125&#xff0c;并且与多款70亿至90亿参数的…

CentOS快速配置网络Docker快速部署

CentOS快速配置网络&&Docker快速部署 CentOS裸机Docker部署1.联通外网2.配置CentOS镜像源3.安装Docker4.启动Docker5.CentOS7安装DockerCompose Bug合集ERROR [internal] load metadata for docker.io/library/java:8-alpineError: Could not find or load main class …

动力电池SOC估算方法

1. SOC介绍 电池的荷电状态SOC反映电池的剩余容量状况&#xff0c;即在一定的放电倍率下&#xff0c;当前电池的剩余容量与总容量的比值。 为了充分发挥电池性能和提高安全性&#xff0c;需要准确估算电池SOC。动力电池在使用过程中表现的高度非线性提高了SOC估算的难度&#…

(04)python-opencv图像处理——图像阈值、平滑图像、形态转换、图像梯度

目录 前言 一、图像阈值 1.1 简单的阈值法 1.2 自适应阈值 二、平滑图像 2.1 二维卷积(图像滤波) 2.2 图像模糊 2.2.1均值模糊 2.2.2高斯模糊 2.2.3 中值滤波 2.2.4 双边滤波 三、形态转换 1、腐蚀 2、膨胀 3、开运算 4、闭运算 四、图像梯度 Sobel 和 Scharr …

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化 零、前言 最近换了Linux系统&#xff0c;但是写教程做PPT的时候还是得用到绘图软件&#xff0c;上网一查&#xff0c;总结对比之后发现Krita比较好用&#xff0c;故此讲解一下如何安装和汉化Krita。 壹、安装 安装很简…

探索 Python 装饰器的新境界:wrapt 库的神秘力量

文章目录 探索 Python 装饰器的新境界&#xff1a;wrapt 库的神秘力量背景&#xff1a;为何选择 wrapt&#xff1f;wrapt 是什么&#xff1f;如何安装 wrapt&#xff1f;简单的 wrapt 库函数使用方法创建简单装饰器保持元信息处理参数传递 场景应用&#xff1a;wrapt 的实际用例…

php 生成随机数

记录&#xff1a;随机数抽奖 要求&#xff1a;每次生成3个 1 - 10 之间可重复&#xff08;或不可重复&#xff09;的随机数&#xff0c;10次为一轮&#xff0c;每轮要求数字5出现6次、数字4出现3次、…。 提炼需求&#xff1a; 1&#xff0c;可设置最小数、最大数、每次抽奖生…

CentOS7.9 下安装 Docker

第一步&#xff1a; sudo yum install -y yum-utils \ > device-mapper-persistent-data \ > lvm2 第二步&#xff1a;安装 sudo wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum -y install…

C语言刷题 LeetCode 删除单链表的重复节点 双指针法

题目要求 链表结构&#xff1a;题目中提到的是未排序的链表&#xff0c;链表是由一系列节点组成的&#xff0c;每个节点包含一个值&#xff08;数据&#xff09;和一个指向下一个节点的指针。去重&#xff1a;我们需要遍历链表&#xff0c;删除所有重复的节点&#xff0c;只保…