最近帮老师做项目,需要用C#作为客户端做传输大量数据给Java服务端,之前的代码是用原生Socket写的,Java端这边非常的冗余复杂,难以维护,因此想到使用rpc框架来实现,方便后续的扩展和维护。
查阅资料发现thrift框架提供了完整的序列化和传输。(考虑效率也可使用protobuf+grpc,文档更加丰富)
关于rpc的框架对比,见:https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html
https://blog.csdn.net/z69183787/article/details/52605653
thrift这个框架的文档太少了,想真的把框架落地使用太难了5555......网上资料参差不齐,但是整了很久也算搞出来了,因此记录一下整个过程和走过的坑。
我是在本地测试的C#和Java都在windows端,首先安装thrift。
各个版本的链接如下:http://archive.apache.org/dist/thrift/
dll
我这里下的是 thrift-0.10.0.tar.gz 版本,环境是VS2019以及.NET 4.5,打开工程:thrift-0.10.0\lib\csharp\src\Thrift.sln,重新生成,编译,即可生成Thrift.dll(位置在thrift-0.10.0\lib\csharp\src\bin\Debug\Thrift.dll),后面编写代码的时候需要引用这个dll。
dat.drift
新建dat.drift文件,使用IDL对接口进行描述,用于生成.cs和.java代码。这一步需要用到Linux系统环境(没有的话建议使用虚拟机装Linux环境),因为我在Win10环境下按照网上的方法是无法生成对应的代码文件的。
IDL描述语言可以参考:https://diwakergupta.github.io/thrift-missing-guide/#_types
https://gitbox.apache.org/repos/asf?p=thrift.git;a=blob;hb=HEAD;f=tutorial/tutorial.thrift
示例如下:
定义了一个结构体和一个service方法
struct Dat{
1: bool isColDat
2: list<byte> data
}
service SendDatService{
oneway void send(1:Dat dat)
}
这里的oneway表示方法调用不需要返回值,这样的话提高了调用的效率,但是可靠性不能保证。
在linux环境下安装thrift,参考:https://blog.csdn.net/huangbaokang/article/details/80065606
安装成功后,使用thrift生成.cs文件,在Linux命令行中执行
thrift -r --gen csharp dat.thrift
当前目录下会生成gen-csharp目录,里面是.cs文件,在编码时需要拷贝到C#工程目录下。
在Linux命令行中执行
thrift -r --gen java dat.thrift
当前目录下会生成gen-java目录,里面是.java文件,在编码时需要拷贝到Java工程目录下。
客户端代码
C#新建项目,引入前面生成Thrift.dll,复制前面生成的.cs文件到项目中,主程序示例代码如下。
using System;
using System.Collections.Generic;
using Thrift.Protocol;
using Thrift.Transport;
namespace IBAClientLT
{
static class Program
{
public const string SERVERIP = "localhost";
public static int SERVERPORT = 9000;
public static int TIMEOUT = 5000;
[STAThread]
static void Main()
{
TTransport transport = null;
try
{
//transport = new TSocket(SERVERIP, SERVERPORT, TIMEOUT);
transport = new TFramedTransport(new TSocket(SERVERIP, SERVERPORT, TIMEOUT));
//协议要和服务端一致
TProtocol protocol = new TCompactProtocol(transport);
SendDatService.Client client = new SendDatService.Client(protocol);
transport.Open();
Dat d = new Dat();
d.IsColDat = false;
List<sbyte> bt = new List<sbyte>();
bt.Add(1);
bt.Add(2);
bt.Add(3);
d.Data = bt;
client.send(d);//远程调用
client.Dispose();
Console.ReadKey();//保持命令行窗口不关闭
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
finally
{
if (null != transport)
{
//close
transport.Close();
}
}
}
}
}
服务端代码
Java新建Maven项目,pom.xml文件添加依赖
<!-- https://mvnrepository.com/artifact/org.apache.thrift/libthrift -->
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
<!--<type>pom</type>-->
</dependency>
继承我们定义的service接口,并且实现接口中的方法,这里我们打印出来内容。
package thrift;
import org.apache.thrift.TException;
public class SendDatServiceImpl implements SendDatService.Iface {
@Override
public void send(Dat dat) throws TException {
System.out.println("iscolDat: "+dat.isColDat);
System.out.println("data: "+dat.data);
}
}
服务端主程序。
package thrift;
import org.apache.thrift.TNonblockingMultiFetchClient;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;
public class ThriftServer {
public static void main(String[] args) throws TTransportException {
TNonblockingServerSocket socket =new TNonblockingServerSocket(9000);
THsHaServer.Args arg =new THsHaServer.Args(socket).minWorkerThreads(1).maxWorkerThreads(4);
SendDatService.Processor<SendDatServiceImpl> processor=
new SendDatService.Processor<SendDatServiceImpl>(new SendDatServiceImpl());
arg.protocolFactory(new TCompactProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server =new THsHaServer(arg);
System.out.println("server start");
server.serve();
}
}