NET Core使用Grpc通信(一):一元請求

来源:https://www.cnblogs.com/LaoPaoEr/p/18105579
-Advertisement-
Play Games

gRPC是一個現代的開源高性能遠程過程調用(RPC)框架,它可以高效地連接數據中心內和跨數據中心的服務,支持負載平衡、跟蹤、運行狀況檢查和身份驗證。 gRPC通過使用 Protocol Buffers 作為數據傳輸格式,實現了在不同平臺上的通信,並支持雙向流和流式傳輸。RPC 是遠程過程調用的縮寫, ...


gRPC是一個現代的開源高性能遠程過程調用(RPC)框架,它可以高效地連接數據中心內和跨數據中心的服務,支持負載平衡、跟蹤、運行狀況檢查和身份驗證。

gRPC通過使用 Protocol Buffers 作為數據傳輸格式,實現了在不同平臺上的通信,並支持雙向流和流式傳輸。RPC 是遠程過程調用的縮寫,實現跨伺服器調用。在開發中,規定調用規則、網路傳輸協議以及數據序列化反序列化規範是確保前後端通信規範性的關鍵。

瞭解GRpc前需要瞭解Rpc概念。

什麼是 RPC 

RPC 是 Remote Procedure Call 的簡稱,中文叫遠程過程調用

說的白話一點,可以這麼理解:比如有兩台伺服器A和B,A伺服器上的應用想調用B伺服器上的另一個應用提供的方法,但由於不在同一個記憶體空間,無法直接調用,所以需要通過網路來實現調用效果。

其實大家在平時開發中有接觸過,例如:前端去請求後端的介面。我們來想一下前後端要制定什麼規則,才能進行介面請求:

  • 調用的語義,也可以理解為介面規範。(比如 RESTful )
  • 網路傳輸協議 (比如 HTTP )
  • 數據序列化反序列化規範(比如 JSON )

只有制定了這些規則,才能保證前後端通信的規範性

什麼是 gRPC交互圖

從上圖中可以看出,RPC 是一種客戶端-服務端(Client/Server)模式。從某種角度來看,所有本身應用程式之外的調用都可以歸類為 RPC。無論是微服務、第三方 HTTP 介面,還是讀寫資料庫中間件 Mysql、Redis。

RPC 特點

  • RPC 是一種協議。RPC實現包括:Dubbo、Thrift、Grpc、Netty等。
  • 網路協議和網路 IO 模型對其透明。RPC 的客戶端認為自己是在調用本地對象,因此其對使用的網路協議( HTTP 協議等)以及網路 IO 模型,是不關心的。
  • 信息格式對其透明。調用方法是需要傳遞參數的,對於遠程調用來說,傳遞過程中參數的信息格式是怎樣構成,以及提供者如何使用這些參數,都是不用關心的。
  • 有跨語言能力。因為調用方實際上也不清楚遠程伺服器的應用程式是使用什麼語言運行的。那麼對於調用方來說,無論伺服器方使用的是什麼語言,本次調用都應該成功,並且返回值也應該按照調用方程式語言所能理解的形式進行描述。

RPC  HTTP 的對比

其實 RPC 跟 HTTP 不是一個層級的東西,RPC 應該是跟 HTTP + RestFul 進行對比。

深入瞭解:HTTP 與 RPC 介面區別

傳輸協議

RPC 可以基於 HTTP 或者 TCP 進行傳輸,而 HTTP 只能基於 HTTP

傳輸效率

RPC 包含了 HTTP2 的優點,所以他的傳輸效率比 HTTP1 更高~

性能消耗

RPC 包含 HTTP2 的優點,比如二進位傳輸、頭部壓縮等,所以性能消耗自然比 HTTP1 低~

負載均衡

RPC 基本都自帶負載均衡策略,而 HTTP 需要配置 Nginx/HAProxy 來完成

服務治理

RPC 能做到自動通知,不影響上游,而 HTTP 需要事先通知,修改 Nginx/HAProxy 配置

gRPC 和 RPC 的關係

Grpc 由谷歌開源的一種 RPC 框架,設計之初便是為瞭解決谷歌內部的 RPC 使用場景所遇到的問題。因此你可以說 gRPC 就是一種 RPC 框架類型。具體來說:

  • RPC是一種編程範式,定義了客戶端像調用本地函數一樣調用遠程函數的方式。
  • gRPC 是 Google 基於 HTTP/2 和 Protocol Buffers 實現的 RPC 框架。
  • gRPC 支持雙向流、流控、頭壓縮等,性能優異。

所以 gRPC 是  RPC 模式的一種高效實現,提供了語言中立、高性能、安全的 RPC 服務框架,使得RPC服務調用更加高效、簡單、通用。它是 RPC 模式的一種優秀代表。

gRPC 的優勢有哪些?

gRPC 是基於 HTTP/2 設計的~所以 gRPC 的優點自然也包含了 HTTP/2 的優點:

  • 數據傳輸二進位分幀
  • 多路復用
  • 服務端推送
  • 頭部壓縮

gRPC的主要優勢及其簡要描述:

優勢 描述
高性能 利用HTTP/2提供高效的網路傳輸,支持雙向流、頭部壓縮、多路復用。
跨語言支持 支持多種編程語言間的無縫通信和集成。
自動化生成代碼 使用Protobuf定義服務,自動生成客戶端和伺服器代碼。
錯誤處理 定義豐富的錯誤碼和狀態碼,便於異常處理和調試。
通信模式多樣 支持多種RPC通信模型,如一對一、服務端流、客戶端流、雙向流等。
可擴展性 攔截器和插件機制允許功能的擴展和定製。
社區和生態系統 擁有活躍的社區支持和豐富的相關工具及庫。

 

gRPC 是怎麼傳輸的?

服務端 Stub 接收客戶端請求,處理請求中的 Protobuf 數據併進行反序列化,然後將請求對象傳入伺服器並實現業務邏輯處理。最終再將響應序列化後返回給客戶端,從而形成一次完整的介面調用過程。

什麼是 gRPC

 

以上概念以及相關知識點來自apifox

NET Core實現Grpc調用

話不多說,以下內容詳細介紹一元調用的過程,貼代碼。

-----------------服務端代碼 Start-----------------

步驟一:

在Grpc服務端(Server)先創建一個.potos文件。文件名(IBook_Service.proto)文件路徑(Protos/IBook_Service.proto)

//表明使用protobuf的編譯器版本為v2,目前最新的版本為v3。
syntax = "proto3";

//定義命名空間
option csharp_namespace = "ZP_BookService_Grpc.Application.Book_Service";

//包名:多個 .proto 文件生成代碼時,可避免命名衝突。
package Book_Service;

//1、定義介面名稱,用於後期實現
service IBook_Service{
 
 // 1.1 根據商品主鍵,獲取商品
 rpc GetBook (BookFrom) returns (BookDto);
}

// 2、定義入參(類)Form:順序要寫,且不能重覆
message BookFrom{
    string BookName = 1; 
}

// 3、定義出參Dto(類):順序要寫,且不能重覆
message BookDto{
    string ID = 1; 
    string CreateTime = 2; 
    string BookName = 3; 
    string BookPrice =4;
    string PublicationDate = 5;
    string Type = 6;
    string Publisher = 7;
    int32  Residue = 8;
}

步驟二:

文件建立好後需要在項目的csproj文件中Project內加上文件的目錄(Protos\IBook_Service.prot) GrpcServices意思是服務端,一定要加上<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Server" />

引用包(Google.Protobuf、Grpc.AspNetCore、Grpc.Tools)

完成以上步驟,右鍵項目選擇 “重新生成”。生成以後可以在項目的obj文件夾(~\Application_Grpc\obj\Debug\net7.0\Protos)看到一個自動生成的Protos文件夾

<ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.26.0" />
    <PackageReference Include="Grpc.AspNetCore" Version="2.61.0" />
    <PackageReference Include="Grpc.Tools" Version="2.62.0" />
</ItemGroup>
<ItemGroup>
	<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Server" />
</ItemGroup>

步驟三:

創建一個實現類(Book_Service)用於實現protos文件夾內自動生成的介面(IBook_Service.IBook_ServiceBase)。註:實現帶有Base的介面。介面名稱是定義proto文件的時候自定義的。

namespace Application_Grpc.Application.BusinessServices
{
    //註入作用域生命周期
    [Service(ServiceLifetime.Scoped)]
    public class Book_Service : IBook_Service.IBook_ServiceBase
    {
        private IBook_Repository _bookRepository { get; }
        private IMapper _mapper { get; }
        public Book_Service(IBook_Repository book_Repository, IMapper mapper)
        {
            _bookRepository = book_Repository;
            _mapper = mapper;
        }
        public override async Task<BookDto> GetBook(BookFrom request, ServerCallContext context)
        {
            BookDto data=new BookDto();
            data.ID = Guid.NewGuid().ToString();
            data.CreateTime = DateTime.Now.ToString();
            data.BookName = request.BookName;
            data.BookPrice = "29.99";
            data.PublicationDate = "1999-03-21";
            data.Type = "經典";
            data.Publisher = "清華大學出版社";
            data.Residue = 5;
            return data;
        }
    }
}

至此,服務端的Grpc就完成了。剩下的就是把項目進行服務依賴註入操作,本實例代碼通過貼特征方式註入。可以在 Program.cs 以常規方式註入自己的服務。

如:builder.Services.AddSingleton<Book_Service>();

-----------------服務端代碼 END-----------------

-----------------客戶端代碼 Start-----------------

步驟一:

步驟一:

在Grpc服務端(Client)先創建一個.potos文件。文件名(IBook_Service.proto)文件路徑(Protos/IBook_Service.proto)。其實就是複製服務端的 proto 修改下 命名空間

//表明使用protobuf的編譯器版本為v2,目前最新的版本為v3。
syntax = "proto3";

//定義命名空間
option csharp_namespace = "ZP_ProjectEntrance.MicroService.Book_Service";

//包名:多個 .proto 文件生成代碼時,可避免命名衝突。
package Book_Service;

//1、定義介面名稱,用於後期實現
service IBook_Service{
 
 // 1.1 根據商品主鍵,獲取商品
 rpc GetBook (BookFrom) returns (BookDto);
}

// 2、定義入參(類)Form:順序要寫,且不能重覆
message BookFrom{
    string BookName = 1; 
}

// 3、定義出參Dto(類):順序要寫,且不能重覆
message BookDto{
    string ID = 1; 
    string CreateTime = 2; 
    string BookName = 3; 
    string BookPrice =4;
    string PublicationDate = 5;
    string Type = 6;
    string Publisher = 7;
    int32  Residue = 8;
}

步驟二:

文件建立好後需要在項目的csproj文件中加上文件的目錄(Protos\IBook_Service.prot) GrpcServices意思是客戶端(Client),一定要加上 <Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Client" />

引用包(Google.Protobuf、Grpc.AspNetCore、Grpc.Tools)

完成以上步驟,右鍵項目選擇 “重新生成”。生成以後可以在項目的obj文件夾(~\ZP_ProjectEntrance\obj\Debug\net7.0\Protos)看到一個自動生成的Protos文件夾

<ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.26.0" />
    <PackageReference Include="Grpc.AspNetCore" Version="2.61.0" />
    <PackageReference Include="Grpc.Tools" Version="2.62.0" />
</ItemGroup>
<ItemGroup>
	<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Client" />
</ItemGroup>

步驟三:重點、重點、重點~

創建一個客戶端類(ClientHelper)用於對接到Grpc的服務端,通過Func型委托形式構建,做成公共的請求服務端入口。

public static class GrpcClientHelper
{
    /// <summary>
    /// 一元rpc調用
    /// </summary>
    /// <typeparam name="TClient">客戶端類型</typeparam>
    /// <typeparam name="TRequest">請求類型  </typeparam>
    /// <typeparam name="TResponse">服務端響應返回類型</typeparam>
    /// <typeparam name="TResult">方法返回類型 </typeparam>
    /// <param name="serverAddress">請求服務端地址</param>
    /// <param name="callFunc">非同步調用 gRPC方法的委托 </param>
    /// <param name="request">封裝請求對象(數據)</param>
    /// <param name="clientFactory">創建 gRPC 客戶端的委托工廠方法  </param>
    /// <returns></returns>
    public static async Task<TResult> CallGrpcServiceAsync<TClient, TRequest, TResponse, TResult>(
        string serverAddress, Func<TClient, TRequest, Task<TResponse>> callFunc, TRequest request, Func<GrpcChannel, TClient> clientFactory)
        where TClient : class
        where TRequest : class
        where TResponse : class
    {
        using var channel = GrpcChannel.ForAddress(serverAddress);
        var client = clientFactory(channel);
        try
        {
            var response = await callFunc(client, request);
            // 這裡添加轉換邏輯,如果 TResponse 不是 TResult,強制類型轉換,需要確保類型相容 
            return (TResult)(object)response;
        }
        catch (RpcException)
        {
            // 處理異常  
            throw;
        }
    }
    /// <summary>
    /// 獲取某個欄位的值
    /// </summary>
    /// <typeparam name="Source"></typeparam>
    /// <param name="source"></param>
    /// <param name="field"></param>
    /// <returns></returns>
    public static object GetFieldValue<Source>(Source source, string field)
    {
        var fieldProperty = source.GetType().GetProperty(field);
        if (fieldProperty != null)
            return fieldProperty.GetValue(source);
        else
            return null;
    }
}

 步驟四:

通過Controllers定義的方法,以請求方法形式進行調用到GrpcClientHelper

namespace ZP_ProjectEntrance.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooKController : ControllerBase
    {
        private readonly ILogger<BooKController> _logger;
        public BooKController(ILogger<BooKController> logger)
        {
            _logger = logger;
        }
        /// <summary>
        /// 獲取書籍信息
        /// </summary>
        /// <returns></returns>
        [HttpGet(Name = "GetBook")]
        public async Task<BookDto> GetBookAsync()
        {
            // 服務端地址,可以擴展為請求分散式集群
            string serverAddress = "http://localhost:5031";
            // 使用 GrpcClientHelper 來調用 gRPC 服務 ,  
            BookDto bookDto = await GrpcClientHelper.CallGrpcServiceAsync<IBook_Service.IBook_ServiceClient, BookFrom, BookDto, BookDto>(
                serverAddress,
                async (client, request) => await client.GetBookAsync(request),  // 非同步調用 gRPC 方法的委托  
                new BookFrom { BookName = "三國演義" },                          // 封裝請求對象(值)
                (channel) => new IBook_Service.IBook_ServiceClient(channel)     // 實現 gRPC 客戶端的委托方法  
            );
            _logger.LogInformation(JsonConvert.SerializeObject(bookDto));
            return bookDto;
        }
    }
}

至此,客戶端的Grpc就完成了。

-----------------客戶端代碼 END-----------------

項目的結構

附:貼特征形式實現依賴註入的代碼,通過反射機制實現。

註:業務層和倉儲層必須是獨立層項目,或相同形式進行隔離(要麼統一介面I_BLL、I_DLL,要麼單純的BLL、DLL),否則自行對自動註冊類進行改造。

代碼可以寫在公共層 ExternalService 項目中,作為基礎服務進行引用。

using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace ExternalService.RegisterServices
{
    /// <summary>
    /// 通過反射機制自動化進行依賴註入服務
    /// </summary>
    public static class ServiceCollectionExtension
    {
        /// <summary>
        /// 註冊介面類型服務,繼承介面(貼特征)
        /// </summary>
        /// <param name="services">this服務</param>
        /// <param name="assembly">程式集</param>
        /// <returns></returns>
        public static IServiceCollection RegisterIntfaceTypeService(this IServiceCollection services, Assembly assembly)
        {
            var interfaces = assembly.GetTypes().Where(t => t.IsInterface).ToList();
            var types = assembly.GetTypes().Where(t => t.IsClass).ToList();

            foreach (var interf in interfaces)
            {
                if (interf == null)
                    continue;

                var type = types.FirstOrDefault(interf.IsAssignableFrom);
                if (type == null)
                    continue;

                var liftTime = ServiceLifetime.Scoped;
                var attr = type.GetCustomAttribute<ServiceAttribute>();
                if (attr != null)
                    liftTime = attr.LifeTime;
                else
                    continue;

                switch (liftTime)
                {
                    default:
                    case ServiceLifetime.Scoped:
                        {
                            //作用生命周期:同一請求之間狀態共用,跟隨HTTP請求生命周期
                            services.AddScoped(interf, type);
                            break;
                        }
                    case ServiceLifetime.Transient:
                        {
                            //瞬時生命周期:無狀態化,每次使用是 new ()
                            services.AddTransient(interf, type);
                            break;
                        }
                    case ServiceLifetime.Singleton:
                        {
                            //單例生命周期:整個程式所有請求狀態共用,整個程式只有一個實例
                            services.AddSingleton(interf, type);
                            break;
                        }
                }
            }
            return services;
        }
        /// <summary>
        /// 註冊普通類服務,非介面類型(貼特征)
        /// </summary>
        /// <param name="services">this服務</param>
        /// <param name="assembly">程式集</param>
        /// <param name="NamespaceKeyWord">命名空間關鍵字</param>
        /// <returns></returns>
        public static IServiceCollection RegisterClassService(this IServiceCollection services, Assembly assembly, string NamespaceKeyWord = "")
        {
            var ClassTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract).ToList();
            if (!string.IsNullOrEmpty(NamespaceKeyWord))
                ClassTypes = assembly.GetTypes().Where(t => t.Name.Contains(NamespaceKeyWord)).ToList();
            foreach (var types in ClassTypes)
            {
                if (types == null)
                    continue;

                var liftTime = ServiceLifetime.Scoped;
                var attr = types.GetCustomAttribute<ServiceAttribute>();
                if (attr != null)
                    liftTime = attr.LifeTime;
                else
                    continue;

                switch (liftTime)
                {
                    default:
                    case ServiceLifetime.Scoped:
                        {
                            //作用生命周期:同一請求之間狀態共用,跟隨HTTP請求生命周期
                            services.AddScoped(types);
                            break;
                        }
                    case ServiceLifetime.Transient:
                        {
                            //瞬時生命周期:無狀態化,每次使用是 new ()
                            services.AddTransient(types);
                            break;
                        }
                    case ServiceLifetime.Singleton:
                        {
                            //單例生命周期:整個程式所有請求狀態共用,整個程式只有一個實例
                            services.AddSingleton(types);
                            break;
                        }
                }
            }
            return services;
        }
    }

    /// <summary>
    /// 生命周期特征
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ServiceAttribute : Attribute
    {
        internal ServiceLifetime LifeTime { get; set; }

        public ServiceAttribute(ServiceLifetime lifeTime) => LifeTime = lifeTime;
    }
}

在使用的項目程式集內創建一個擴展服務 AddApplication_Register。然後調用註冊類擴展服務services.RegisterClassService(Assembly.GetExecutingAssembly());

using System.Reflection;

using ExternalService.RegisterServices;
using Microsoft.Extensions.DependencyInjection;

namespace Application_Grpc
{
    public static class Application_GrpcExtension
    {
        public static IServiceCollection AddApplication_Register(this IServiceCollection services)
        {
            services.RegisterClassService(Assembly.GetExecutingAssembly());
            return services;
        }
    }
}

在Program.cs內註冊添加項目程式集的擴展服務,把上面Application_GrpcExtension類的AddApplication_Register進行調用:builder.Services.AddApplication_Register();


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面我們使用了IIncrementalGenerator來生成代碼,接下來我們來詳細瞭解下IIncrementalGenerator的核心部分IncrementalValueProvider。 介紹 IncrementalValueProvider是基於管道的模式,將我們需要的數據進行處理轉換後傳遞 ...
  • 前言 在WPF應用程式開發中,我們可以藉助其強大靈活的設計能力打造出絢麗而富有創意的用戶界面。然而,與這種高度定製化的界面相比,標準MessageBox卻顯得有些原始和古老。它的外觀與現代、絢麗的應用界面格格不入,使得用戶在交互中可能感到突兀或不符合預期。 本文將深入探討如何在WPF中封裝自定義Me ...
  • socket在接收數據時,經常會因為網路延遲、緩存區數據處理不及時等原因造成收到的數據是多個包黏在一起的情況。如下圖所示 圖中紅色框部分是通訊的 心跳包 圖中黃色框部分和未框選部分是 兩包 數據包 所以可見此時緩存區裡面同時存在了一個心跳包,兩個數據包 如何分包(此處僅針對幾個完整的數據包在一起的情 ...
  • 首先看完成效果 一個玩家的效果 多個玩家的效果 源碼地址 https://gitee.com/chesterdotchen/snake-with-orleans 項目介紹 Snake.Common項目 IGameGrain:游戲的Grain定義,與State定義 ISnakeGrain:蛇的Grai ...
  • 本章將和大家分享 Elasticsearch 中的數據聚合功能,通過聚合(aggregations)可以實現對文檔數據的統計、分析、運算。 ...
  • 工具類擴展 1. ILSpy 2022 (免費) ILSpy 是 ILSpy 開源反編譯器的 Visual Studio 擴展。 是一款開源、免費的、且適用於.NET平臺反編譯【C#語言編寫的程式和庫(.dll)內容】工具;可以集成在Visual Studio 開發工具中,能夠十分快捷方便的查看源代 ...
  • 前言 WPF中Window相信大家都很熟悉,有時我們有一些自定義需求預設Window是無法滿足的,比如在標題欄上放一些自己東西,這個時候我們就需要寫一個自己的Window,實現起來也很簡單,只要給Window設置一個WindowChrome.WindowChrome附加屬性就可以實現,WindowC ...
  • 前言 上文介紹瞭如何通過一個Form自定義控制項來簡化數據的錄入,並自動實現數據校驗,自動佈局排列等功能。本文繼續介紹如何優化表格控制項的使用,縮減代碼量,實現工作效率的提升。 一、功能實現 上文中分析了DataGrid跟ListView兩種表格控制項的優劣,在這裡我們選擇ListView來實現我們的表格 ...
一周排行
    -Advertisement-
    Play Games
  • 隨著Aspire發佈preview5的發佈,Microsoft.Extensions.ServiceDiscovery隨之更新, 服務註冊發現這個屬於老掉牙的話題解決什麼問題就不贅述了,這裡主要講講Microsoft.Extensions.ServiceDiscovery(preview5)以及如何 ...
  • 概述:通過使用`SemaphoreSlim`,可以簡單而有效地限制非同步HTTP請求的併發量,確保在任何給定時間內不超過20個網頁同時下載。`ParallelOptions`不適用於非同步操作,但可考慮使用`Parallel.ForEach`,儘管在非同步場景中謹慎使用。 對於併發非同步 I/O 操作的數量 ...
  • 1.Linux上安裝Docken 伺服器系統版本以及內核版本:cat /etc/redhat-release 查看伺服器內核版本:uname -r 安裝依賴包:yum install -y yum-utils device-mapper-persistent-data lvm2 設置阿裡雲鏡像源:y ...
  • 概述:WPF界面綁定和渲染大量數據可能導致性能問題。通過啟用UI虛擬化、非同步載入和數據分頁,可以有效提高界面響應性能。以下是簡單示例演示這些優化方法。 在WPF中,當你嘗試綁定和渲染大量的數據項時,性能問題可能出現。以下是一些可能導致性能慢的原因以及優化方法: UI 虛擬化: WPF提供了虛擬化技術 ...
  • 引言 上一章節介紹了 TDD 的三大法則,今天我們講一下在單元測試中模擬對象的使用。 Fake Fake - Fake 是一個通用術語,可用於描述 stub或 mock 對象。 它是 stub 還是 mock 取決於使用它的上下文。 也就是說,Fake 可以是 stub 或 mock Mock - ...
  • 為.net6在CentOS7上面做準備,先在vmware虛擬機安裝CentOS 7.9 新建CentOS764位的系統 因為CentOS8不更新了,所以安裝7;簡單就一筆帶過了 選擇下載好的操作系統的iso文件,下載地址https://mirrors.aliyun.com/centos/7.9.20 ...
  • 經過前面幾篇的學習,我們瞭解到指令的大概分類,如:參數載入指令,該載入指令以 Ld 開頭,將參數載入到棧中,以便於後續執行操作命令。參數存儲指令,其指令以 St 開頭,將棧中的數據,存儲到指定的變數中,以方便後續使用。創建實例指令,其指令以 New 開頭,用於在運行時動態生成並初始化對象。方法調用指... ...
  • LiteDB 是一個輕量級的嵌入式 NoSQL 資料庫,其設計理念與 MongoDB 類似,但它是完全使用 C# 開發的,因此與 C# 應用程式的集成非常順暢。與 SQLite 相比,LiteDB 提供了 NoSQL(即鍵值對)的數據存儲方式,並且是一個開源且免費的項目。它適用於桌面、移動以及 We ...
  • 1 開源解析和拆分文檔 第三方的工具去對文件解析拆分,去將我們的文件內容給提取出來,並將我們的文檔內容去拆分成一個小的chunk。常見的PDF word mark down, JSON、HTML。都可以有很好的一些模塊去把這些文件去進行一個東西去提取。 優勢 支持豐富的文檔類型 每種文檔多樣化選擇 ...
  • OOM是什麼?英文全稱為 OutOfMemoryError(記憶體溢出錯誤)。當程式發生OOM時,如何去定位導致異常的代碼還是挺麻煩的。 要檢查OOM發生的原因,首先需要瞭解各種OOM情況下會報的異常信息。這樣能縮小排查範圍,再結合異常堆棧、heapDump文件、JVM分析工具和業務代碼來判斷具體是哪 ...