【Nacos】服務發布之優雅預熱上線方案

目錄

  • 一、背景
  • 二、注冊時機
    • 2.1、注冊機制
    • 2.2、分析源碼找到注冊時機
  • 三、注冊前心跳健康檢測
    • 3.1、方案實施
    • 3.2、源碼分析
    • 3.3、優化代碼
  • 四、流量權重配置
  • 五、總結
    • 5.1、整體完整流程:
    • 5.2、流程圖:
    • 5.1、優化方案完整代碼:

一、背景

有些面向廣大C端的微服務,類似商品服務,每日需要承擔十萬級的QPS每個節點可能會高達數千QPS的。每一秒的抖動都可能對大量用戶造成影響。因此在高頻產品迭代的前提下,平穩的進行服務發布和新老服務替換是一個必要的能力。

以商品服務為例子product-service,承載了商品的查詢與新增等功能,整體QPS 31k+單機qps 1.2k+,正常平均響應時間10ms以下

在這里插入圖片描述

在這里插入圖片描述

  • 優雅方案部署前狀態

可以看到部署優雅功能前,啟動階段會導致100ms+的響應抖動。這個說明啟動瞬間會有部分用戶體驗受到較大的影響,是值得研究優化的點

在這里插入圖片描述

  • 優雅方案部署后狀態

部署后很直觀可以看到,平均響應能保持正常的10ms以下

在這里插入圖片描述

二、注冊時機

2.1、注冊機制

商品微服務架構是基于行業內流行的Spring Cloud架構體系,因此存在注冊中心(nacos)的概念,專門維護所有可用服務節點的信息。所有服務可以在注冊中心進行注冊,并基于注冊中心提供的其他節點的信息進行相應的調用,這就是所謂的注冊機制。Nacos源碼詳細講解可以看《【Nacos】Nacos源碼保姆級解析》

2.2、分析源碼找到注冊時機

為了解決應用啟動時出現的慢調用問題,首要步驟是檢查注冊時機的合理性。是否存在服務尚未準備就緒,就被過早地部署到生產環境中,從而導致調用端無法接收到正常的響應?

在深入分析源代碼后,確認Nacos的注冊時機是依賴于Spring的生命周期管理機制。具體來說,它監聽的是WebServerInitializedEvent事件,即內置Tomcat服務器完全啟動的那一刻。因此,可以觀察到Nacos的心跳(beat)緊隨Tomcat的17000端口啟動之后進行注冊并完成。這表明,自Tomcat啟動并監聽17000端口后,Nacos就已經記錄了該服務器,并可以開始接收對應的請求。

相關日志記錄如下:

public void onApplicationEvent(WebServerInitializedEvent event) {bind(event);
}
## tomcat端口已啟動
[18:01:15.217] INFO  [TID: N/A] org.springframework.boot.web.embedded.tomcat.TomcatWebServer 220 start - Tomcat started on port(s): 17000 (http) with context path ''
## 增加本機nacos心跳
[18:01:15.331] INFO  [TID: N/A] com.alibaba.nacos.client.naming.beat.BeatReactor 81 addBeatInfo - [BEAT] adding beat: BeatInfo{port=17000, ip='127.0.0.1', weight=1.0, serviceName='DEFAULT_GROUP@@product-v1', cluster='DEFAULT', metadata={preserved.register.source=SPRING_CLOUD}, scheduled=false, period=5000, stopped=false} to beat map.
## 注冊本機服務進nacos
[18:01:15.333] INFO  [TID: N/A] com.alibaba.nacos.client.naming.net.NamingProxy 230 registerService - [REGISTER-SERVICE] public registering service DEFAULT_GROUP@@product-v1 with instance: Instance{instanceId='null', ip='127.0.0.1', port=17000, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='null', metadata={preserved.register.source=SPRING_CLOUD}}
## 本機在nacos注冊完成
[18:01:15.338] INFO  [TID: N/A] com.alibaba.cloud.nacos.registry.NacosServiceRegistry 75 register - nacos registry, DEFAULT_GROUP product-v1 127.0.0.1:17000 register finished

關鍵源碼如下:

在這里插入圖片描述

理論上,tomcat是在spring的context準備完后才正式啟動端口的,所以此時應該bean都已經完成了實例化

在這里插入圖片描述

但凡事都有特例,仔細查看源碼,會發現啟動時實例化的bean是有限制條件的,如下圖。

在這里插入圖片描述

/*** Return whether this bean is "abstract", i.e. not meant to be instantiated* itself but rather just serving as parent for concrete child bean definitions.*/
@Override
public boolean isAbstract() {return this.abstractFlag;
}/*** Return whether this a <b>Singleton</b>, with a single shared instance* returned from all calls.* @see #SCOPE_SINGLETON*/
@Override
public boolean isSingleton() {return SCOPE_SINGLETON.equals(this.scope) || SCOPE_DEFAULT.equals(this.scope);
}/*** Return whether this bean should be lazily initialized, i.e. not* eagerly instantiated on startup. Only applicable to a singleton bean.* @return whether to apply lazy-init semantics ({@code false} by default)*/
@Override
public boolean isLazyInit() {return (this.lazyInit != null && this.lazyInit.booleanValue());
}

很明顯,如果你的bean標記了@Lazy,那肯定不會在這里被實例化。還有另外一種重要的使用場景,@RefreshScope,這個注解廣泛應用于配合@Configuration動態刷新配置。debug了其觸發時機,如下圖是在spring的refreshContext的最后,afterRefresh生命周期之前,觸發的RefreshScope實例化,而此時context、tomcat都已完成,所以當用戶請求來讀取這些@Lazy或者@RefreshScope的bean時就會臨時進行實例化或者等待實例化完成,在大qps的場景下可能會造成卡頓,這也就是啟動時會瞬時卡頓的一個原因

在這里插入圖片描述

分析到這一步,其實已經大致能猜到原因,就是服務注冊時機不正確。而像lazy或者refreshScope這種常用注解可能在實際工作中確實可能需要使用,因此單純禁用并不是一個好辦法。

所以解法很簡單,就是修改nacos注冊的時間,放棄nacos自動的注冊時機,改成手動注冊,把注冊時機掌握在自己手上。目前選擇的做法如最下面的代碼塊,在spring runner(選擇這里是因為callRunnersafterRefresh之后)中異步起一個線程進行回調處理,其等待數秒之后,再手動進行注冊。

/*** 進行nacos手動注冊*/
private void doNacosRegister(){log.warn("nacos手動注冊流程開始");try {// 臨時獲取權限拿參數// 通過反射拿registration屬性。即使 registration 是私有字段(private),也可以通過反射獲取。// 公共屬性(public)才能get方法獲取Field declaredField = nacosAutoServiceRegistration.getClass().getDeclaredField("registration");// 設置 registration 字段為可訪問狀態。// 說明:// 如果 registration 字段是私有的(private),默認情況下無法通過反射直接訪問。// setAccessible(true) 可以繞過 Java 的訪問控制檢查,允許訪問私有字段。// 這是一個危險操作,因為它破壞了封裝性,應謹慎使用。declaredField.setAccessible(true);// 通過反射從 nacosAutoServiceRegistration 對象中獲取 registration 字段的值。// 將該值強制轉換為 NacosRegistration 類型。NacosRegistration nacosRegistration = (NacosRegistration) declaredField.get(nacosAutoServiceRegistration);// 將 registration 字段的訪問權限恢復為原始狀態(通常是不可訪問狀態)。// 說明:// 這是一個可選操作,目的是恢復字段的訪問控制,避免對其他代碼產生影響。// 在實際開發中,這一步通常可以省略,因為 setAccessible(true) 的作用范圍僅限于當前反射操作。declaredField.setAccessible(false);// 如果開啟了自動注冊 那么就直接返回if (nacosRegistration.isRegisterEnabled()) {log.warn("nacos已打開自動注冊,跳過手動注冊!");return;}// 手動注冊nacosRegistration.getNacosDiscoveryProperties().setRegisterEnabled(true);nacosAutoServiceRegistration.start();} catch (Exception e) {throw new RuntimeException(e);} finally {log.warn("nacos手動注冊流程結束");}
}

那就只有這一個卡點么?很明顯不是,而且純靠幾秒的延遲也無法保證所有必要的bean都預熱完成了,進一步探索。

三、注冊前心跳健康檢測

3.1、方案實施

包括mysqlredis在內的中間件在啟動后采用懶加載機制不會主動創建連接,這樣也有可能會造成卡頓。

繼續探究了一下啟動后可繼續優化的方向。發現更加合理的方向是在注冊流程中結合spring actuator健康檢查機制,這樣剛好可以和k8s集群監聽的spring actuator的liveness保持邏輯判斷的一致

spring actuator會在啟動時檢測包括mysql、redis等一系列中間件的連接狀態,確認待各項指標全部ok后。此時再進行nacos注冊可以解決該問題。

綜上所述,對nacos注冊進行了進一步的優化,使用健康檢查進行連接預熱,在注冊流程里融合了spring actuator的健康檢查機制,一方面可以確保實例完全可用,一方面可以解決中間件初次連接的問題。

例如心跳檢測失敗:

在這里插入圖片描述

其實就是ES注冊失敗

在這里插入圖片描述

心跳檢測成功:

在這里插入圖片描述

3.2、源碼分析

以mysql舉例,使用的Hikari連接池在datasource創建的時候采用的是懶加載模式,直到第一次調用getConnection才會真正和mysql進行連接。

在這里插入圖片描述

com.zaxxer.hikari.HikariDataSource#getConnection()

在這里插入圖片描述

spring actuator的健康檢查機制可以解決此類問題,針對所有的中間件,不管你是否有主動進行getConnection,它都會在檢查時主動getConnection

org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator#doHealthCheck

在這里插入圖片描述
在這里插入圖片描述

同樣的問題在redis的lettuce客戶端也有一樣的處理,不會主動創建連接直到首次調用。同樣可以依靠redis的健康檢查進行初次連接

org.springframework.boot.actuate.autoconfigure.health.HealthEndpointConfiguration.AdaptedReactiveHealthContributors#adapt(org.springframework.boot.actuate.health.ReactiveHealthIndicator)

在這里插入圖片描述

3.3、優化代碼

// 初次健康檢查,預熱
this.firstHealthCheck();// 異步健康檢查
CompletableFuture.supplyAsync(() -> {log.warn("異步監測健康狀態開始");Boolean isUp = false;// 等待5秒才注冊try {for (int i = 1; i <= CHECK_HEALTH_NACOS_REGISTER_MAX_TIMES; i++) {isUp = this.isUpStatus();log.warn("第{}次異步健康檢測:{}", i, isUp);if (isUp){// 如果已啟動,注冊并中斷循環this.doNacosRegister();break;}Thread.sleep(5000); // 模擬耗時操作}} catch (InterruptedException e) {throw new RuntimeException(e);}return isUp;
}).thenAccept(result -> {if (result) {log.warn("異步監測健康狀態結束");} else {System.exit(99);log.error("異步監測健康狀態一直失敗,請檢查!");}
});

日志:

[14:52:57.978] WARN  [restartedMainraceId] com.dev.common.config.register.NacosDelayRegisterRunner 46 run - ---開始執行應用程序已啟動,執行runner邏輯---
[14:52:57.979] WARN  [restartedMainraceId] com.dev.common.config.register.NacosDelayRegisterRunner 175 firstHealthCheck - 開始進行初次預熱健康檢查
[14:52:59.578] WARN  [restartedMainraceId] com.dev.common.config.register.NacosDelayRegisterRunner 178 firstHealthCheck - 初次預熱健康檢查完成:OUT_OF_SERVICE
[14:52:59.579] WARN  [ForkJoinPool.commonPool-worker-9raceId] com.dev.common.config.register.NacosDelayRegisterRunner 76 lambda$handleCommandLineArguments$0 - 異步監測健康狀態開始
[14:52:59.653] WARN  [ForkJoinPool.commonPool-worker-9raceId] com.dev.common.config.register.NacosDelayRegisterRunner 83 lambda$handleCommandLineArguments$0 - 第1次異步健康檢測:false
[14:53:04.717] WARN  [ForkJoinPool.commonPool-worker-9raceId] com.dev.common.config.register.NacosDelayRegisterRunner 83 lambda$handleCommandLineArguments$0 - 第2次異步健康檢測:true
[14:53:04.717] WARN  [ForkJoinPool.commonPool-worker-9raceId] com.dev.common.config.register.NacosDelayRegisterRunner 122 doNacosRegister - nacos手動注冊流程開始

如上述代碼和日志,在注冊前進行一次健康檢查,然后起異步線程進行定時異步檢查健康狀態。可以看到哪怕是到了spring runner階段,health check仍然處于不可用狀態,直到第二次異步健康檢測才變更為可用,此時再進行nacos手動注冊最為合適

四、流量權重配置

至此已經基本解決優雅上線的問題,但出于精益求精的態度,并且針對C端高請求量的場景特點(低流量業務選做)。重新審視了一遍nacos的設計和架構,發現一個一直忽略的重要功能,權重。因此制定了進一步的優化方向:對用戶流量進行控流,逐步預熱上線。依托于nacos的權重weight機制,可以對用戶流量進行設置從0.01至1的權重配置,逐步放大用戶流量至全量,這樣做可以更好預熱服務,防止瞬間高請求量導致擴tomcat線程等操作的耗時。

新老版本nacos客戶端代碼編寫及load-balancer的權重適配

在開發過程中發現,雖然nacos服務端有設置weight的地方,但實際上客戶端的lb組件并沒有針對weight做判斷。查閱資料后及翻閱源碼后證實了這一點,在2.x版本之前都是簡單的輪詢機制在之后的版本也需要特別打開開關才會采用weight的權重配置

org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse

在這里插入圖片描述

因此針對新版本nacos,可以選擇打開NacosLoadBalancer的開關,針對老版本,參考了NacosLoadBalancer的做法,自行實現了LoadBalancer核心算法代碼如下,概括總結一下,各臺機器的weight形成各自的區間,依靠隨機數去命中區間,以此達到權重的效果。

/*** Random get one item with weight.** @return item*/
public T randomWithWeight() {Ref<T> ref = this.ref;double random = ThreadLocalRandom.current().nextDouble(0, 1);int index = Arrays.binarySearch(ref.weights, random);if (index < 0) {index = -index - 1;} else {return ref.items.get(index);}if (index < ref.weights.length) {if (random < ref.weights[index]) {return ref.items.get(index);}}if (ref.weights.length == 0) {throw new IllegalStateException("Cumulative Weight wrong , the array length is equal to 0.");}/* This should never happen, but it ensures we will return a correct* object in case there is some floating point inequality problem* wrt the cumulative probabilities. */return ref.items.get(ref.items.size() - 1);
}

注冊流程加入權重

可以通過以下代碼

// 注冊前實例化節點,并配置weight
Instance instance = new Instance();
instance.setIp(nacosDiscoveryProperties.getIp());
instance.setPort(nacosDiscoveryProperties.getPort());
instance.setWeight(0.5);

五、總結

5.1、整體完整流程:

  • 關閉自動注冊進行手動注冊,且應在spring runner階段

  • 注冊前進行spring actuator的health check

  • health check返回成功后進行0.01weight的小流量注冊

  • 逐步放大weight直至到1

  • 服務發布結束

5.2、流程圖:

在這里插入圖片描述

5.1、優化方案完整代碼:

GitHub地址,有幫助麻煩給個star

import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.actuate.health.HealthComponent;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.concurrent.CompletableFuture;import static org.springframework.boot.actuate.health.Status.UP;/*** @author hanson.huang* @version V1.0* @ClassName NacosDelayRegisterRunner* @Description nacos優雅預熱上線方案* @date 2025/3/14 14:22**/
@Component
@Slf4j
public class NacosDelayRegisterRunner implements ApplicationRunner {/*** 最大健康檢查次數*/private static final int CHECK_HEALTH_NACOS_REGISTER_MAX_TIMES = 10;@Resourceprivate NacosAutoServiceRegistration nacosAutoServiceRegistration;@Resourceprivate HealthEndpoint healthEndpoint;@Overridepublic void run(ApplicationArguments args) throws Exception {// 在這里編寫應用程序啟動后要執行的邏輯log.warn("---開始執行應用程序已啟動,執行runner邏輯---");// 你還可以獲取并處理命令行參數和應用程序參數handleCommandLineArguments(args);}/*** 讀取程序啟動參數并執行* @param args 啟動參數*/private void handleCommandLineArguments(ApplicationArguments args) {// 獲取并處理命令行參數System.out.println("---命令行參數:---");for (String arg : args.getSourceArgs()) {System.out.println(arg);}// 獲取并處理應用程序參數System.out.println("---應用程序參數:---");for (String name : args.getOptionNames()) {System.out.println(name + "=" + args.getOptionValues(name));}// 如果在啟動參數手動設置了不注冊nacos,就跳過手動注冊,為了開發環境和backendif ( !checkDisableNacos(args.getSourceArgs()) ) {// 初次健康檢查,預熱this.firstHealthCheck();// 異步健康檢查CompletableFuture.supplyAsync(() -> {log.warn("異步監測健康狀態開始");Boolean isUp = false;// 等待5秒才注冊try {for (int i = 1; i <= CHECK_HEALTH_NACOS_REGISTER_MAX_TIMES; i++) {isUp = this.isUpStatus();log.warn("第{}次異步健康檢測:{}", i, isUp);if (isUp){// 如果已啟動,注冊并中斷循環this.doNacosRegister();break;}Thread.sleep(5000); // 模擬耗時操作}} catch (InterruptedException e) {throw new RuntimeException(e);}return isUp;}).thenAccept(result -> {if (result) {log.warn("異步監測健康狀態結束");} else {System.exit(99);log.error("異步監測健康狀態一直失敗,請檢查!");}});}}private boolean checkDisableNacos(String[] args){System.out.println(System.getProperty("spring.cloud.nacos.discovery.register-enabled"));for (String arg : args) {if (StringUtils.contains(arg, "spring.cloud.nacos.discovery.register-enabled") && StringUtils.contains(arg,"false")|| StringUtils.equals(System.getProperty("spring.cloud.nacos.discovery.register-enabled"), "false")){return true;}}return false;}/*** 進行nacos手動注冊*/private void doNacosRegister(){log.warn("nacos手動注冊流程開始");try {// 臨時獲取權限拿參數// 通過反射拿registration屬性。即使 registration 是私有字段(private),也可以通過反射獲取。// 公共屬性(public)才能get方法獲取Field declaredField = nacosAutoServiceRegistration.getClass().getDeclaredField("registration");// 設置 registration 字段為可訪問狀態。// 說明:// 如果 registration 字段是私有的(private),默認情況下無法通過反射直接訪問。// setAccessible(true) 可以繞過 Java 的訪問控制檢查,允許訪問私有字段。// 這是一個危險操作,因為它破壞了封裝性,應謹慎使用。declaredField.setAccessible(true);// 通過反射從 nacosAutoServiceRegistration 對象中獲取 registration 字段的值。// 將該值強制轉換為 NacosRegistration 類型。NacosRegistration nacosRegistration = (NacosRegistration) declaredField.get(nacosAutoServiceRegistration);// 將 registration 字段的訪問權限恢復為原始狀態(通常是不可訪問狀態)。// 說明:// 這是一個可選操作,目的是恢復字段的訪問控制,避免對其他代碼產生影響。// 在實際開發中,這一步通常可以省略,因為 setAccessible(true) 的作用范圍僅限于當前反射操作。declaredField.setAccessible(false);// 如果開啟了自動注冊 那么就直接返回if (nacosRegistration.isRegisterEnabled()) {log.warn("nacos已打開自動注冊,跳過手動注冊!");return;}// 手動注冊nacosRegistration.getNacosDiscoveryProperties().setRegisterEnabled(true);nacosAutoServiceRegistration.start();} catch (Exception e) {throw new RuntimeException(e);} finally {log.warn("nacos手動注冊流程結束");}}/*** 進行初次健康檢查*/private void firstHealthCheck(){log.warn("開始進行初次預熱健康檢查");// 進行初次健康檢查HealthComponent endpoint = healthEndpoint.health();log.warn("初次預熱健康檢查完成:" + endpoint.getStatus());}/*** 是否已啟動* @return 是/否*/private Boolean isUpStatus(){return UP.equals( healthEndpoint.health().getStatus() );}
}

其他注意事項:

  • healthcheck必須確保沒有廢棄中間件的引入,以免healthcheck一直不過

  • 啟動時CPU資源一定要給足,否則啟動過慢

  • weight參數的使用要謹慎,要確保各種客戶端的兼容性

創作不易,不妨點贊、收藏、關注支持一下,各位的支持就是我創作的最大動力??

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/72238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/72238.shtml
英文地址,請注明出處:http://en.pswp.cn/web/72238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

接口自動化腳本優化的多個關鍵維度及具體方法總結

以下是優化接口自動化腳本的多個關鍵維度及具體方法&#xff0c;幫助提升測試效率、可維護性和可靠性&#xff1a; 1. 測試用例設計優化 維度優化方法參數化使用數據驅動&#xff08;如CSV、Excel、JSON&#xff09;&#xff0c;分離測試數據與邏輯&#xff0c;減少重復代碼。…

AI驅動的數字供應鏈安全情報預警服務:云脈XSBOM

先發制人&#xff0c;精準預警數字供應鏈中的安全風險 Pre-emptive Strategy, Accurate Warning of Security Risks in Digital Supply Chain 云脈XSBOM數字供應鏈安全情報預警依托懸鏡安全團隊強大的供應鏈管理監測能力和AI安全大數據云端分析能力&#xff0c;對全球數字供應…

8051匯編--條件轉移指令

在8051匯編語言中&#xff0c;控制轉移指令用于改變程序的執行順序&#xff0c;主要包括無條件轉移、條件轉移和調用/返回指令。以下是對這些指令的總結&#xff1a; 一、無條件轉移指令 LJMP&#xff08;Long Jump&#xff09; ? 功能&#xff1a;長轉移&#xff0c;可以跳…

Kubernetes學習筆記-移除Nacos遷移至K8s

項目服務的配置管理和服務注冊發現由原先的Nacos全面遷移到Kubernetes上。 一、移除Nacos 移除Nacos組件依賴。 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <…

算法系列之回溯算法求解數獨及所有可能解

有沒有對數獨感興趣的朋友呢&#xff1f;數獨作為一款經典的邏輯游戲&#xff0c;其目標是在一個9x9的方格中填入數字1至9&#xff0c;確保每一行、每一列以及每一個3x3的子網格中都包含這些數字且不重復。盡管數獨的規則看似簡單&#xff0c;但編寫一個能夠自動求解數獨的程序…

C++ primer plus 類和對象上

目錄 前言 一 接口的設計 二 方法的設計和使用 三 構造函數 四 析構函數 五 析構函數和構造函數小結 總結 前言 前面已經描述了很多有關于類和對象的知識了&#xff0c;所以我們直接開始上手操作 一 接口的設計 首先我們要知道什么是接口 接口是一個共享框架&…

css模擬雷達掃描動畫

<div class"radar-scan"><div class"radar-container" /></div> 樣式&#xff1a; .radar-scan {background-image: linear-gradient(0deg,transparent 24%,rgba(32, 255, 77, 0.15) 25%,rgba(32, 255, 77, 0.15) 26%,transparent 27%,…

AdaLoRA 參數 配置:CAUSAL_LM“ 表示因果語言模型任務

AdaLoRA 參數 配置:CAUSAL_LM" 表示因果語言模型任務 config = AdaLoraConfig( init_r=16, # 增加 LoRA 矩陣的初始秩 lora_alpha=32, target_modules=[“q_proj”, “v_proj”], lora_dropout=0.1, bias=“none”, task_type=“CAUSAL_LM” ) 整體功能概述 AdaLoraCon…

C# 集合

集合 概述集合接口和類型列表(ArrayList, List)隊列(Queue)棧(Statck)鏈表(LinkedList)有序表(SortedList)字典Lookup類其他字典類 HashSet(不重復項的無序列表)位數組BitArrayBitVector32 性能 概述 數組和Array類。數組的大小是固定的。如果元素個數是動態的&#xff0c;就應…

WebSocket與MQTT協議深度對比:選擇合適的通信協議

在現代互聯網應用中&#xff0c;實時通信變得愈發重要。隨著物聯網&#xff08;IoT&#xff09;和實時數據流的普及&#xff0c;選擇合適的通信協議顯得尤為關鍵。WebSocket和MQTT是當前最為流行的兩種協議&#xff0c;它們各自有不同的應用場景、優缺點以及性能特點。在這篇文…

ELK(Elasticsearch、Logstash、Kbana)安裝及Spring應用

Elasticsearch安裝及Spring應用 一、引言二、基本概念1.索引&#xff08;Index&#xff09;2.類型&#xff08;Type&#xff09;3.文檔&#xff08;Document&#xff09;4.分片&#xff08;Shard&#xff09;5.副本&#xff08;Replica&#xff09; 二、ELK搭建1.創建掛載的文件…

MacOS 15.3.1 安裝 GPG 提示Error: unknown or unsupported macOS version: :dunno

目錄 1. 問題鎖定 2. 更新 Homebrew 3. 切換到新的 Homebrew 源 4. 安裝 GPG 5. 檢查 macOS 版本兼容性 6. 使用 MacPorts 或其他包管理器 7. 創建密鑰&#xff08;生成 GPG 簽名&#xff09; 往期推薦 1. 問題鎖定 通常是因為你的 Homebrew 版本較舊&#xff0c;或者你…

C++:類和對象(從底層編譯開始)詳解[前篇]

目錄 一.inline內聯的詳細介紹 &#xff08;1&#xff09;為什么在調用內聯函數時不需要建立棧幀&#xff1a; &#xff08;2&#xff09;為什么inline聲明和定義分離到兩個文件會產生鏈接錯誤&#xff0c;鏈接是什么&#xff0c;為什么沒有函數地址&#xff1a; 二.類&…

C++中,存儲持續性、作用域和鏈接性

在C++中,存儲持續性、作用域和鏈接性是變量和函數的重要屬性,它們共同決定了變量的生命周期、可見性以及跨文件訪問能力。以下是詳細的總結: 1. 存儲持續性(Storage Duration) 存儲持續性指變量在內存中的生命周期,分為四類: 自動存儲持續性(Automatic) 局部變量(函…

四種 No-SQL

在一個常規的互聯網服務中&#xff0c;讀取與寫入的比例大約是 100:1 到 1000:1。然而&#xff0c;從硬盤讀取時&#xff0c;數據庫連接操作耗時&#xff0c;99% 的時間花費在磁盤尋址上。 為了優化讀取性能&#xff0c;非規范化的設計通過添加冗余數據或分組數據來引入。下述…

【 Manus平替開源項目】

文章目錄 Manus平替開源項目1 OpenManus1.1 簡介1.2 安裝教程1.3 運行 2 OWL2.1 簡介2.2 安裝教程2.3 運行 3 OpenHands&#xff08;原OpenDevin&#xff09;3.1 簡介3.2 安裝教程和運行 Manus平替開源項目 1 OpenManus 1.1 簡介 開發團隊: MetaGPT 核心貢獻者&#xff08;5…

【Linux 服務之ollama 部署過慢問題】

特別慢的 curl -fsSL https://ollama.com/install.sh | sh參考 方法1 export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download" curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/downl…

療養院管理系統設計與實現(代碼+數據庫+LW)

摘 要 傳統辦法管理信息首先需要花費的時間比較多&#xff0c;其次數據出錯率比較高&#xff0c;而且對錯誤的數據進行更改也比較困難&#xff0c;最后&#xff0c;檢索數據費事費力。因此&#xff0c;在計算機上安裝療養院管理系統軟件來發揮其高效地信息處理的作用&#xf…

Web后端開發之Maven

Maven Mven是apache旗下的一個開源項目&#xff0c;用來管理和構建java項目的工具。 通過一小段描述信息來管理項目。 Maven的作用 1.依賴管理&#xff1a;方便快捷的管理項目依賴的資源&#xff08;jar包&#xff09;&#xff0c;避免版本沖突問題 以前用某個jar包需要下載…

在線招聘小程序:AI簡歷篩選與精準職位推薦服務

當AI算法遇上小程序開發:重新定義「人崗匹配」的智能招聘革命 一、傳統招聘困境:求職者與企業為何總在「錯過」? 在數字化浪潮下,企業HR日均需處理數百份簡歷,卻仍有60%的崗位因匹配效率低下而空置;求職者海投簡歷后,近八成用戶表示從未收到精準反饋。這種雙向資源錯配…