深度思考rpc框架面經之四

7 netty機制的一些理解

推薦閱讀: 深度思考netty網絡編程框架

7.1 Netty支持的端口號:

Netty可以綁定到任何合法的端口號,這與大多數網絡庫類似。有效的端口范圍是從0到65535,但通常建議使用1024以上的端口,因為0-1023的端口已經為眾所周知的服務保留。

7.2 Netty使用NIO機制:

  • NIO與復用Channel:
    Netty基于Java NIO(非阻塞IO)構建,其核心是事件驅動和回調機制。當我們說到"復用Channel",通常指的是IO多路復用技術,例如在Unix系統中的select, poll和epoll等機制。這些機制允許單一線程監聽多個連接的IO事件(如可讀、可寫等),從而提高并發處理能力。

  • 程序員手動設置:
    Netty提供了一個高層次的抽象,所以開發者不需要直接與低層的多路復用API交互。開發者創建和配置ChannelEventLoop和其他Netty組件時,IO多路復用是自動進行的。但是,當然,Netty也為高級用戶提供了足夠的靈活性,以允許他們調整和優化行為。

7.3 Netty默認的傳輸層協議:

當你創建一個Netty服務或客戶端而沒有明確指定傳輸類型時,Netty默認使用TCP作為其傳輸協議。但是,明確指定你想使用的傳輸類型總是一個好習慣。例如,在Netty中,你可以選擇NioServerSocketChannelNioSocketChannel為基于NIO的TCP傳輸,也可以選擇其他的Channel類型,如NioDatagramChannel用于UDP傳輸。

7.4 這里的channel相當于是與某一個客戶端建立的連接,EventLoop相當于調用了select方法監聽這些連接嗎,然后會根據線程模型將這些連接交給相應的handle

是的,你的理解基本上是準確的。Netty的概念和傳統的網絡編程模型有很多相似之處,但它為開發者提供了更高級的抽象和更靈活的配置選項。以下是對你提到的幾個關鍵點的概述和補充:

  1. Channel:

    在Netty中,Channel是網絡通信的核心抽象,代表一個連接或一個數據傳輸通道。對于TCP來說,一個Channel對應于一個客戶端與服務器之間的連接。

  2. EventLoop:

    EventLoop負責處理網絡事件和執行I/O操作。事實上,一個EventLoop通常對應于一個線程,這個線程在內部使用如selectpollepoll等多路復用技術來監聽多個Channel上的事件。這樣,一個線程(或EventLoop)可以高效地管理和處理多個連接。

  3. 線程模型與Handler:

    Netty有一個叫做EventLoopGroup的概念,它是EventLoop的一個集合,通常用于代表一組工作線程。當一個新的連接被接受時,它會被分配給EventLoopGroup中的一個EventLoop并且在該連接的整個生命周期內,所有的I/O操作都由這個EventLoop(即一個固定的線程)處理,這保證了對于一個特定的連接,其相關的事件始終由同一個線程按順序處理。

    EventLoop檢測到某個Channel上的事件(例如數據可讀)時,它會將該事件傳遞給對應的Handler處理。Handler是Netty中用于處理各種網絡事件(如讀、寫、連接、斷開連接等)的組件。

  4. Pipeline:

    Netty還有一個ChannelPipeline的概念,它是ChannelHandler的一個鏈表,用于處理或攔截Channel的入站和出站事件。這為開發者提供了一個強大的工具來組合和重用各種處理邏輯。

總之,Netty提供了一套強大而靈活的工具和抽象,使得網絡編程變得更加簡單和高效。

8 服務注冊/發現/下線以及負載均衡(阿里一面)

8.1 一個rpc框架什么時候跟注冊中心交互的,可以介紹一下嗎?

RPC框架與注冊中心主要在以下幾個時刻交互:

  • 服務注冊:當服務提供者啟動后,會將自己提供的服務信息注冊到注冊中心,通常這個過程發生在服務提供者啟動的初始化階段。

  • 服務發現:服務消費者在啟動后,會從注冊中心獲取所需的服務信息,這個過程稱為服務發現。服務發現可能在服務消費者啟動的時候發生,也可能在運行過程中定時進行,以獲取最新的服務信息。

  • 服務下線:服務提供者在關閉或者因為某種原因無法提供服務時,會將自己從注冊中心移除。這個過程通常發生在服務提供者關閉的時候。

  • 服務變更:當自身的運行狀態發生了變化時,可以向注冊心報告

8.2 服務注冊的時候會注冊什么信息呢?

正確答案:
在使用RPC(遠程過程調用)框架的過程中,服務注冊是非常重要的一部分。在服務注冊過程中,以下是一些通常會注冊的信息:

  • 服務標識符(與實例id不同):這是一個全局唯一的標識符,用于區分各個服務。這個標識符通常包含服務的名稱以及版本號,這樣消費者可以根據需要選擇對應版本的服務。
  • 服務地址:注冊的服務需要提供其可以被找到和訪問的網絡地址,這通常包括IP地址和端口號。
  • 服務接口元數據:這包括一些額外的服務信息,如服務的描述、服務的狀態(如健康狀況)、服務提供的方法或接口詳情等。
  • 服務提供者信息:可能包含提供者的機器信息,比如CPU,內存等,便于服務發現和負載均衡系統做出更合理的決策。

這些信息將被注冊到服務注冊中心,供消費者查詢和使用。注意,具體注冊哪些信息可能會因不同的RPC框架和使用場景而異。

8.3 rpc的客戶端路由怎么做的(是在講怎么做負載均衡的)?支持自定義的路由操作嗎(自定義的負載均衡策略)

真正要實現拓展的方法:

RPC框架通常會提供一些內置的負載均衡策略,例如:輪詢、隨機、最小連接數等。然而,在某些情況下,這些內置的策略可能無法滿足特定的業務需求。許多RPC框架也考慮到了這種情況,所以它們支持用戶自定義負載均衡策略。
以一些常見的RPC框架為例:

  • 在Dubbo中,用戶可以通過實現LoadBalance接口來定義自己的負載均衡策略。在服務消費者端,可以通過@Reference(loadbalance = “myLoadBalance”)來使用自定義的負載均衡策略。

在實現自定義的負載均衡策略時,需要考慮到服務的健康狀況、網絡延遲、服務器的負載等因素。此外,也需要注意線程安全性和性能的問題。
注意,自定義負載均衡策略的具體方法會因不同的RPC框架而異。具體實現時,需要查閱對應RPC框架的官方文檔或相關資料。

9 泛化調用

9.1 什么是泛化調用?

答:一般的調用過程,使用的方法簽名是明確的,明文的,但是還有一種情況是服務端本身提供不了這么明確的api簽名,這個時候你傳過去的服務和方法都有可能是字符串,你傳過去的參數就是一個大的map,它只是指定好了哪個請求打到對應的哪個服務端,服務端這一層再去做相應的反射和尋找;客戶端拿到響應的數據時,不使用已知sdk對象接收,而是用固定的泛型或者Object對象接收。

9.1.1 gpt4:什么是RPC框架的泛化調用?

泛化調用允許客戶端調用服務端的方法,而無需在客戶端編譯時知道服務的接口定義。簡而言之,它允許在沒有服務接口和實體類的情況下調用遠程服務。這對于某些動態調用的場景(如API網關或特定的服務測試工具)特別有用。

使用泛化調用,服務消費者通常傳遞服務名稱、方法名稱以及方法參數等詳細信息,RPC框架則負責將這些信息轉化為實際的服務調用。

9.1.2 有哪些RPC框架支持這種調用?

多數成熟的RPC框架都支持泛化調用。常見的如:

  • Dubbo: Alibaba的開源RPC框架Dubbo支持泛化調用,允許使用者在不知道服務定義的情況下進行遠程方法調用。
  • gRPC: Google的gRPC框架也支持類似的功能,盡管它的實現方式可能與Dubbo等框架略有不同。
  • Apache Thrift: 這是另一個支持泛化調用的RPC框架。

9.1.3 泛化調用下,服務的提供者還需要向注冊中心注冊所有的接口服務嗎?

是的,服務提供者仍然需要將其接口服務注冊到注冊中心。泛化調用主要影響的是客戶端,因為它允許客戶端在不知道接口定義的情況下進行調用。而服務提供者仍然需要將其服務和接口信息注冊到注冊中心,以便消費者能夠發現和調用它。注冊中心的主要目的是服務發現,不論是傳統的調用還是泛化調用,消費者都需要知道提供者的地址和其他元數據。

9.1.4 比如下面我這種客戶端的調用方式是非泛化調用嘛:

    public static void main2(String[] args) {RpcClient client = new NettyClient(CommonSerializer.PROTOBUF_SERIALIZER);RpcClientProxy rpcClientProxy = new RpcClientProxy(client);HelloService helloService = rpcClientProxy.getProxy(HelloService.class);HelloObject object = new HelloObject(12, "This is a message");String res = helloService.hello(object);System.out.println(res);ByeService byeService = rpcClientProxy.getProxy(ByeService.class);System.out.println(byeService.bye("Netty"));}

答:
基于你提供的代碼,你正在使用的是非泛化調用。以下是原因:

  1. 明確定義的接口: 你有HelloServiceByeService兩個明確定義的接口,以及相應的方法(例如hello()bye())。在非泛化調用中,服務消費者和服務提供者通常都知道這個接口的定義。

  2. 明確的類型: 使用了明確的類型,如HelloObject。這是一個特定類型的參數,消費者和提供者都知道其結構。

  3. 代理的使用: 你通過RpcClientProxy創建了接口的代理,使得遠程調用看起來就像是本地調用。但是,你仍然明確地調用了helloService.hello(object)byeService.bye("Netty"),這意味著你知道調用的方法和需要傳遞的參數。

泛化調用中,調用通常更加動態和靈活。例如,你可能只需要知道方法的名字、參數的類型和參數值,但不需要事先知道接口或方法的明確定義。而在非泛化調用中,像你的示例那樣,所有這些都是明確的和預先定義的。

9.1.5 那你能給我一個簡單的泛化調用的demo嘛

當然可以。泛化調用是一種更加靈活、更加動態的調用方式,主要應用于以下場景:

  1. 當你不知道要調用的具體服務或方法。
  2. 當服務消費者和服務提供者沒有共享的接口定義。
  3. 當需要構建更加靈活的、可以在運行時決定調用哪個服務的系統。

泛化調用的關鍵在于,你不是基于固定的、已知的接口或類來發起調用,而是在運行時動態構建要調用的服務信息。

以下是一個泛化調用的簡單示例:

public class GenericInvokeDemo {public static void main(String[] args) throws Exception {RpcClient client = new NettyClient(CommonSerializer.PROTOBUF_SERIALIZER);RpcClientProxy rpcClientProxy = new RpcClientProxy(client);// 使用泛化調用,不需要預先定義接口String serviceName = "HelloService";String methodName = "hello";// 這里僅僅是為了示意,實際的參數名和參數類型可能需要通過其他方式獲得String[] parameterTypes = new String[] {"HelloObject"};Object[] arguments = new Object[] {new HelloObject(12, "This is a message")};Object result = invoke(rpcClientProxy, serviceName, methodName, parameterTypes, arguments);System.out.println(result);}public static Object invoke(RpcClientProxy proxy, String serviceName, String methodName, String[] parameterTypes, Object[] arguments) throws Exception {// 構建RpcRequest,通常需要更加復雜的邏輯來處理參數類型和參數值RpcRequest request = new RpcRequest();request.setInterfaceName(serviceName);request.setMethodName(methodName);request.setParameters(arguments);// ... 其他屬性設置// 使用RpcClientProxy發送請求RpcResponse response = proxy.sendRequest(request);// 檢查響應并返回結果if (response.getStatus() == RpcResponse.SUCCESS) {return response.getData();} else {throw new Exception("RPC調用失敗: " + response.getMessage());}}
}

在這個示例中,我們沒有直接使用HelloService接口或它的hello方法,而是動態地構建了serviceNamemethodName來確定要調用的服務。這允許我們在沒有預先知道具體服務的情況下進行RPC調用。

請注意,這只是一個簡化的示例,實際的泛化調用可能會涉及到更加復雜的邏輯,例如處理不同的參數類型、處理不同的返回類型等。

9.2 為什么需要泛化調用?

答:泛化調用的主要優勢在于它提供了一個高度靈活的調用方式,使得調用者不必依賴于服務端的具體接口定義。泛化調用有以下主要應用場景和好處:

  1. 動態調用:當接口數量巨大或者接口經常變動時,使用泛化調用可以避免頻繁地更新客戶端SDK或者代碼。

  2. 橋接不同系統:對于一些需要跨平臺或跨語言工作的應用,泛化調用可以作為一個中間層來橋接不同系統,不必為每個系統單獨定制API。

  3. 測試與工具開發:泛化調用可以方便地構建測試工具,讓開發者或者測試者動態地調用服務,而無需預先知道服務的具體實現。

  4. API網關與管理:對于API網關或者服務管理平臺,泛化調用允許它們轉發請求到具體的服務,同時提供統一的日志、監控和安全策略。

  5. 減少代碼冗余:泛化調用避免了為每一個服務或者接口生成和維護客戶端代碼,降低了維護成本。

9.3 泛化調用的缺點是什么?

答:盡管泛化調用提供了很大的靈活性,但它也有一些缺點:

  1. 性能開銷:由于涉及到動態解析和反射,泛化調用通常比具體的方法調用有更高的性能開銷。

  2. 復雜性增加:泛化調用引入了額外的解析和映射邏輯,可能會增加系統的復雜性。

  3. 類型不安全:由于方法簽名和參數都是動態指定的,可能導致運行時錯誤,而這些錯誤在編譯時是難以檢測到的。

  4. 開發體驗:對于開發者來說,沒有具體的接口定義可能導致更難以理解和使用,同時也難以利用某些IDE的特性,如代碼補全和提示。

總的來說,泛化調用是一個強大而靈活的工具,但也需要根據具體的應用場景來權衡其優缺點。

10 protocol buffer協議

推薦閱讀:protobuf怎樣傳輸復雜數據結構?

10.1 什么是.proto文件,里面包含了什么內容,一般存儲在哪里?

  1. .proto 文件:
    .proto 文件是 Protocol Buffers (protobuf) 的語言中立的描述文件。它定義了你想要通過 protobuf 序列化或反序列化的消息的格式。使用特定于語言的編譯器(例如 protoc 為 Java、C++ 和 Python),可以從 .proto 文件生成數據訪問類。

  2. .proto 文件內容:
    .proto 文件包括以下內容:

    • 消息定義: 定義消息類型和它們的字段。
    message Person {string name = 1;int32 age = 2;string email = 3;
    }
    
    • 字段數據類型: 像 int32、string、bool、bytes 等基本數據類型,以及自定義類型(其他消息類型或枚舉)。

    • 服務定義 (可選): 如果你使用 protobuf 與 gRPC 進行 RPC 通信,那么你可以在 .proto 文件中定義服務和它們的方法。

    service MyService {rpc MyMethod(RequestType) returns (ResponseType);
    }
    
    • 枚舉定義:
    enum MyEnum {UNKNOWN = 0;TYPE_A = 1;TYPE_B = 2;
    }
    
    • 導入其他 .proto 文件: 使用 import 語句,可以在一個 .proto 文件中重用另一個 .proto 文件中的定義。
    import "other_file.proto";
    
    • 包和命名空間:為了避免命名沖突,你可以給 .proto 文件定義一個包。
    package mypackage;
    
    • 其他選項: 如定義自定義的 Java 包、優化模式等。
    option java_package = "com.mycompany.mycode";
    
  3. .proto 文件存儲位置:
    .proto 文件的存儲位置取決于項目的結構和開發團隊的選擇。在多個項目或多種語言之間共享 .proto 文件時,可能會將它們存儲在一個中心的位置。對于大型的組織,有時會有一個專門的 git 倉庫來存儲所有 .proto 文件,以確保跨團隊之間的共享和版本控制。

    在單個項目中,.proto 文件通常與源代碼一起存儲在版本控制系統中,例如在一個特定的目錄下,例如 src/main/proto/(對于 Java Maven 項目)。

總的來說,.proto 文件是 protobuf 系統中的核心部分,它定義了要序列化和反序列化的消息的結構。

10.2 .proto文件定義了消息的格式,所以客戶端和服務端雙方會根據這個文件反射生成對應的類是嘛?

是的,您的理解是正確的。

當您定義了一個.proto文件后,可以使用 Protocol Buffers 的編譯器 protoc 來生成對應的代碼。這些生成的代碼會包含你在.proto文件中定義的消息類型的類定義。

例如,如果你定義了一個 .proto 文件,內容如下:

syntax = "proto3";message Person {string name = 1;int32 age = 2;string email = 3;
}

使用 protoc 編譯器,你可以為多種編程語言生成代碼,例如 Java、C++、Python 等。比如,如果你想為 Java 生成代碼,你可以使用以下命令:

protoc --java_out=. your_file_name.proto

執行這條命令后,編譯器將會為你生成一個 Java 文件,里面包含了 Person 類的定義。這個類會包含字段的 getter、setter 以及其他一些方法,如序列化和反序列化的方法。

因此,服務端和客戶端都可以使用這個.proto文件生成對應的代碼,并確保他們之間的消息格式是一致的。這是 Protocol Buffers 強大之處:您只需要定義一次消息格式,然后可以為多種編程語言生成代碼。

10.3 如果使用 Protocol Buffers,客戶端和服務端序列化對象的 Serializable 接口還有必要實現嗎?(根據10.1和2可知不需要了)

當你使用 Protocol Buffers (protobuf) 作為序列化框架時,不需要再讓你的對象實現 Java 的 Serializable 接口。實際上,protobuf 有自己的方式來定義和序列化數據。

在 protobuf 中,你會定義 .proto 文件,描述你的數據結構。然后,protobuf 編譯器 (protoc) 會生成對應語言的數據類和序列化/反序列化代碼。這些生成的代碼在 Java 中并不依賴于 Serializable 接口。

總之,如果你已經選擇使用 protobuf,那么不需要(也不應該)實現 Serializable 接口。而protobuf提供的序列化和反序列化方法會比 Java 原生的 Serializable 更高效和靈活。

10.4 如何理解 Protocol Buffers的兼容性

Protocol Buffers(簡稱 Protobuf)的設計重點之一是向后兼容性和向前兼容性。這使得服務可以獨立地進行迭代和部署,而不會導致與其他服務或客戶端之間的通信中斷。這種兼容性對于微服務架構和分布式系統來說尤為重要,因為在這些系統中,各個服務的升級和部署可能是獨立進行的。

以下是一些關于 Protobuf 兼容性的要點:

  1. 向后兼容性(Backwards Compatibility)

    • 如果您添加新的字段,老版本的代碼讀取新版本的數據時,它會忽略這些新字段。
    • 如果您刪除一個字段,您不應該再使用該字段的編號。新版本的代碼讀取舊版本的數據時,會將該字段視為默認值(如果提供的話)。
  2. 向前兼容性(Forwards Compatibility)

    • 老版本的代碼可以解析由新版本的代碼產生的數據(不包括新代碼引入的新字段)。
    • 新字段在舊版本代碼中會被忽略。
  3. 非兼容性的變更

    • 修改已存在的字段的編號是破壞性的。
    • 修改字段的類型(如從 int32 改為 string)也是破壞性的。
  4. 保留字段

    • 如果你刪除了字段,你應該在 .proto 文件中聲明該字段為保留字段,以確保將來不會再次使用這個字段編號或名稱。

為了最大程度地保持兼容性,通常的建議是:

  • 不要改變已存在的字段的編號和類型。
  • 新增的字段應該是可選的,或者有明確的默認值。
  • 刪除字段時,使用保留關鍵字來防止未來的沖突。

這種兼容性機制確保了即使服務和客戶端的版本不完全匹配,他們之間的通信也可以正常進行,至少在大多數情況下。

10.5 能給出例子嘛?

當然可以。通過具體的例子,我們來了解 Protocol Buffers 的向前和向后兼容性。

假設我們有一個服務,該服務用于管理用戶信息。最初版本的 .proto 文件可能如下:

Version 1 的 .proto 文件:

message User {int32 id = 1;string name = 2;
}
  1. 當服務和客戶端都是這個版本時,他們可以順利地互相通訊。

現在,我們決定擴展這個消息,增加一個新的字段。

Version 2 的 .proto 文件:

message User {int32 id = 1;string name = 2;string email = 3; // 新增的字段
}
  1. 向后兼容性:當新版本的服務發送包含 email 字段的 User 消息給舊版本的客戶端時,舊客戶端會忽略這個新字段,并正常處理其他字段。這就是向后兼容性。

隨后,我們決定某個字段不再需要了,所以我們要刪除它。

Version 3 的 .proto 文件:

message User {int32 id = 1;string name = 2;reserved 3; // 字段編號 3 現在已經被保留
}
  1. 向前兼容性:當舊版本的服務(還有 email 字段的版本)發送 User 消息給新版本的客戶端時,新客戶端會看到這個字段已被標記為保留(雖然實際的名字 email 不會出現),因此會忽略它,并正常處理其他字段。這就是向前兼容性。

這些示例展示了如何在 Protobuf 消息結構發生變化時保持通信的兼容性。這種兼容性極大地方便了分布式系統的迭代和部署。

10.6 什么是向前兼容、向后兼容

這個問題的答案涉及到我們如何定義"前"和"后"的概念,以及我們從哪個方向考慮這種兼容性。

  1. 向后兼容性(Backwards Compatibility)

    • 當我們說某樣東西是“向后兼容”的時候,我們通常意味著新版本的系統可以處理舊版本的數據或請求。換句話說,我們在向“后面”看,考慮新版本與之前的版本之間的兼容性。
    • 例如,一個新版的軟件可以讀取由其舊版本創建的文件。或者在本例中,新版本的服務可以處理舊版本客戶端的請求。
    • 實際場景:服務端使用了新版本(如增加了新字段),而客戶端仍然使用舊版本。當客戶端發送不包含新字段的數據到服務端時,服務端能夠正常處理這些數據。
  2. 向前兼容性(Forwards Compatibility)

    • 當我們說某樣東西是“向前兼容”的時候,我們意味著舊版本的系統可以處理新版本的數據或請求。我們在向“前面”看,考慮如何讓舊版本與未來的版本之間保持兼容性。
    • 例如,一個舊版的軟件可以讀取由其新版本創建的文件。或者在本例中,舊版本的客戶端可以處理新版本服務端的響應。
    • 實際場景:客戶端使用了新版本(如增加了新字段),而服務端仍然使用舊版本。當服務端發送不包含新字段的數據到客戶端時,客戶端能夠正常處理這些數據,且忽略新字段。

這種命名方式可能會令人感到混淆,但主要是從系統的角度來看:當考慮“后面”的版本時,我們考慮向后兼容性;當考慮“前面”的版本時,我們考慮向前兼容性。

10.7 版本的新舊是由服務端和客戶端的對比來的是嘛,并且以服務端為中心

是的,你的理解是正確的。在許多分布式系統和客戶端-服務器模型中,特別是在考慮兼容性時,服務端往往被視為中心。原因有以下幾點:

  1. 集中控制:服務端通常有更多的控制權。例如,你可以決定何時更新服務端軟件,但你無法決定所有客戶端何時更新。

  2. 多樣性的客戶端:客戶端可能有各種各樣的版本、平臺和配置。考慮到這種多樣性,保持服務端的穩定性和兼容性顯得尤為重要。

  3. 穩定性和持續性:服務端通常需要24/7在線,為各種版本的客戶端提供服務。因此,考慮服務端的兼容性尤為關鍵。

  4. 發布流程:在某些情況下,服務端的更新可能相對集中和可控,而客戶端的更新(尤其是在移動環境中)可能會受到用戶選擇、設備兼容性等因素的影響。

因此,當我們討論兼容性時,我們確實通常從服務端的角度來看。例如,向后兼容性確保新版本的服務端能夠與舊版本的客戶端交互,而向前兼容性確保舊版本的服務端能夠與新版本的客戶端交互。這種以服務端為中心的視角使得版本管理和協議更新變得相對更加簡單和可控。

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

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

相關文章

算法與數據結構(二十四)最優子結構原理和 dp 數組遍歷方向

注:此文只在個人總結 labuladong 動態規劃框架,僅限于學習交流,版權歸原作者所有; 本文是兩年前發的 動態規劃答疑篇open in new window 的修訂版,根據我的不斷學習總結以及讀者的評論反饋,我給擴展了更多…

【STM32】高效開發工具CubeMonitor快速上手

工欲善其事必先利其器。擁有一個輔助測試工具,能極大提高開發項目的效率。STM32CubeMonitor系列工具能夠實時讀取和呈現其變量,從而在運行時幫助微調和診斷STM32應用,類似于一個簡單的示波器。它是一款基于流程的圖形化編程工具,類…

面試題:線程池的底層工作原理

線程池的幾個重要的參數: 1、corePoolSize:線程池的核心線程數(也是默認線程數) 2、maximumPoolSize:最大線程數 3、keepAliveTime:允許的線程最大空閑時間(單位/秒) 線程池內部是…

鏈表之第二回

歡迎來到我的:世界 該文章收入欄目:鏈表 希望作者的文章對你有所幫助,有不足的地方還請指正,大家一起學習交流 ! 目錄 前言第一題:反轉一個鏈表第二題:鏈表內指定區間反轉第三題:判斷一個鏈表…

opencv+ffmpeg+QOpenGLWidget開發的音視頻播放器demo

前言 本篇文檔的demo包含了 1.使用OpenCV對圖像進行處理,對圖像進行置灰,旋轉,摳圖,高斯模糊,中值濾波,部分區域清除置黑,背景移除,邊緣檢測等操作;2.單純使用opencv播放…

一個案例:Vue2組件化開發組件從入門到入土

1. 環境搭建 1.1. 創建項目 npm install -g vue/clivue create vue_study_todolist1.2. 清空項目代碼 清楚HelloWorld.Vue代碼中的內容。 1.3. 啟動空項目 1.4 項目目標 項目組件實現以下效果 2. 組件拆分代碼 Vue是一個基于組件的框架,允許您將界面拆分成小的…

open cv學習 (五) 圖像的閾值處理

圖像的閾值處理 demo1 # 二值化處理黑白漸變圖 import cv2 img cv2.imread("./img.png", 0) # 二值化處理 t1, dst cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) cv2.imshow("img", img) cv2.imshow("dst", dst) cv2.waitKey() cv2.des…

Golang使用MinIO

最近在使用Golang做了一個網盤項目(學習),文件存儲一直保存在本地(各廠商提供的oss貴),所以就在思考怎么來處理這些文件,類似的方案很對hdfs、fastdfs,但這其中MinIO是最近幾年比較火…

生信豆芽菜-差異基因富集分析的圈圖

網址:http://www.sxdyc.com/visualsEnrichCirplot 1、數據準備 準備一個基因集的文件 2、選擇富集分析的數據庫,同時輸入展示top幾的條目,選擇顏色,如果是GO的話選擇三個顏色,如果是KEGG選擇一個,如果是G…

神經網絡論文研讀-多模態方向-綜述研讀(上)

翻譯以機翻為主 原文目錄 前言 圖1:LMU印章(左)風格轉移到梵高的向日葵繪畫(中)并與提示混合 - 梵高,向日葵 -通過CLIPVGAN(右)。在過去的幾年中,自然語言處理&#xff…

微信小程序實現拖拽的小球

目錄 前言 js 獲取微信小程序中獲取系統信息 觸摸移動事件的處理函數 觸摸結束事件的處理函數 用于監聽頁面滾動事件 全局參數 html CSS 前言 小程序開發提供了豐富的API和事件處理函數,使得開發者可以方便地實現各種交互功能。其中,拖拽功能…

無涯教程-Perl - tell函數

描述 此函數返回指定FILEHANDLE中讀取指針的當前位置(以字節為單位)。如果省略FILEHANDLE,則它將返回上次訪問的文件中的位置。 語法 以下是此函數的簡單語法- tell FILEHANDLEtell返回值 此函數以字節為單位返回當前文件位置。 例 以下是顯示其基本用法的示例代碼,要檢…

leetcode473. 火柴拼正方形(回溯算法-java)

火柴拼正方形 leetcode473 火柴拼正方形題目描述回溯算法 上期經典算法 leetcode473 火柴拼正方形 難度 - 中等 原題鏈接 - leetcode473 火柴拼正方形 題目描述 你將得到一個整數數組 matchsticks ,其中 matchsticks[i] 是第 i 個火柴棒的長度。你要用 所有的火柴棍…

BC119 小樂樂與字符串

描述 在慶祝祖國母親70華誕之際&#xff0c;老師給小樂樂出了一個問題。大家都知道China的英文縮寫是CHN&#xff0c;那么給你一個字符串s&#xff0c;你需要做的是統計s中子序列“CHN”的個數。子序列的定義&#xff1a;存在任意下標a < b < c&#xff0c;那么“s[a]s[b…

微服務—Eureka注冊中心

eureka相當于是一個公司的管理人事HR,各部門之間如果有合作時&#xff0c;由HR進行人員的分配以及調度&#xff0c;具體選哪個人&#xff0c;全憑HR的心情&#xff0c;如果你這個部門存在沒有意義&#xff0c;直接把你這個部門撤銷&#xff0c;全體人員裁掉&#xff0c;所以不想…

計算機網絡筆記

TCP有連接可靠服務 TCP特點&#xff1a; 1.TCP是面向連接的傳輸層協議&#xff1b; 2.每條TCP連接只能有兩個端點&#xff0c;每條TCP連接是一對一的&#xff1b; 3.TCP提供可靠交付&#xff0c;保證傳送數據無差錯&#xff0c;不丟失&#xff0c;不重復且有序&#xff1b; 4.…

Android Studio瀑布流實現

效果&#xff1a; ImageDetail class package com.example.waterfallflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.ImageView;public class ImageDetail extends Activity{Overrideprotected void …

DNNGP、DeepGS 和 DLGWAS模型構成對比

一、DNNGP DNNGP 是基于深度卷積神經網絡&#xff0c;這個結構包括一個輸入層&#xff0c;三個卷積層&#xff0c;一個批標準化層&#xff0c;兩個dropout層&#xff0c;一個平坦化層&#xff0c;一個 dense層。 dropout層&#xff1a;在神經網絡中,dropout層是一個非常有效的正…

信息與通信工程面試準備——數學知識|正態分布|中心極限定理

目錄 正態分布 正態分布的參數 正態分布的第一個參數是均值 正態分布的第二個參數是標準差SD 所有正態分布的共同特征 標準正態分布&#xff1a;正態分布的特例 中心極限定理 理解定義 示例# 1 示例# 2 知道樣本均值總是正態分布的實際含義是什么&#xff1f; 正態分…

Scala 如何調試隱式轉換--隱式轉換代碼的顯示展示

方法1 在需要隱式轉換的地方&#xff0c;把需要的參數顯示的寫出。 略方法2&#xff0c;查看編譯代碼 在terminal中 利用 scalac -Xprint:typer xxx.scala方法打印添加了隱式值的代碼示例。 對于復雜的工程來說&#xff0c;直接跑到terminal執行 scalac -Xprint:typer xxx.…