当前位置: 首页 > news >正文

springCloudGateway+nacos自定义负载均衡-通过IP隔离开发环境

先说一下想法,小公司开发项目,参考若依框架使用的spring-cloud-starter-gatewayspring-cloud-starter-alibaba-nacos, 用到了nacos的配置中心和注册中心,有多个模块(每个模块都是一个服务)。
想本地开发,且和其他同事不互相影响的情况下,有同事建议 本地部署 完整的环境(nacos、需要的模块、前端等),除了 数据库和redis共用。这种方案也能用,就是占用自己电脑资源太多了(ps 自己电脑摸鱼的硬件资源就少了)。
我通过看nacos相关的教程,发现可以通过 配置中心 和 注册中心 的namespace命名不同进行隔离,这样可以不用部署nacos服务。
继续分析发现,网关模块 默认的 负载均衡 ReactorServiceInstanceLoadBalancer 的实现 RoundRobinLoadBalancer 是通过轮询 访问服务的,可以 改造 负载均衡 来实现 本地开发环境最小化(只允许开发的服务模块),其他服务用 测试环境提供。
当前改造只适用请求通过网关,如果模块服务之间 直连调用,就需要在每个模块增加类似处理。暂不考虑

(ps 本人小白一枚springCloudGateway使用的少)本文章限定在这个项目里面,如果自己项目环境不一样,不保证能拿来直接用。

实现截图-不想看原理的先食用结果

在这里插入图片描述
在这里插入图片描述

参考

本来想通过搜索和AI找到解决方案,结果代码抄下来每一个能实现的。要么没有引用的类,要么无法实现自己的想法。所以就不贴了。
每个项目的依赖和版本情况都不一样,最好的方法是 找到框架的核心实现类,在跟踪调试和找到引用与初始化的地方。想办法自定义实现注入-这种方式最好,毕竟都是框架给留的口子。实在不行,就只能通过魔改或劫持的方式实现了。

原理图-设想

改造前请求流程

在这里插入图片描述
这种情况,适用于 非开发环境,简单高效

改造后请求流程

在这里插入图片描述
改造后的,适用于 本地开发,通过网关只请求自己的服务,或者自己没有只请求测试环境(简单的测试环境所有服务一台机器上)。尽最大可能不影响 其他同事开发。
图中没有画 锁定请求服务IP的情况(这块代码没测试)。

项目框架主要依赖

参考时候请注意 依赖包版本是否差距过大

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>3.1.4</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2021.0.4.0</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2021.0.4.0</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId><version>2021.0.4.0</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId><version>2021.0.4.0</version>
</dependency>
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.8.5</version>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId><version>3.1.5</version>
</dependency>

自定义实现

自定义负载均衡类

根据 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer 轮询策略 改造,这块代码倒还好实现。TestLocalLockIpLoadBalancer直接注入spring中,ObjectProvider<ServiceInstanceListSupplier> 获取不到服务列表,看了spring实现,要完全替换需要注入好几个类,有点麻烦。最后改成运行时替换。

自定义路由负载均衡,负载均衡策略:

  • 先根据请求IP查询IP相同的服务
  • 再找和网关IP相同的服务
  • 最后轮询其他IP服务
package *.*.*.config;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;/*** 自定义路由负载均衡->初始化,负载均衡策略: 先找请求IP查询IP相同的服务、再找和网关IP相同的服务、最后轮询其他IP服务** @see  org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer* @author z* @date 2025/1/10*/
public class TestLocalLockIpLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Logger log = LoggerFactory.getLogger(TestLocalLockIpLoadBalancer.class);final AtomicInteger position;final String serviceId;ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;private String localIp;public TestLocalLockIpLoadBalancer setLocalIp(String localIp) { this.localIp = localIp; return this; }public TestLocalLockIpLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> provide, String serviceId) {this(provide, serviceId, new Random().nextInt(1000));}public TestLocalLockIpLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> provide, String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = provide;this.position = new AtomicInteger(seedPosition);System.out.println("自定义路由负载均衡->初始化,负载均衡策略: 先找请求IP查询IP相同的服务、再找和网关IP相同的服务、最后轮询其他IP服务");}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {System.out.println("自定义路由负载均衡---->");ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);String ip = getIpAddr(request);return supplier.get(request).next().map(services -> processInstanceResponse(supplier, services, ip));}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> services, String reqIp) {Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(services, reqIp);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, String reqIp) {System.out.println("自定义路由负载均衡--- 服务数量:" + instances.size());if (instances.isEmpty()) {log.warn("没有可供服务的服务器: {}, 请求: {}" , serviceId, reqIp);return new EmptyResponse();}if (instances.size() == 1) {System.out.println("自定义路由负载均衡--- 服务只有一个: " + instances.get(0).getHost());return new DefaultResponse(instances.get(0));}if( null != reqIp && !reqIp.isEmpty()){for (ServiceInstance instance : instances) {if(instance.getHost().equals(reqIp)){System.out.println("自定义路由负载均衡--- 策略1: 自己处理 " + reqIp);return new DefaultResponse(instance);}}}for (ServiceInstance instance : instances) {if(instance.getHost().equals(localIp)){System.out.println("自定义路由负载均衡--- 策略2: 测试处理 " + localIp);return new DefaultResponse(instance);}}int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());System.out.println("自定义路由负载均衡--- 策略3: 默认轮询:" + instance.getHost());return new DefaultResponse(instance);}/** 获取客户端IP */private String getIpAddr(Request request) {DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();RequestData clientRequest = (RequestData) requestContext.getClientRequest();HttpHeaders headers = clientRequest.getHeaders();if(headers.containsKey("sourceIp")){return headers.getFirst("sourceIp");}return null;}
}

自定义配置注入-偷天换日

最麻烦的事,是TestLocalLockIpLoadBalancer注入spring后,ObjectProvider<ServiceInstanceListSupplier> 获取不到服务列表。当前操作是运行时替换。在不考虑性能的情况下,这个只要 重写LoadBalancerClientFactory工厂类,改造获取 负载均衡 实例的方法就行。

注入的全局拦截器LockIpGlobalFilter,主要是获取 当前请求的 来源IP 放到请求头里面,因为 负载均衡的Request好像是获取不到IP信息。
自定义工厂类LockIpLoadBalancerClientFactory实现,重写获取负载均衡方法(替换为自定义类)

package *.*.*.config;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** 路由过滤 优先 请求ip对应的服务实例** @see org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter* @author z.* @date 2025/1/10*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.lockIp", havingValue = "true")
public class TestLocalLockIpConfig {private static final Logger log = LoggerFactory.getLogger(TestLocalLockIpConfig.class);@Autowiredprivate InetUtils inetUtils;@Beanpublic LockIpGlobalFilter lockIpGlobalFilter(){System.out.println(">>>>>>>>>自定义全局过滤器-注册");return new LockIpGlobalFilter();}@Beanpublic LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties,ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {System.out.println(">>>>>>>>>自定义路由工厂-注册");LockIpLoadBalancerClientFactory clientFactory = new LockIpLoadBalancerClientFactory(properties);clientFactory.setConfigurations(configurations.getIfAvailable(Collections::emptyList));return clientFactory.setLocalIp(inetUtils.findFirstNonLoopbackHostInfo().getIpAddress());}public static class LockIpGlobalFilter implements GlobalFilter, Ordered{@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();System.out.println("------->自定义全局过滤器: " + request.getURI().getPath());String ip = remoteIp(request);if(ip != null && !UNKNOWN.equals(ip)){ServerHttpRequest.Builder mutate = request.mutate();mutate.header("sourceIp", ip);}System.out.println("自定义全局过滤器-获取IP: " + ip);return chain.filter(exchange);}@Overridepublic int getOrder() { return 0; }private static final String UNKNOWN = "unknown";public String remoteIp(ServerHttpRequest request){HttpHeaders headers = request.getHeaders();String def = null != request.getRemoteAddress() ? request.getRemoteAddress().getHostString() : UNKNOWN;String[] keys = {"x-forwarded-for","Proxy-Client-IP","X-Forwarded-For","WL-Proxy-Client-IP","X-Real-IP"};String ip = Arrays.stream(keys).map(headers::getFirst).filter(v-> v != null && !v.isEmpty() && !UNKNOWN.equalsIgnoreCase(v)).findFirst().orElse(def);//本机请求 负载均衡 第二个策略 优先使用本机服务if( "0:0:0:0:0:0:0:1".equals(ip) || "127.0.0.1".equals(ip) ){ return null; }//从多级反向代理中获得第一个非unknown IP地址if (ip.indexOf(",") <= 0) {return Arrays.stream(ip.trim().split(",")).filter(subIp -> !UNKNOWN.equalsIgnoreCase(subIp)).findFirst().orElse(ip);}return ip;}}public static class LockIpLoadBalancerClientFactory extends LoadBalancerClientFactory{/** 本网关服务 的内网IP */private String localIp;public LockIpLoadBalancerClientFactory setLocalIp(String localIp) { this.localIp = localIp; return this; }public LockIpLoadBalancerClientFactory(LoadBalancerClientsProperties properties){ super(properties); }@Overridepublic <T> T getInstance(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);try {T t = context.getBean(type);if(t instanceof TestLocalLockIpLoadBalancer){ return t; }if(t instanceof RoundRobinLoadBalancer){System.out.println("自定义路由工厂-路由负载均衡策略: 默认轮询 "+t);String[] beanName = context.getBeanNamesForType(type);System.out.println("自定义路由工厂-路由负载均衡策略: 默认 删除第一个: "+ Arrays.toString(beanName));context.removeBeanDefinition(beanName[0]);System.out.println("自定义路由工厂-路由负载均衡策略: 自定义 创建");TestLocalLockIpLoadBalancer balancer = new TestLocalLockIpLoadBalancer(this.getLazyProvider(name, ServiceInstanceListSupplier.class), name).setLocalIp(localIp);System.out.println("自定义路由工厂-路由负载均衡策略: 自定义 注入");context.getBeanFactory().registerSingleton(beanName[0],balancer);t = context.getBean(type);System.out.println("自定义路由工厂-路由负载均衡策略: 注入后获取 " + t);}return t;} catch (Exception e) {log.error("自定义路由工厂", e);return null;}}}
}

配置文件-开关

通过配置,控制是否开启 自定义负载均衡

spring:cloud:lockIp: true

Spring网关源码分析

通过搜索和咨询AI,找到springCloudGateway处理服务负载均衡的拦截器org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter,核心代码是

	private final LoadBalancerClientFactory clientFactory;private final GatewayLoadBalancerProperties properties;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);//判断是不是注册中心的服务地址if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {return chain.filter(exchange);}//其他代码略...URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);//微服务模块IDString serviceId = requestUri.getHost();//存活的服务列表Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),RequestDataContext.class, ResponseData.class, ServiceInstance.class);//创建负载均衡函数choose入参的RequestDefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(new RequestData(exchange.getRequest()), getHint(serviceId)));//通过 负载均衡客户端工厂 获取 负载均衡配置LoadBalancerProperties loadBalancerProperties = clientFactory.getProperties(serviceId);return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {//通过负载均衡获取到服务后的处理代码略...}).then(chain.filter(exchange)).doOnError(/*异常处理略*/).doOnSuccess(/*成功处理略*/);}private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {//本次核心代码,通过 负载均衡客户端工厂 获取 负载均衡ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(serviceId,ReactorServiceInstanceLoadBalancer.class);if (loadBalancer == null) {throw new NotFoundException("No loadbalancer available for " + serviceId);}supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));return loadBalancer.choose(lbRequest);}

拦截器获取 负载均衡ReactorLoadBalancer,是通过LoadBalancerClientFactory类的函数getInstance,这个类是 ReactiveLoadBalancerClientFilter构造入参。找到拦截器初始化的代码org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration,是spring的自动注入配置类

	@Bean@ConditionalOnBean(LoadBalancerClientFactory.class)@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)@ConditionalOnEnabledGlobalFilterpublic ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory factory,GatewayLoadBalancerProperties properties) {return new ReactiveLoadBalancerClientFilter(factory, properties);}

那就找到注入LoadBalancerClientFactory类的代码。也没看到初始化负载均衡ReactorLoadBalancer<ServiceInstance>的代码

	@ConditionalOnMissingBean@Beanpublic LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties) {LoadBalancerClientFactory factory = new LoadBalancerClientFactory(properties);factory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));return factory;}

下面就看到,挠头的配置类初始化方式

进入LoadBalancerClientFactory的构造函数,发现LoadBalancerClientConfiguration负载均衡客户端配置类。

	public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);this.properties = properties;}

LoadBalancerClientConfiguration负载均衡客户端配置类,请留意这里,这个配置类不是项目启动自动注入。也就是说,参考reactorServiceInstanceLoadBalancer函数自定义注入spring是不对的。就因为这个情况,卡了好几天,因为它是运行时初始化的。

//当设置为 false 时,Spring 不会为配置类生成代理对象。也就是不会自动注入配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment ev,LoadBalancerClientFactory factory) {//自定义ReactorLoadBalancer注入spring,name获取为null。实际这个应该是serviceId//获取配置参数 loadbalancer.client.nameString name = ev.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer( factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
//其他略
}

换个方向,分析函数getInstance,实现是在org.springframework.cloud.context.named.NamedContextFactory类里。看到这里,就能发现有一个map字段contexts,存着每个 微服务模块 的上下文AnnotationConfigApplicationContext。只有请求到对应 微服务模块 才初始化对应的 负载均衡类。
代码做了简化处理

    public <T> T getInstance(String serviceId, Class<T> type) {return (T)this.getContext(serviceId).getBean(type);}protected AnnotationConfigApplicationContext getContext(String serviceId) {if (!this.contexts.containsKey(serviceId)) {this.contexts.put(serviceId, this.createContext(serviceId));}return (AnnotationConfigApplicationContext)this.contexts.get(serviceId);}protected AnnotationConfigApplicationContext createContext(String serviceId) {AnnotationConfigApplicationContext context /*上下文获取 代码略...*/;//上下文配置注册 代码略...//-----核心代码,注入配置类 LoadBalancerClientConfigurationcontext.register(new Class[]{PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});//-----核心代码,注入配置类 需要的 Environment 配置参数 loadbalancer.client.namecontext.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, serviceId)));//其他 代码略...//上下文 刷新并启动,类似初始化-只能调用一次context.refresh();return context;}

通过上面的代码分析,spring通过运行时注入负载均衡,可能是考虑 网关无法感知到有多少微服务模块。
如果自定义负载均衡,正常的启动注入 每个微服务模块的负载均衡器,代码开发是比较麻烦的,需要明确列出所有的微服务,还需要考虑让负载均衡获取到存活的 服务列表。
所以最后 通过 覆盖函数getInstance,替换为自定义 负载均衡,这种代码量最简单粗暴(在不考虑性能的情况下)

测试日志

>>>>>>>>>自定义全局过滤器-注册
>>>>>>>>>自定义路由工厂-注册------->自定义全局过滤器: /a/login
自定义全局过滤器-获取IP: null
自定义路由工厂-路由负载均衡策略: 默认轮询 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer@1fc654d2
自定义路由工厂-路由负载均衡策略: 默认 删除第一个: [reactorServiceInstanceLoadBalancer]
自定义路由工厂-路由负载均衡策略: 自定义 创建
自定义路由负载均衡->初始化,负载均衡策略: 先找请求IP查询IP相同的服务、再找和网关IP相同的服务、最后轮询其他IP服务
自定义路由工厂-路由负载均衡策略: 自定义 注入
自定义路由工厂-路由负载均衡策略: 注入后获取 *.*.*.config.TestLocalLockIpLoadBalancer@72e4d1fe
自定义路由负载均衡---->
自定义路由负载均衡--- 服务数量:1
自定义路由负载均衡--- 服务只有一个: 192.168.1.10------->自定义全局过滤器: /a/login
自定义全局过滤器-获取IP: null
自定义路由负载均衡---->
自定义路由负载均衡--- 服务数量:2
自定义路由负载均衡--- 策略2: 测试处理 192.168.1.17------->自定义全局过滤器: /a/login
自定义全局过滤器-获取IP: 192.168.1.10
自定义路由负载均衡---->
自定义路由负载均衡--- 服务数量:2
自定义路由负载均衡--- 策略1: 自己处理 192.168.1.10

测试截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

END

用的公司项目测试,抱歉内容做了脱敏处理。
仅供参考-请结合实际情况使用

相关文章:

springCloudGateway+nacos自定义负载均衡-通过IP隔离开发环境

先说一下想法&#xff0c;小公司开发项目&#xff0c;参考若依框架使用的spring-cloud-starter-gateway和spring-cloud-starter-alibaba-nacos, 用到了nacos的配置中心和注册中心&#xff0c;有多个模块&#xff08;每个模块都是一个服务&#xff09;。 想本地开发&#xff0c;…...

MyBatis-增删改查操作一些细节

目录 删除 新增 修改 查询 小结&#xff1a; 删除功能 需求&#xff1a;根据ID删除用户信息 SQL&#xff1a;delete from user where id 5; Mapper接口方法&#xff08;注意这里不是实现类&#xff09;&#xff1a; /*** 根据id删除*/ Delete("delete from user wher…...

windows 极速安装 Linux (Ubuntu)-- 无需虚拟机

1. 安装 WSL 和 Ubuntu 打开命令行&#xff0c;执行 WSL --install -d ubuntu若报错&#xff0c;则先执行 WSL --update2. 重启电脑 因安装了子系统&#xff0c;需重启电脑才生效 3. 配置 Ubuntu 的账号密码 打开 Ubuntu 的命令行 按提示&#xff0c;输入账号&#xff0c;密…...

【学习笔记】各种强化学习环境

0. 写在前面 0.1 强化学习综述/资料&#xff08;更新中&#xff09; 鹏程实验室&#xff1a; 中文报道&#xff1a;学术分享丨具身智能综述&#xff1a;鹏城实验室&#xff06;中大调研近400篇文献&#xff0c;英文原文&#xff1a;Aligning Cyber Space with Physical World…...

统计有序矩阵中的负数

统计有序矩阵中的负数 描述 给你一个 m * n 的矩阵 grid&#xff0c;矩阵中的元素无论是按行还是按列&#xff0c;都以非递增顺序排列。 请你统计并返回 grid 中 负数 的数目 示例 1&#xff1a; 输入&#xff1a;grid [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]…...

【已解决】git clone报错:Failed to connect to github.com port 443: Timed out

1.问题原因1 报错信息1&#xff1a; fatal: unable to access https://github.com/microsoft/xxx/: Failed to connect to github.com port 443: Timed out 报错信息2&#xff1a; fatal: unable to access https://github.com/xxx/xx/: OpenSSL SSL_read: Connection was …...

Android SystemUI——使用Dagger2加载组件(四)

SystemUI 是 Android 系统中的一个重要模块,负责绘制系统栏(如状态栏、导航栏)、锁屏、快捷设置等用户界面元素。由于其复杂性,良好的架构设计和依赖管理对于保持代码的可维护性和扩展性至关重要。这就是 Dagger2 在此发挥重要作用的地方。 一、Dagger2介绍 Dagger2 是一个…...

Lesson 109 A good idea

Lesson 109 A good idea 词汇 idea n. 主意&#xff0c;想法 复数&#xff1a;ideas 用法&#xff1a;口语&#xff1a;Good idea! 好主意&#xff01;       Big idea! 高见&#xff01;好主意&#xff01;       Great idea! 好主意       Bad idea! 坏主…...

网络安全-RSA非对称加密算法、数字签名

数字签名非常普遍&#xff1a; 了解数字签名前先了解一下SHA-1摘要&#xff0c;RSA非对称加密算法。然后再了解数字签名。 SHA-1 SHA-1&#xff08;secure hash Algorithm &#xff09;是一种 数据加密算法。该算法的思想是接收一段明文&#xff0c;然后以一种不可逆的方式将…...

自动化办公|xlwings简介

xlwings 是一个开源的 Python 库&#xff0c;旨在实现 Python 与 Microsoft Excel 的无缝集成。它允许用户使用 Python 脚本自动化 Excel 操作&#xff0c;读取和写入数据&#xff0c;执行宏&#xff0c;甚至调用 VBA 脚本。这使得数据分析、报告生成和其他与 Excel 相关的任务…...

C#使用OpenTK绘制3D可拖动旋转图形三棱锥

接上篇,绘制着色矩形 C#使用OpenTK绘制一个着色矩形-CSDN博客 上一篇安装OpenTK.GLControl后,这里可以直接拖动控件GLControl 我们会发现GLControl继承于UserControl //// 摘要:// OpenGL-aware WinForms control. The WinForms designer will always call the default//…...

【网络云SRE运维开发】2025第3周-每日【2025/01/14】小测-【第13章ospf路由协议】理论和实操

文章目录 选择题&#xff08;10道&#xff09;理论题&#xff08;5道&#xff09;实操题&#xff08;5道&#xff09; 【网络云SRE运维开发】2025第3周-每日【2025/01/14】小测-【第12章ospf路由协议】理论和实操 选择题&#xff08;10道&#xff09; 在OSPF协议中&#xff0c…...

计算机网络 (34)可靠传输的工作原理

前言 计算机网络可靠传输的工作原理主要依赖于一系列协议和机制&#xff0c;以确保数据在传输过程中能够准确无误地到达目的地。 一、基本概念 可靠传输指的是数据链路层的发送端发送什么&#xff0c;在接收端就收到什么&#xff0c;即保证数据的完整性、正确性和顺序性。由于网…...

提高互联网Web安全性:避免越权漏洞的技术方案

目录 一、越权漏洞概述 二、常见的越权漏洞类型 三、越权漏洞的影响 四、越权漏洞的技术解决方案 一、越权漏洞概述 越权&#xff08;Authorization Bypass&#xff09;类漏洞是指在系统中&#xff0c;攻击者通过绕过身份验证或访问控制&#xff0c;获取本不应访问的资源或…...

c语言 --- 字符串

创建字符串 1. 使用字符数组创建字符串 #include <stdio.h>int main() {char str[20] "Hello, world!";str[0] h; // 修改字符串的第一个字符printf("%s\n", str); // 输出&#xff1a;hello, world!return 0; }解释&#xff1a; 数组大小 20 表…...

Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(3)

1.zip/unzip指令 语法&#xff1a; zip 压缩⽂件.zip ⽬录或⽂件 功能&#xff1a;将⽬录或⽂件压缩成zip格式 常⽤选项: -r&#xff1a;递归处理&#xff0c;将指定⽬录下的 所有⽂件和⼦⽬录⼀并处理 example: 1.事前准备 建立以下文件与目录&#xff1a; 2.压缩test 目…...

Java中网络编程的学习

目录 网络编程概述 网络模型 网络通信三要素: IP 端口号 通信协议 IP地址&#xff08;Internet Protocol Address&#xff09; 端口号 网络通信协议 TCP 三次握手 四次挥手 UDP TCP编程 客户端Socket的工作过程包含以下四个基本的步骤&#xff1a; 服务器程序…...

微服务的CAP定理与数据一致性抉择

分布式系统中的CAP定理&#xff0c;包括一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;和分区容错性&#xff08;Partition Tolerance&#xff09;三个核心要素。 微服务是分布式系统的一种表现形式&#xff0c;以及用户对于系统是分…...

正则表达式 - 简介

正则表达式 - 简介 正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是一种用于处理字符串的强大工具&#xff0c;它允许我们按照特定的模式&#xff08;pattern&#xff09;来搜索、匹配、查找和替换文本。正则表达式广泛应用于各种编程语言和工具…...

MySQL:表的内外连接

目录 1.内连接 2.左外连接和右外连接 178. 分数排名 - 力扣&#xff08;LeetCode&#xff09; 1.内连接 内连接就是两张表做笛卡尔积&#xff0c;再加上一个筛选条件。 这两个sql语句是一样的。 2.左外连接和右外连接 左外连接就是左表必须是完全显示&#xff0c;即使筛选…...

65.在 Vue 3 中使用 OpenLayers 绘制带有箭头的线条

前言 在现代的前端开发中&#xff0c;地图已经成为许多项目的核心功能之一。OpenLayers 是一个强大的开源地图库&#xff0c;它提供了丰富的功能和高度的定制化支持。在本篇文章中&#xff0c;我将向大家展示如何在 Vue 3 中使用 OpenLayers 绘制带有箭头的线条。 我们将实现…...

关于编写测试用例的细枝末节

这里写目录标题 故障判别类-边界考虑示例1.0&#xff1a;若A&#xff1e;20.3且持续时间≥15ms时&#xff08;判故周期为1000Hz&#xff09;&#xff0c;输出B为1&#xff0c;否则输出B为0。 故障判别类-不可恢复测试示例1.1&#xff1a;若A&#xff1e;20.3且持续时间≥15ms时…...

【Vim Masterclass 笔记13】第 7 章:Vim 核心操作之——文本对象与宏操作 + S07L28:Vim 文本对象

文章目录 Section 7&#xff1a;Text Objects and MacrosS07L28 Text Objects1 文本对象的含义2 操作文本对象的基本语法3 操作光标所在的整个单词4 删除光标所在的整个句子5 操作光标所在的整个段落6 删除光标所在的中括号内的文本7 删除光标所在的小括号内的文本8 操作尖括号…...

(一)QSQLite3库简介

1、SQLite数据库 SQLite数据库&#xff0c;作为一个轻量级的关系型数据库管理系统&#xff0c;广泛应用于移动设备和桌面应用程序中。由于其简单易用、无需配置的特点&#xff0c;它为开发者提供了极大的便利。然而&#xff0c;正是由于其应用广泛&#xff0c;随着用户对于系统…...

新版 MacOS 无法从 /usr/local/lib 加载动态链接库的解决办法

自己编写的动态链接库在Unix规范下一般位于/usr/local/lib&#xff0c;在2023年及之前的MacOS版本中&#xff0c;直接将动态库安装到该位置即可在程序运行时加载&#xff0c;可是升级MacOS版本后&#xff0c;ld就报错。 错误现象 运行程序&#xff0c;报错 dyld[6376]: Libra…...

PanWeidb-使用BenchmarkSQL对磐维数据库进行压测

本文提供PanweiDb使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL,一个JDBC基准测试工具,内嵌了TPC-C测试脚本,支持很多数据库,如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统(OLTP系统)的规范,一般情况下我们也把这类系统称为业…...

git在本地创建新分支并将该分支推送到远程仓库

1. 创建本地分支 首先&#xff0c;创建并切换到一个新的本地分支&#xff1a; git checkout -b new-branch-name2. 推送本地分支到远程仓库 将新的本地分支推送到远程仓库&#xff0c;并在远程创建一个对应的新分支&#xff1a; git push origin new-branch-name3. 设置本地…...

Axure9笔记

快速入门 原型图种类 1.线框图 2.高保真图 3.简易需求文档&#xff08;PRD&#xff09; tips 按住shift可以等比缩放 旋转&#xff1a;ctrl按角角 矢量图素材&#xff1a; iconfont-阿里巴巴矢量图标库 复制svg图-->将svg图换为形状 截屏&#xff1a; Windows&…...

33_操作Redis分片集群

1.Redis分片集群读写 我们使用的redis-cli --cluster提供了很多操作集群的命令,可以通过下面方式查看。 [root@localhost ~]# redis-cli --cluster help 1.连接上7001节点后,尝试存储一组num=100和a=10的数据,语句如下所示。 [root@node1 cluster]# redis-cli -a 123456…...

llama.cpp 模型可视化工具 GGUF Visualizer

llama.cpp 模型可视化工具 GGUF Visualizer 1. GGUF Visualizer for VS Code (gguf-viz)1.1. Features1.2. Extension Settings References GGUF Visualizer https://marketplace.visualstudio.com/items?itemNameAgainstEntropy.gguf-viz 1. GGUF Visualizer for VS Code (g…...

MAC AndroidStudio模拟器无网络

先确认PC端是正常访问网络的&#xff1b; 模拟器端修改Wifi设置&#xff1a;设置 - 网络和互联网 - WALN设置 按照上图修改&#xff1b; IP设置&#xff1a;从DHCP修改为静态&#xff0c;IP地址&#xff1a;10.0.2.16 &#xff0c;网关&#xff1a;10.0.2.2 &#xff0c; DNS…...

如何添加合适的索引:MySql 数据库索引认知

写在前面 博文内容涉及 Mysql 数据库索引简单认知&#xff0c;包括SQL执行过程&#xff0c;数据库数据存储原理。如何通过索引加快数据查询原理简单介绍适合有一定SQL基础的开发运维小伙伴建立数据库索引认知&#xff0c;学会如何添加索引理解不足小伙伴帮忙指正 &#x1f603;…...

深度学习中的学习率调度器(scheduler)分析并作图查看各方法差异

文章目录 1. 指数衰减调度器&#xff08;Exponential Decay Scheduler&#xff09;工作原理适用场景实现示例 2. 余弦退火调度器&#xff08;Cosine Annealing Scheduler&#xff09;工作原理适用场景实现示例 3. 步长衰减调度器&#xff08;Step Decay Scheduler&#xff09;工…...

测试人员面试需要掌握的内容

测试人员面试需要掌握的内容 1、在公司的测试流程是什么&#xff1f; 产品经理确认本次版本的需求&#xff0c;召开需求评审会&#xff0c;进行估时排期&#xff0c;需求和时间都确定之后&#xff0c;UI出设计图&#xff0c;开发人员进行开发&#xff0c;测试人员编写测试用例…...

【C++】函数(下)

1、函数的常见样式 常见的函数样式有四种&#xff1a; &#xff08;1&#xff09;无参数无返回值 &#xff08;2&#xff09;有参数无返回值 &#xff08;3&#xff09;无参数有返回值 &#xff08;4&#xff09;有参数有返回值 &#xff08;1&#xff09;无参数无返回值 示例…...

dockerfile实现lnmp

dockerfile实现lnmp 自定义镜像实现整个架构 (基础镜像centos7) nginx cd /opt mkdir nginx mysql php vim Dockerfile docker network create --subnet172.111.0.0/16 mynetwork #创建自定义网段 docker run -itd --name nginx -p 80:80 --cpu-quota 20000 -m 512m -v /op…...

C语言进阶-2指针(一)

目录 1. 字符指针1.1 一般用法&#xff1a;字符指针指向单字符1.2 第二种用法&#xff0c;字符串首地址给指针变量1.3 习题,下面代码的输出结果是什么&#xff1f;为什么&#xff1f; 2. 指针数组2.1实例—— 字符指针数组2.2实例——整形指针数组2.3 例子&#xff0c;识别下下…...

JAVA:Spring Boot 集成 JWT 实现身份验证的技术指南

1、简述 在现代Web开发中&#xff0c;安全性尤为重要。为了确保用户的身份&#xff0c;JSON Web Token&#xff08;JWT&#xff09;作为一种轻量级且无状态的身份验证方案&#xff0c;广泛应用于微服务和分布式系统中。本篇博客将讲解如何在Spring Boot 中集成JWT实现身份验证…...

SpringBoot链接Kafka

一、SpringBoot生产者 &#xff08;1&#xff09;修改SpringBoot核心配置文件application.propeties, 添加生产者相关信息 # 连接 Kafka 集群 spring.kafka.bootstrap-servers192.168.134.47:9093# SASL_PLAINTEXT 和 SCRAM-SHA-512 认证配置 spring.kafka.properties.securi…...

《深度剖析算法优化:提升效率与精度的秘诀》

想象一下&#xff0c;你面前有一堆杂乱无章的数据&#xff0c;你需要从中找到特定的信息&#xff0c;或者按照一定的规则对这些数据进行排序。又或者&#xff0c;你要为一个物流公司规划最佳的配送路线&#xff0c;以降低成本和提高效率。这些问题看似复杂&#xff0c;但都可以…...

APP推荐:全新TV端来了,8K原画电视版

▌ 软件介绍 B站都不陌生吧&#xff0c;一个能追番、学习、娱乐的多元平台&#xff0c;之前也分享过几款第三方TV端&#xff0c;其中的BV最近更新了全新版本。 使用了全新的UI界面&#xff0c;由之前的顶部菜单栏改成了侧边布局&#xff0c;已解锁限制&…...

Spark vs Flink分布式数据处理框架的全面对比与应用场景解析

1. 引言 1.1 什么是分布式数据处理框架 随着数据量的快速增长&#xff0c;传统的单机处理方式已经无法满足现代数据处理需求。分布式数据处理框架应运而生&#xff0c;它通过将数据分片分布到多台服务器上并行处理&#xff0c;提高了任务的处理速度和效率。 分布式数据处理框…...

【Linux】正则表达式

正则表达式是一种可供Linux工具过滤文本的自定义模板&#xff0c;Linux工具&#xff08;如sed、gawk&#xff09;会在读取数据时使用正则表达式对数据进行模式匹配。 正则表达式使用元字符来描述数据流中的一个或多个字符。它是由正则表达式引擎实现的。正则表达式引擎是一种底…...

《银行保险机构数据安全管理办法》正式实施,分类分级、安全评估共筑安全防线

金融数据具有高价值和高敏感性&#xff0c;金融数据安全关乎国家安全和金融消费者权益密切相关。在当前数字化进程加速的背景下&#xff0c;数据合作频繁&#xff0c;安全风险也随之增加&#xff0c;给机构管理带来了新挑战。 为规范银行业保险业数据处理活动&#xff0c;保障数…...

excel仅复制可见单元格,仅复制筛选后内容

背景 我们经常需要将内容分给不同的人&#xff0c;做完后需要合并 遇到情况如下 那是因为直接选择了整列&#xff0c;当然不可以了。 下面提供几种方法&#xff0c;应该都可以 直接选中要复制区域然后复制&#xff0c;不要选中最上面的列alt;选中可见单元格正常复制&#xff…...

26_Redis RDB持久化

从这个模块开始带领大家来学习Redis分布式缓存的相关内容,主要学习目标见下: 数据丢失问题:实现Redis数据持久化(RDB和AOF)并发能力问题:搭建Redis主从集群,实现读写分离故障恢复问题:利用Redis哨兵模式,实现健康检测和自动恢复存储能力问题:搭建Redis分片集群,利用…...

Windows图形界面(GUI)-QT-C/C++ - QT控件创建管理初始化

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 控件创建 包含对应控件类型头文件 实例化控件类对象 控件设置 设置父控件 设置窗口标题 设置控件大小 设置控件坐标 设置文本颜色和背景颜色 控件排版 垂直布局 QVBoxLayout …...

Java+Maven+GDAL

下载已经编译好的压缩包&#xff0c;下载地址 解压 jar 包 release-1930-x64-dev.zip\release-1930-x64\bin\gdal\java 目录下 打成Maven依赖 mvn install:install-file -Dfilegdal-3.10.1.jar -DgroupIdorg.gdal -DartifactIdgdal -Dversion3.10.1 -Dpackagingjar -Dgener…...

基于Python机器学习、深度学习技术提升气象、海洋、水文领域实践应用-以ENSO预测为例讲解

1. 背景与目标 ENSO&#xff08;El Nio-Southern Oscillation&#xff09;是全球气候系统中最显著的年际变率现象之一&#xff0c;对全球气候、农业、渔业等有着深远的影响。准确预测ENSO事件的发生和发展对于减灾防灾具有重要意义。近年来&#xff0c;深度学习技术在气象领域…...

Java算法 数据结构 栈 队列 优先队列 比较器

目录 栈 Stack 性质 构造 方法 代码示例 队列 Queue 性质 构造 方法 代码示例 优先队列 PriorityQueue 性质 构造 方法 代码示例 比较器 1. Comparator 接口的方法 2. 常见的内置比较器 1. 自然排序比较器&#xff08;naturalOrder()&#xff09; 2. 逆序排…...