# C#怎么使用Thrift作為RPC框架
## 1. Thrift簡介
Apache Thrift是由Facebook開發并貢獻給Apache基金會的高性能、跨語言的RPC(遠程過程調用)框架。它通過IDL(接口定義語言)定義服務接口,然后自動生成多種編程語言的客戶端和服務端代碼,使不同語言開發的系統能夠無縫通信。
### 核心特點
- **跨語言支持**:支持C++, Java, Python, PHP, C#, Go等20+語言
- **高性能二進制協議**:比JSON/XML更高效的序列化
- **多種傳輸協議**:TCP/HTTP/WebSocket等
- **服務治理能力**:支持連接池、負載均衡等
## 2. 環境準備
### 2.1 安裝Thrift編譯器
1. 從[Apache Thrift官網](https://thrift.apache.org/)下載對應系統的編譯器
2. Windows用戶推薦使用預編譯的exe版本
3. 將thrift.exe路徑加入系統PATH環境變量
驗證安裝:
```bash
thrift --version
<!-- 在.csproj中添加NuGet包引用 -->
<ItemGroup>
<PackageReference Include="Thrift" Version="0.17.0" />
<PackageReference Include="Thrift.Compiler" Version="0.13.0" />
</ItemGroup>
創建calculator.thrift
文件:
namespace csharp CalculatorService
service Calculator {
i32 Add(1:i32 num1, 2:i32 num2),
double Divide(1:double dividend, 2:double divisor) throws (1:InvalidOperationException e),
list<string> GetOperationsHistory()
}
exception InvalidOperationException {
1: string message,
2: int errorCode
}
namespace
定義生成的代碼包名service
定義RPC服務接口i32
, double
, string
, bool
等list<T>
, map<K,V>
, set<T>
throws
聲明可能拋出的異常使用Thrift編譯器生成代碼:
thrift -gen csharp calculator.thrift
生成的文件結構:
gen-csharp/
└── CalculatorService/
├── Calculator.cs # 服務接口
├── CalculatorAsync.cs # 異步接口
└── InvalidOperationException.cs # 異常類
public class CalculatorHandler : Calculator.IAsync
{
private readonly List<string> _history = new();
public async Task<int> AddAsync(int num1, int num2, CancellationToken cancellationToken)
{
var result = num1 + num2;
_history.Add($"Add: {num1} + {num2} = {result}");
return await Task.FromResult(result);
}
public async Task<double> DivideAsync(double dividend, double divisor, CancellationToken cancellationToken)
{
if (Math.Abs(divisor) < double.Epsilon)
{
throw new InvalidOperationException
{
Message = "Cannot divide by zero",
ErrorCode = 400
};
}
var result = dividend / divisor;
_history.Add($"Divide: {dividend} / {divisor} = {result}");
return result;
}
public async Task<List<string>> GetOperationsHistoryAsync(CancellationToken cancellationToken)
{
return await Task.FromResult(_history);
}
}
using var handler = new CalculatorHandler();
var processor = new Calculator.AsyncProcessor(handler);
var serverTransport = new TServerSocket(port: 9090);
var server = new TThreadPoolServer(processor, serverTransport);
Console.WriteLine("Starting the server...");
server.Serve();
TThreadPoolServer
:線程池模型(默認)TNonblockingServer
:非阻塞IOTHsHaServer
:半同步半異步using var transport = new TSocket("localhost", 9090);
using var protocol = new TBinaryProtocol(transport);
var client = new Calculator.Client(protocol);
transport.Open();
try
{
int sum = client.Add(10, 20);
Console.WriteLine($"10 + 20 = {sum}");
double quotient = client.Divide(100, 3);
Console.WriteLine($"100 / 3 = {quotient:F2}");
}
catch (InvalidOperationException e)
{
Console.WriteLine($"Error {e.ErrorCode}: {e.Message}");
}
using var transport = new TSocket("localhost", 9090);
using var protocol = new TBinaryProtocol(transport);
var client = new Calculator.AsyncClient(protocol);
await transport.OpenAsync();
try
{
var history = await client.GetOperationsHistoryAsync(CancellationToken.None);
Console.WriteLine("History: " + string.Join(", ", history));
}
catch (TTransportException e)
{
Console.WriteLine($"Network error: {e.Message}");
}
// 服務端
var transport = new TServerSocket(port: 9090);
var transFactory = new TFramedTransport.Factory();
var protoFactory = new TBinaryProtocol.Factory();
var server = new TThreadPoolServer(
new Calculator.Processor(handler),
transport,
transFactory,
protoFactory);
var protocol = new TJSONProtocol(transport);
public class ThriftClientPool<T> where T : class
{
private readonly ConcurrentQueue<T> _pool = new();
private readonly Func<T> _factory;
public ThriftClientPool(Func<T> factory, int initialCount = 5)
{
_factory = factory;
for (int i = 0; i < initialCount; i++)
{
_pool.Enqueue(factory());
}
}
public T GetClient()
{
if (_pool.TryDequeue(out var client))
return client;
return _factory();
}
public void ReturnClient(T client)
{
_pool.Enqueue(client);
}
}
TZlibTransport
ThriftClientPool
var transport = new TSocket("localhost", 9090)
{
Timeout = 5000 // 5秒超時
};
確保服務端和客戶端使用相同的協議和傳輸方式:
// 服務端
var protoFactory = new TCompactProtocol.Factory();
// 客戶端必須匹配
var protocol = new TCompactProtocol(transport);
當修改IDL后: 1. 重新生成所有語言的代碼 2. 滾動升級服務(先升級服務端)
TLogging.Logger = new ConsoleLogger(LogLevel.Info);
var transport = new TInstrumentedTransport(socketTransport);
Console.WriteLine($"Bytes sent: {transport.BytesWritten}");
CalculatorRpc/
├── CalculatorService/
│ ├── calculator.thrift
│ └── gen-csharp/ (生成的代碼)
├── Server/
│ ├── Program.cs
│ └── CalculatorHandler.cs
├── Client/
│ └── Program.cs
└── Shared/
└── ThriftClientPool.cs
特性 | Thrift | gRPC | WCF |
---|---|---|---|
跨語言支持 | ? | ? | × |
HTTP/2 | × | ? | ? |
二進制協議 | ? | ? | ? |
開發便捷性 | 中 | 高 | 高 |
.NET集成度 | 中 | 高 | 極高 |
Thrift在C#中的使用流程: 1. 定義IDL接口 2. 生成客戶端/服務端代碼 3. 實現服務邏輯 4. 配置傳輸協議 5. 啟動服務并調用
適合場景: - 多語言系統集成 - 高性能內部服務調用 - 需要自定義協議的復雜場景
通過本文的詳細指南,您應該已經掌握了在C#中使用Thrift進行RPC開發的核心技能。實際項目中還需結合具體需求選擇合適的配置和優化策略。 “`
這篇文章共計約2600字,涵蓋了從環境搭建到高級使用的完整流程,包含: - 基礎概念介紹 - 詳細操作步驟 - 代碼示例 - 最佳實踐 - 問題排查 - 方案對比
可根據實際需求調整細節或補充特定場景的示例。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。