【源碼分析】Nacos實例注冊流程分析-事件驅動框架

【踩坑記錄】

本人下載的Nacos 服務端版本是2.3.2,在開始進行源碼編譯便遇到問題,下面是各個問題記錄

源碼大量爆紅

在最開始用Idea加載Maven項目的時候,發現項目中大量的代碼爆紅,提示其類或者包不存在,后來結果查閱資料發現,是Nacos在2.x的版本引入了Grpc通信方式,并且采用Protobuf作為序列化協議。
導致源碼中有一些類是采用其進行編寫的,我們需要對其進行編譯,才能生成對應的包和Class,大家如果在閱讀其他源碼時遇到這種情況,可以向這個方面去想一想。
在這里插入圖片描述
具體來說有圖中兩個模塊,需要編譯,但是直接在父項目進行Compile即可,編譯后重新刷新項目或者重新構建即可。

斷點失效?

在成功啟動Nacos服務的時候,是采用斷點調試方式啟動的
在這里插入圖片描述
同時也對其進行設置了單機模式
在這里插入圖片描述
最后也成功啟動了,就在我創建Demo項目后,進行啟動。
在這里插入圖片描述
控制臺也有了對應的服務(這里需要提示一下,Nacos的客戶端必須要有 name 這個配置,否則不會注冊

spring:application:name: nacos-democloud:nacos:discovery:server-addr: 127.0.0.1:8841ephemeral: true // 是否臨時注冊,默認為true

通過上一篇客戶端的文章,跨域知道,其底層進行注冊時是調用

 this.reqApi(UtilAndComs.nacosUrlInstance, params, "POST");

在這里插入圖片描述
可以知道,其接口地址是/nacos/v1/ns/instance,于是我興高采烈的去服務的打斷點,如下
在這里插入圖片描述
然后重新啟動客戶端,奇怪的是,并沒有在斷點處阻塞,而是直接注冊成功了,這我非常蒙,就在絞盡腦汁的時候,突然想到前面不是說了,引入了Grpc嗎,后面一搜,果然,2.x版本默認采用grpc進行交互。那這個坑到這就解決了。
對于Nacos 2.x版本,默認是通過gRPC協議進行通信的
在這里插入圖片描述
正確的入口在這。

實例注冊源碼分析

1.0 入口

客戶端調用入口,首先通過傳入的元信息,插件Service,然后根據操作類型,進入對應的函數;
在這里插入圖片描述

2.0注冊流程開始

private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta)throws NacosException {clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(),request.getInstance().getIp(), request.getInstance().getPort()));return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);}

2.1 注冊并發布事件

  @Overridepublic void registerInstance(Service service, Instance instance, String clientId) throws NacosException {NamingUtils.checkInstanceIsLegal(instance);// 通過單例模式 + map 獲取對應的服務(沒有就添加到對應的map中)Service singleton = ServiceManager.getInstance().getSingleton(service);if (!singleton.isEphemeral()) {throw new NacosRuntimeException(NacosException.INVALID_PARAM,String.format("Current service %s is persistent service, can't register ephemeral instance.",singleton.getGroupedServiceName()));}// 獲取當前連接ID,也是通過并發map 管理連接,注意不要長時間阻塞斷點,否則連接會斷開,導致拿不到Client client = clientManager.getClient(clientId);checkClientIsLegal(client, clientId);InstancePublishInfo instanceInfo = getPublishInfo(instance);client.addServiceInstance(singleton, instanceInfo);// 設置當前連接最新更新的時間client.setLastUpdatedTime();// 重新設置版本號,用于版本控制client.recalculateRevision();// 發布事件NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));}

3.0 NotifyCenter - 統一事件通知中心。


/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import static com.alibaba.nacos.api.exception.NacosException.SERVER_ERROR;
/*** Unified Event Notify Center.* 統一事件通知中心 - 實現了一個事件發布-訂閱框架,用于處理系統內的事件通知** @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>* @author zongtanghu*/
public class NotifyCenter {// 日志記錄器private static final Logger LOGGER = LoggerFactory.getLogger(NotifyCenter.class);// 環形緩沖區大小 - 用于普通事件發布器的隊列大小public static int ringBufferSize;// 共享緩沖區大小 - 用于慢速事件共享發布器的隊列大小public static int shareBufferSize;// 通知中心關閉狀態標志 - 使用原子布爾值確保線程安全private static final AtomicBoolean CLOSED = new AtomicBoolean(false);// 默認事件發布器工廠 - 用于創建不同類型的事件發布器private static final EventPublisherFactory DEFAULT_PUBLISHER_FACTORY;// NotifyCenter單例實例 - 通過靜態final保證單例private static final NotifyCenter INSTANCE = new NotifyCenter();// 共享發布器實例 - 用于處理所有SlowEvent慢速事件private DefaultSharePublisher sharePublisher;// 事件發布器類型 - 通過SPI加載或使用默認實現private static Class<? extends EventPublisher> clazz;/*** 發布器管理容器 - 存儲不同事件類型對應的發布器* key: 事件類的規范名稱* value: 對應的事件發布器*/private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);// 靜態初始化塊 - 在類首次加載時執行初始化static {// 從系統屬性讀取環形緩沖區大小,默認為16384// 對于寫入吞吐量高的應用,需要適當增加這個值String ringBufferSizeProperty = "nacos.core.notify.ring-buffer-size";ringBufferSize = Integer.getInteger(ringBufferSizeProperty, 16384);// 從系統屬性讀取共享緩沖區大小,默認為1024// 用于公共發布器的消息暫存隊列緩沖區大小String shareBufferSizeProperty = "nacos.core.notify.share-buffer-size";shareBufferSize = Integer.getInteger(shareBufferSizeProperty, 1024);// 通過SPI機制加載EventPublisher的實現類final Collection<EventPublisher> publishers = NacosServiceLoader.load(EventPublisher.class);Iterator<EventPublisher> iterator = publishers.iterator();// 如果找到自定義實現類,使用第一個;否則使用默認實現if (iterator.hasNext()) {clazz = iterator.next().getClass();} else {clazz = DefaultPublisher.class;}// 初始化默認發布器工廠 - 使用lambda表達式實現工廠接口DEFAULT_PUBLISHER_FACTORY = (cls, buffer) -> {try {// 創建發布器實例并初始化EventPublisher publisher = clazz.newInstance();publisher.init(cls, buffer);return publisher;} catch (Throwable ex) {LOGGER.error("Service class newInstance has error : ", ex);throw new NacosRuntimeException(SERVER_ERROR, ex);}};try {// 創建并初始化共享發布器實例 - 用于處理所有SlowEventINSTANCE.sharePublisher = new DefaultSharePublisher();INSTANCE.sharePublisher.init(SlowEvent.class, shareBufferSize);} catch (Throwable ex) {LOGGER.error("Service class newInstance has error : ", ex);}// 添加JVM關閉鉤子,確保系統關閉時能夠正確釋放資源ThreadUtils.addShutdownHook(NotifyCenter::shutdown);}/*** 獲取發布器映射表 - 僅用于測試** @return 發布器映射表*/@JustForTestpublic static Map<String, EventPublisher> getPublisherMap() {return INSTANCE.publisherMap;}/*** 根據事件類型獲取對應的發布器** @param topic 事件類型* @return 對應的事件發布器*/public static EventPublisher getPublisher(Class<? extends Event> topic) {// 如果是SlowEvent類型,使用共享發布器if (ClassUtils.isAssignableFrom(SlowEvent.class, topic)) {return INSTANCE.sharePublisher;}// 否則從映射表中獲取對應的發布器return INSTANCE.publisherMap.get(topic.getCanonicalName());}/*** 獲取共享發布器實例** @return 共享發布器實例*/public static EventPublisher getSharePublisher() {return INSTANCE.sharePublisher;}/*** 關閉通知中心及其包含的所有發布器實例* 使用CAS操作確保只執行一次*/public static void shutdown() {// 如果已經關閉,則直接返回,避免重復關閉if (!CLOSED.compareAndSet(false, true)) {return;}LOGGER.warn("[NotifyCenter] Start destroying Publisher");// 關閉所有普通發布器for (Map.Entry<String, EventPublisher> entry : INSTANCE.publisherMap.entrySet()) {try {EventPublisher eventPublisher = entry.getValue();eventPublisher.shutdown();} catch (Throwable e) {LOGGER.error("[EventPublisher] shutdown has error : ", e);}}// 關閉共享發布器try {INSTANCE.sharePublisher.shutdown();} catch (Throwable e) {LOGGER.error("[SharePublisher] shutdown has error : ", e);}LOGGER.warn("[NotifyCenter] Destruction of the end");}/*** 注冊訂閱者(使用默認發布器工廠)* 如果發布器不存在,會使用默認工廠創建一個新的發布器** @param consumer 訂閱者實例*/public static void registerSubscriber(final Subscriber consumer) {registerSubscriber(consumer, DEFAULT_PUBLISHER_FACTORY);}/*** 使用指定工廠注冊訂閱者* 如果發布器不存在,會使用指定工廠創建一個新的發布器** @param consumer 訂閱者實例* @param factory  發布器工廠*/public static void registerSubscriber(final Subscriber consumer, final EventPublisherFactory factory) {// 處理智能訂閱者 - 可以訂閱多種事件類型// 如果要監聽多個事件,需要分別進行處理// 基于子類的subscribeTypes方法返回的列表,可以注冊到發布器if (consumer instanceof SmartSubscriber) {for (Class<? extends Event> subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) {// 對于慢速事件,注冊到共享發布器if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);} else {// 對于普通事件,注冊到對應的發布器addSubscriber(consumer, subscribeType, factory);}}return;}// 處理普通訂閱者 - 只訂閱一種事件類型final Class<? extends Event> subscribeType = consumer.subscribeType();// 如果是慢速事件,注冊到共享發布器if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);return;}// 對于普通事件,注冊到對應的發布器addSubscriber(consumer, subscribeType, factory);}/*** 將訂閱者添加到發布器中* 如果發布器不存在,會先創建發布器** @param consumer      訂閱者實例* @param subscribeType 訂閱的事件類型* @param factory       發布器工廠*/private static void addSubscriber(final Subscriber consumer, Class<? extends Event> subscribeType,EventPublisherFactory factory) {// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(subscribeType);synchronized (NotifyCenter.class) {// 確保發布器存在,如果不存在則創建// 注釋說MapUtils.computeIfAbsent是不安全的方法,這里使用自定義的MapUtilMapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, subscribeType, ringBufferSize);}// 獲取發布器并添加訂閱者EventPublisher publisher = INSTANCE.publisherMap.get(topic);if (publisher instanceof ShardedEventPublisher) {// 如果是分片發布器,需要傳入訂閱類型((ShardedEventPublisher) publisher).addSubscriber(consumer, subscribeType);} else {// 普通發布器直接添加訂閱者publisher.addSubscriber(consumer);}}/*** 取消訂閱者的注冊** @param consumer 訂閱者實例* @throws NoSuchElementException 如果訂閱者沒有對應的發布器*/public static void deregisterSubscriber(final Subscriber consumer) {// 處理智能訂閱者 - 需要逐一取消多個事件類型的訂閱if (consumer instanceof SmartSubscriber) {for (Class<? extends Event> subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) {if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {// 從共享發布器中移除訂閱INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType);} else {// 從普通發布器中移除訂閱removeSubscriber(consumer, subscribeType);}}return;}// 處理普通訂閱者 - 只有一個訂閱類型final Class<? extends Event> subscribeType = consumer.subscribeType();if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {// 從共享發布器中移除訂閱INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType);return;}// 從普通發布器中移除訂閱,如果移除失敗則拋出異常if (removeSubscriber(consumer, subscribeType)) {return;}throw new NoSuchElementException("The subscriber has no event publisher");}/*** 從發布器中移除訂閱者** @param consumer      訂閱者實例* @param subscribeType 訂閱的事件類型* @return 移除是否成功*/private static boolean removeSubscriber(final Subscriber consumer, Class<? extends Event> subscribeType) {// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(subscribeType);// 查找對應的發布器EventPublisher eventPublisher = INSTANCE.publisherMap.get(topic);if (null == eventPublisher) {return false;}// 根據發布器類型調用不同的移除方法if (eventPublisher instanceof ShardedEventPublisher) {((ShardedEventPublisher) eventPublisher).removeSubscriber(consumer, subscribeType);} else {eventPublisher.removeSubscriber(consumer);}return true;}/*** 請求發布器發布事件* 發布器采用懶加載模式,只有在實際發布事件時才會調用publisher.start()** @param event 事件實例* @return 發布是否成功*/public static boolean publishEvent(final Event event) {try {// 通過事件實例獲取事件類型,并調用內部發布方法return publishEvent(event.getClass(), event);} catch (Throwable ex) {// 捕獲所有異常,確保不影響調用方LOGGER.error("There was an exception to the message publishing : ", ex);return false;}}/*** 請求發布器發布事件的內部實現** @param eventType 事件類型* @param event     事件實例* @return 發布是否成功*/private static boolean publishEvent(final Class<? extends Event> eventType, final Event event) {// 如果是慢速事件,使用共享發布器發布if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {return INSTANCE.sharePublisher.publish(event);}// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(eventType);// 查找對應的發布器并發布事件EventPublisher publisher = INSTANCE.publisherMap.get(topic);if (publisher != null) {return publisher.publish(event);}// 對于插件事件,允許沒有對應發布器if (event.isPluginEvent()) {return true;}// 找不到發布器,記錄警告日志LOGGER.warn("There are no [{}] publishers for this event, please register", topic);return false;}/*** 注冊到共享發布器* 用于將慢速事件類型注冊到共享發布器** @param eventType 慢速事件類型* @return 共享發布器實例*/public static EventPublisher registerToSharePublisher(final Class<? extends SlowEvent> eventType) {return INSTANCE.sharePublisher;}/*** 使用默認工廠注冊發布器** @param eventType    事件類型* @param queueMaxSize 發布器隊列最大大小* @return 注冊的發布器實例*/public static EventPublisher registerToPublisher(final Class<? extends Event> eventType, final int queueMaxSize) {return registerToPublisher(eventType, DEFAULT_PUBLISHER_FACTORY, queueMaxSize);}/*** 使用指定工廠注冊發布器** @param eventType    事件類型* @param factory      發布器工廠* @param queueMaxSize 發布器隊列最大大小* @return 注冊的發布器實例*/public static EventPublisher registerToPublisher(final Class<? extends Event> eventType,final EventPublisherFactory factory, final int queueMaxSize) {// 如果是慢速事件,直接返回共享發布器if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {return INSTANCE.sharePublisher;}// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(eventType);synchronized (NotifyCenter.class) {// 確保發布器存在,如果不存在則創建// 注釋說MapUtils.computeIfAbsent是不安全的方法,這里使用自定義的MapUtilMapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, eventType, queueMaxSize);}return INSTANCE.publisherMap.get(topic);}/*** 注冊指定的發布器* 允許用戶提供自定義的發布器實例** @param eventType 事件類型* @param publisher 指定的事件發布器*/public static void registerToPublisher(final Class<? extends Event> eventType, final EventPublisher publisher) {// 空檢查,避免空指針異常if (null == publisher) {return;}// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(eventType);synchronized (NotifyCenter.class) {// 只有在不存在時才放入,避免覆蓋已有發布器INSTANCE.publisherMap.putIfAbsent(topic, publisher);}}/*** 取消注冊發布器* 將指定事件類型的發布器從管理容器中移除并關閉** @param eventType 事件類型*/public static void deregisterPublisher(final Class<? extends Event> eventType) {// 獲取事件類型的規范名稱作為topicfinal String topic = ClassUtils.getCanonicalName(eventType);// 從管理容器中移除發布器EventPublisher publisher = INSTANCE.publisherMap.remove(topic);try {// 關閉發布器,釋放資源publisher.shutdown();} catch (Throwable ex) {LOGGER.error("There was an exception when publisher shutdown : ", ex);}}}

Nacos NotifyCenter 核心設計與功能

一、整體架構

NotifyCenter 是 Nacos 中的統一事件通知中心,實現了一個高效的事件發布-訂閱框架,用于系統內部組件間的解耦通信。它采用單例模式,集中管理事件的發布和訂閱,支持高吞吐量的事件處理。

┌────────────────────────────────────────────────────────┐
│                    NotifyCenter                         │
├────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐     ┌─────────────┐     ┌───────────┐ │
│  │ 事件發布管理 │     │ 訂閱者管理  │     │ 資源管理  │ │
│  └─────────────┘     └─────────────┘     └───────────┘ │
│                                                         │
└────────────────────────────────────────────────────────┘▲                    ▲                 ▲│                    │                 │
┌─────────┴──────┐   ┌────────┴────────┐   ┌────┴─────┐
│   Publisher    │   │   Subscriber    │   │  Event   │
└────────────────┘   └─────────────────┘   └──────────┘

二、核心設計特點

1. 雙類型事件機制

NotifyCenter 將事件分為兩類,并采用不同的處理策略:

  • 普通事件:每種事件類型對應一個專用發布器,存儲在 publisherMap
  • 慢速事件(SlowEvent):所有慢速事件共享一個發布器 sharePublisher,避免阻塞普通事件處理
// 決定事件路由到哪個發布器
if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {return INSTANCE.sharePublisher.publish(event);
} else {EventPublisher publisher = INSTANCE.publisherMap.get(topic);return publisher.publish(event);
}

2. 延遲加載與按需創建

發布器采用懶加載模式,僅在需要時才創建,節約系統資源:

// 發布器不存在時才創建
synchronized (NotifyCenter.class) {MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, eventType, ringBufferSize);
}

3. 可配置的緩沖區大小

通過系統屬性支持自定義緩沖區大小,適應不同場景的性能需求:

// 讀取系統配置設置緩沖區大小
String ringBufferSizeProperty = "nacos.core.notify.ring-buffer-size";
ringBufferSize = Integer.getInteger(ringBufferSizeProperty, 16384);String shareBufferSizeProperty = "nacos.core.notify.share-buffer-size";
shareBufferSize = Integer.getInteger(shareBufferSizeProperty, 1024);

4. SPI 擴展機制

使用 Java SPI 機制支持自定義發布器實現,提高框架擴展性:

// 通過SPI加載自定義發布器實現
final Collection<EventPublisher> publishers = NacosServiceLoader.load(EventPublisher.class);

5. 線程安全設計

使用并發工具和同步機制確保多線程環境下的正確性:

  • 采用 ConcurrentHashMap 存儲發布器
  • 使用 synchronized 塊保護關鍵操作
  • 使用 AtomicBoolean 確保關閉操作僅執行一次

三、核心功能模塊

1. 訂閱者管理

支持注冊和注銷訂閱者,支持兩類訂閱者:

  • 普通訂閱者(Subscriber):只訂閱一種事件類型
  • 智能訂閱者(SmartSubscriber):可同時訂閱多種事件類型
public static void registerSubscriber(final Subscriber consumer) {// 針對SmartSubscriber,遍歷其所有訂閱類型if (consumer instanceof SmartSubscriber) {for (Class<? extends Event> subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) {// 分別注冊每種事件類型// ...}} else {// 針對普通Subscriber處理單一事件類型// ...}
}

2. 發布器管理

維護事件類型到發布器的映射,支持注冊、獲取和注銷發布器:

// 發布器容器
private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);// 注冊發布器
public static EventPublisher registerToPublisher(final Class<? extends Event> eventType, final int queueMaxSize) {// ...
}// 獲取發布器
public static EventPublisher getPublisher(Class<? extends Event> topic) {// ...
}// 注銷發布器
public static void deregisterPublisher(final Class<? extends Event> eventType) {// ...
}

3. 事件發布

提供統一的事件發布入口,根據事件類型路由到對應發布器:

public static boolean publishEvent(final Event event) {try {return publishEvent(event.getClass(), event);} catch (Throwable ex) {LOGGER.error("There was an exception to the message publishing : ", ex);return false;}
}

4. 資源管理

提供優雅的資源釋放機制,確保系統關閉時能正確清理資源:

public static void shutdown() {if (!CLOSED.compareAndSet(false, true)) {return;}// 關閉所有普通發布器for (EventPublisher publisher : publisherMap.values()) {publisher.shutdown();}// 關閉共享發布器sharePublisher.shutdown();
}

四、使用流程示例

1. 定義事件

// 普通事件
public class ConfigChangeEvent extends Event {// 事件內容
}// 慢速事件
public class ServiceChangeEvent extends SlowEvent {// 事件內容
}

2. 創建訂閱者

// 普通訂閱者
public class ConfigChangeListener implements Subscriber<ConfigChangeEvent> {@Overridepublic void onEvent(ConfigChangeEvent event) {// 處理事件}@Overridepublic Class<? extends Event> subscribeType() {return ConfigChangeEvent.class;}
}

3. 注冊訂閱者

NotifyCenter.registerSubscriber(new ConfigChangeListener());

4. 發布事件

ConfigChangeEvent event = new ConfigChangeEvent();
NotifyCenter.publishEvent(event);

五、設計優勢

  1. 高性能:通過區分快慢事件、使用環形緩沖區提高事件處理吞吐量
  2. 高可靠:完善的異常處理和資源管理,確保系統穩定性
  3. 良好擴展性:通過SPI機制支持定制發布器實現
  4. 靈活配置:可通過系統屬性調整性能參數
  5. 解耦設計:發布者和訂閱者完全解耦,提高系統模塊化程度

NotifyCenter 作為 Nacos 中的核心基礎設施,為整個系統提供了高效可靠的事件通知機制,是理解 Nacos 內部通信機制的關鍵組件。

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

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

相關文章

Unity物理射線濾除某層

關鍵點&#xff1a;使用LayerMask&#xff0c;針對Physics里檢測collider的射線&#xff08;raycast、OverlapSphere...&#xff09;都適用 1.使用layerMask過濾層 int ignoreLayer LayerMask.NameToLayer("IgnoreRaycast");// 獲取要忽略的層 int layerMask ~(1…

【白話神經網絡(二)】矩陣、CNN、RNN

全連接層 回顧前面學過的知識&#xff1a; 一個最簡單的神經網絡&#xff0c;就是ywxb 套上一個激活函數。 如果有多個輸入&#xff0c;那就是多個w和x 如果有多個輸出&#xff0c;那就再來一行公式&#xff0c;多一組w和b 要是神經元多了的話&#xff0c;公式密密麻麻的&…

Unity教程(二十二)技能系統 分身技能

Unity開發2D類銀河惡魔城游戲學習筆記 Unity教程&#xff08;零&#xff09;Unity和VS的使用相關內容 Unity教程&#xff08;一&#xff09;開始學習狀態機 Unity教程&#xff08;二&#xff09;角色移動的實現 Unity教程&#xff08;三&#xff09;角色跳躍的實現 Unity教程&…

深入解析Java面向對象三大特征之多態、final、抽象類與接口

面向對象編程&#xff08;OOP&#xff09;的三大核心特征為封裝、繼承、多態&#xff0c;其中多態是最具靈活性和擴展性的特性。本文將從多態的本質出發&#xff0c;結合final關鍵字、抽象類與接口的設計&#xff0c;深入探討這些概念的應用場景及其在代碼中的實現細節&#xf…

編碼器和解碼器概念及算法示例【清晰易懂】

編碼器&#xff08;Encoder&#xff09;和解碼器&#xff08;Decoder&#xff09;是處理信息的一對“搭檔”&#xff0c;它們的作用就像是“翻譯員”和“逆翻譯員”。 1. 編碼器&#xff08;Encoder&#xff09;是什么&#xff1f; &#x1f449; 把原始信息變成另一種形式&a…

爬蟲逆向:逆向中用到匯編語言詳細總結

更多內容請見: 爬蟲和逆向教程-專欄介紹和目錄 文章目錄 一、匯編語言基礎二、常見匯編指令2.1 數據傳輸指令2.2 算術指令2.3 邏輯指令2.4 控制流指令2.5 其他指令三、寄存器概述四、調用約定五、棧操作與函數調用六、逆向工程中的匯編分析七、常用逆向工具八、實際案例分析九…

CTF WEB題

[文件包含,少許難度] 地址:攻防世界 代碼審計WRONG WAY! <?php include("flag.php"); #包含了一個“flag.php”文件 highlight_file(__FILE__); #來顯示當前文件的源代碼 if(isset($_GET["file1"]) && isset($_GET["file2"])) #isse…

c++圖論(一)之圖論的起源和圖的概念

C 圖論之圖論的起源和圖的概念 圖論&#xff08;Graph Theory&#xff09;是數學和計算機科學中的一個重要分支&#xff0c;其起源可以追溯到 18 世紀 的經典問題。以下是圖論的歷史背景、核心起源問題及其與基本概念和用途&#xff1a; 借用一下CSDN的圖片哈 一、圖論的起源&…

Ollama + CherryStudio:構建本地私有知識庫

前面我們介紹了Ollama的安裝和使用&#xff0c;并通過Open-WebUI進行調用&#xff0c;相信大家對Ollama也有了一定的了解&#xff1b;這篇博文就結合Ollama工具和CherryStudio工具構建一個本地知識庫&#xff08;RAG&#xff09;&#xff1b;在進行接下來的操作之前&#xff0c…

【實戰ES】實戰 Elasticsearch:快速上手與深度實踐-8.2.1AWS OpenSearch無服務器方案

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 8.2.1AWS OpenSearch 無服務器方案深度解析與實踐指南1. Serverless架構的核心價值與行業趨勢1.1 傳統Elasticsearch集群的運維挑戰1.2 Serverless技術演進路線技術特性對比…

清晰易懂的Java8安裝教程

小白也能看懂的 Java 8 安裝教程&#xff08;JDK 和 JRE 分目錄安裝&#xff09; 本教程將手把手教你如何在 Windows 系統上安裝 Java 8&#xff08;JDK 1.8&#xff09;&#xff0c;并將 JDK 和 JRE 安裝到不同的目錄中&#xff0c;同時提供國內 Java 8 下載源和方法。即使你…

圖搜索的兩種寫法,廣度優先和深度優先

最近AI的爆發大家都瘋了&#xff0c;也確實夠瘋&#xff0c;前幾年誰能天天和AI聊天呢&#xff0c;特別它越來越智能&#xff0c;越來越理解你&#xff0c;你越來越離不開它&#xff0c;我很好奇將來它會不會有情緒&#xff0c;太可怕了&#xff0c;一旦有了這個就有了感情&…

嵌入式八股RTOS與Linux---前言篇

前言 Linux與RTOS是校招八股的時候很喜歡考察的知識,在這里并沒有把兩個操作系統完全的獨立開去講,放在一起對比或許可能加深印象。我們講Linux的內核有五部分組成:進程調度、內存管理、文件系統、網絡接口、進程間通信,所以我也將從這五方面出發 中斷管理去對比和RTOS的不同。…

ChatBI 的技術演進與實踐挑戰:衡石科技如何通過 DeepSeek 實現商業落地

隨著人工智能技術的快速發展&#xff0c;ChatBI&#xff08;基于自然語言交互的商業智能&#xff09;逐漸成為企業數據分析領域的熱門話題。作為 BI&#xff08;商業智能&#xff09;領域的新形態&#xff0c;ChatBI 通過自然語言處理&#xff08;NLP&#xff09;技術&#xff…

基于Vue實現Echarts的平滑曲線

在Vue2.x的項目中使用echarts實現如下效果 安裝echarts npm install echarts --save組件引入echarts // 在你的Vue組件中 import * as echarts from echarts;在模板中添加一個div元素&#xff0c;用來放置圖表 <divref"chart"class"chart"style"…

關于重構分析查詢界面的思考(未完)

業務系統里&#xff0c;查詢界面很常見&#xff0c;數據分析場景需求普遍而迫切&#xff0c;而新的技術也在不斷出現&#xff0c;很有必要重構分析查詢界面。 查詢篩選 為了盡可能從數據中發現&#xff0c;需要盡可能地將查詢條件添加進來&#xff0c;可這樣&#xff0c;查詢…

在jQuery中DOM操作

&#xff08;一&#xff09;元素選取 各種選擇器的使用方法與示例 標簽選擇器&#xff1a;通過 HTML 標簽名稱來選取元素。例如&#xff0c;若想選中頁面中所有的段落元素&#xff0c;可使用$(‘p’)。假設我們有如下 HTML 結構&#xff1a; 這是第一個段落 這是嵌套在div中的…

Java 集合框架中 `List` 接口及其子類的詳細介紹,并用 UML 圖表展示層次結構關系,用表格對比各個類的差異。

下面是 Java 集合框架中 List 接口及其子類的詳細介紹&#xff0c;并用 UML 圖表展示層次結構關系。最后&#xff0c;我會用表格對比各個類的差異。 Java 集合框架中 List 接口及其子類 UML 類圖描述 以下是 List 接口及其子類的 UML 類圖描述&#xff0c;不包含方法。 詳細…

Java面試八股—Redis篇

一、Redis的使用場景 &#xff08;一&#xff09;緩存 1.Redis使用場景緩存 場景&#xff1a;緩存熱點數據&#xff08;如用戶信息、商品詳情&#xff09;&#xff0c;減少數據庫訪問壓力&#xff0c;提升響應速度。 2.緩存穿透 正常的訪問是&#xff1a;根據ID查詢文章&…

Spring Boot使用線程池創建多線程

在 Spring Boot 2 中&#xff0c;可以使用 Autowired 注入 線程池&#xff08;ThreadPoolTaskExecutor 或 ExecutorService&#xff09;&#xff0c;從而管理線程的創建和執行。以下是使用 Autowired 方式注入線程池的完整示例。 1. 通過 Autowired 注入 ThreadPoolTaskExecuto…