您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。