C#使用远程服务调用框架Apache Thrift

Apache Thrift 是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架。和其它RPC框架相比,它主要具有如下连个特点:

  • 高性能。 它采用的是二进制序列化,并且用的是长连接。比传统的使用XML,SOAP,JSON等短连接的解决方案要快得多。
  • 多语言支持。 它对提供了对C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk等多种常用语言的支持

正因为如此,Thrift对于高并发、大数据量和多语言的环境是非常有优势的,本文就简单的介绍一下如何使用它。

使用Thrift和传统的Corba之类的RPC框架并无太大差异,主要分为如下三个部分:

  • 使用IDL定义提供的服务:主要用于服务接口和数据传输对象(DTO)并且有一套自己的语法, Thrift中命名为thirft文件,并且有一套自己的语法。
  • 将IDL编译成框架代码
  • 客户端使用框架代码调用远程服务
  • 服务端使用框架代码实现接口,并提供服务

和传统RPC框架一样,Thrift框架提供数据传输的服务,服务端和客户端只需要关注业务即可;这一系列流程并没有多大新颖的地方。

准备工作:

需要在Thrift官网下载两个文件:

  • IDL编译工具。官方本身提供了Windows的exe版本:thrift-0.9.2.tar.gz
  • Thrift类库。Thrift底层框架,提供了底层的数据传输服务。

下载完后,由于Thrift类库是以源码形式提供的,因此需要自己编译。为了简单,这里以C#为例,打开 "\lib\csharp\src\Thrift.sln" 工程文件,直接编译即可生成Thrift.dll文件。官方说.net版本.net 3.5及以上即可。

示例项目:

Thrift本身提供了多多种语言的支持,这里示例项目仍以官网的CSharp Tutorial为例来介绍一下如何在C#中使用Thrift。

首先下载接口定义文件 tutorial.thrift 和shared.thrift 。官方的文档中加了一大堆注释,实际上还是比较简单的,主体部分如下:

service Calculator extends shared.SharedService
{
   void ping(),
   i32 add(1:i32 num1, 2:i32 num2),
   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
   oneway void zip()
}

主要就是定义了一个这样的接口,提供了四个接口。以及一些相关的结构体的定义。定义好接口后,调用Thrift.exe将其编译成C#的代码:

thrift -r --gen csharp tutorial.thrift

编译完后,会生成一个gen-csharp文件夹,里面就是相关的结构体和接口的定义,和一些框架的实现,具体的后续再讲。这个文件夹里的文件大部分都是服务端和客户端都要用的,为了简单起见,大可以建立一个ShareProject(低版本的VS不支持的话可以建立一个独立的类库项目),以方便客户端和服务端引用。

客户端:

首先还是来看看客户端代码(运行这段代码需要加上前面编译的Thrift.dll和编译idl文件生成的一堆C#代码):

using System;
using Thrift;
using Thrift.Protocol;
using Thrift.Server;
using Thrift.Transport;

namespace CSharpTutorial
{
    public class CSharpClient
    {
        public static void Main()
        {
            try
            {
                TTransport transport = new TSocket("localhost", 9090);
                TProtocol protocol = new TBinaryProtocol(transport);
                Calculator.Client client = new Calculator.Client(protocol);

                transport.Open();
                try
                {
                    client.ping();
                    Console.WriteLine("ping()");

                    int sum = client.add(1, 1);
                    Console.WriteLine("1+1={0}", sum);

                    Work work = new Work();

                    work.Op = Operation.DIVIDE;
                    work.Num1 = 1;
                    work.Num2 = 0;
                    try
                    {
                        int quotient = client.calculate(1, work);
                        Console.WriteLine("Whoa we can divide by 0");
                    }
                    catch (InvalidOperation io)
                    {
                        Console.WriteLine("Invalid operation: " + io.Why);
                    }

                    work.Op = Operation.SUBTRACT;
                    work.Num1 = 15;
                    work.Num2 = 10;
                    try
                    {
                        int diff = client.calculate(1, work);
                        Console.WriteLine("15-10={0}", diff);
                    }
                    catch (InvalidOperation io)
                    {
                        Console.WriteLine("Invalid operation: " + io.Why);
                    }

                    SharedStruct log = client.getStruct(1);
                    Console.WriteLine("Check log: {0}", log.Value);

                }
                finally
                {
                    transport.Close();
                }
            }
            catch (TApplicationException x)
            {
                Console.WriteLine(x.StackTrace);
            }

        }
    }
}

官方的例子简化一下后如下:

    using (TTransport transport = new TSocket("localhost", 9090))
    using (TProtocol protocol = new TBinaryProtocol(transport))
    using (Calculator.Client client = new Calculator.Client(protocol))
    {
        transport.Open();
        int sum = client.add(1, 1);
        Console.WriteLine("1+1={0}", sum);
    }

主要就设计到了三个类:TTransport、TProtocol和Calculator.Client,其中TTransport、Tprotocol是用于传输控制的,和业务无关,是在Thrift.dll中定义的。而Calculator.Client则是客户端的Proxy,用于执行RPC,和业务相关,是由idl文件编译生成。客户端无需编写代码,即可拥有RPC能力。(这个特效基本上所有RPC框架都具有)

服务端:

服务端示例代码如下:

using System;
using System.Collections.Generic;
using Thrift.Server;
using Thrift.Transport;

namespace CSharpTutorial
{
    public class CalculatorHandler : Calculator.Iface
    {
        Dictionary<int, SharedStruct> log;

        public CalculatorHandler()
        {
            log = new Dictionary<int, SharedStruct>();
        }

        public void ping()
        {
            Console.WriteLine("ping()");
        }

        public int add(int n1, int n2)
        {
            Console.WriteLine("add({0},{1})", n1, n2);
            return n1 + n2;
        }

        public int calculate(int logid, Work work)
        {
            Console.WriteLine("calculate({0}, [{1},{2},{3}])", logid, work.Op, work.Num1, work.Num2);
            int val = 0;
            switch (work.Op)
            {
                case Operation.ADD:
                    val = work.Num1 + work.Num2;
                    break;

                case Operation.SUBTRACT:
                    val = work.Num1 - work.Num2;
                    break;

                case Operation.MULTIPLY:
                    val = work.Num1 * work.Num2;
                    break;

                case Operation.DIVIDE:
                    if (work.Num2 == 0)
                    {
                        InvalidOperation io = new InvalidOperation();
                        io.What = (int)work.Op;
                        io.Why = "Cannot divide by 0";
                        throw io;
                    }
                    val = work.Num1 / work.Num2;
                    break;

                default:
                    {
                        InvalidOperation io = new InvalidOperation();
                        io.What = (int)work.Op;
                        io.Why = "Unknown operation";
                        throw io;
                    }
            }

            SharedStruct entry = new SharedStruct();
            entry.Key = logid;
            entry.Value = val.ToString();
            log[logid] = entry;

            return val;
        }

        public SharedStruct getStruct(int key)
        {
            Console.WriteLine("getStruct({0})", key);
            return log[key];
        }

        public void zip()
        {
            Console.WriteLine("zip()");
        }
    }

    public class CSharpServer
    {
        public static void Main()
        {
            try
            {
                CalculatorHandler handler = new CalculatorHandler();
                Calculator.Processor processor = new Calculator.Processor(handler);
                TServerTransport serverTransport = new TServerSocket(9090);
                TServer server = new TSimpleServer(processor, serverTransport);

                // Use this for a multithreaded server
                // server = new TThreadPoolServer(processor, serverTransport);

                Console.WriteLine("Starting the server...");
                server.Serve();
            }
            catch (Exception x)
            {
                Console.WriteLine(x.StackTrace);
            }
            Console.WriteLine("done.");
        }
    }
}

官方代码基本上包括两个部分:接口实现和服务的启动,简化后如下:

    public class CalculatorHandler : Calculator.Iface
    {
        public void ping()
        {
        }

        public int add(int n1, int n2)
        {
            return n1 + n2;
        }

        public int calculate(int logid, Work work)
        {
            throw new InvalidOperation();
        }

        public SharedStruct getStruct(int key)
        {
            return new SharedStruct();
        }

        public void zip()
        {
        }
    }

接口实现依赖于IDL生成的Calculator.Iface接口,这里我就实现了一个add接口。再来看看服务端

    static void Main(string[] args)
    {
        CalculatorHandler handler = new CalculatorHandler();
        Calculator.Processor processor = new Calculator.Processor(handler);

        TServerTransport serverTransport = new TServerSocket(9090);
        TServer server = new TSimpleServer(processor, serverTransport);

        Console.WriteLine("Starting the server...");
        server.Serve();
    }

服务端主要的功能就是将刚才的接口作为服务加载起来,并启动服务,也是比较常规的做法,没有太多需要介绍的地方

从这个Demo来看,主要过程和其它RPC框架的流程倒也没有太大区别。一个比较出彩的地方是Thrift是提供了原生的.net运行库的(前面编译的Thrift.dll),作为一个跨语言的框架不用Pinvoke非常给力,给部署带来不少方便。本来高性能才是它的主要特点,后续有空再测试一下。

最后,本文只是一个简单的入门体验,如果需要更多信息,请参看Thrift的官方网站

到此这篇关于C#使用远程服务调用框架Apache Thrift的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Apache Thrift环境配置

    安装Thrift的官方文档地址: http://thrift.apache.org/docs/install/ 当我看到windows安装需要Cygwin或MinGW时,我就直接放弃在windows中配置的想法了,直接打开虚拟机用CentOS进行安装,使用一堆命令安装毕竟比windows方便. CentOS安装Thrift 官方文档地址: http://thrift.apache.org/docs/install/centos 基本上按照官方的操作,可以一直进行到最后一组命令,就是下面这个地方:

  • 利用thrift实现js与C#通讯的实例代码

    1.为什么要用thrift js C#? 1.1 首先,js 通过 thrift 访问C#,实际上是一种c/s模式.thrift是通信工具,js是客户端,C#是服务端. 1.2 使用js直接与thrift server通信.让web开发变得更简单.如果使用Web Service,你需要自己去实现C/S两端的序列化与反序列化操作,还需要自行处理异常,降低了开发效率.而thrift则会自动生成两端的操作类,你只需要处理方法内部的逻辑即可. 1.3 js直接与thrift server通信,可以提高性

  • C#使用Thrift作为RPC框架入门详细教程

    前言 本文将介绍由 Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的.无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发.大数据量和多语言的环境更有优势.本文将详细介绍 Thri

  • C#使用远程服务调用框架Apache Thrift

    Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.和其它RPC框架相比,它主要具有如下连个特点: 高性能. 它采用的是二进制序列化,并且用的是长连接.比传统的使用XML,SOAP,JSON等短连接的解决方案要快得多. 多语言支持. 它对提供了对C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk等多种常用语言的支持 正因为如此,Thrift对于高并

  • JavaScript 异步调用框架 (Part 5 - 链式实现)

    调用入口 链式调用存在Async.go方法和Async.chain方法两个入口,这两个入口本质上是一致的,只是Async.chain方法在调用时先不提供初始参数,而Async.go方法在调用时提供了初始参数并启动异步调用链. 复制代码 代码如下: Async.chain = function() { var chain = new Async.Operation({ chain: true }); return chain; }; Async.go = function(initialArgum

  • JavaScript 异步调用框架 (Part 1 - 问题 & 场景)

    问题 在Ajax应用中,调用XMLHttpRequest是很常见的情况.特别是以客户端为中心的Ajax应用,各种需要从服务器端获取数据的操作都通过XHR异步调用完成.然而在单线程的JavaScript编程中,XHR异步调用的代码风格实在是与一般的JavaScript代码格格不入. 额外参数 考虑一个除法函数,如果它是纯客户端的同步函数,那么签名会是这样的: function divide(operand1, operand2) 然而假设我们对客户端除法的精度不满意,于是把除法转移到服务器端来执行

  • JavaScript 异步调用框架 (Part 3 - 代码实现)

    类结构 首先我们来搭一个架子,把需要用到的似有变量都列出来.我们需要一个数组,来保存回调函数列表:需要一个标志位,来表示异步操作是否已完成:还可以学IAsyncResult,加一个state,允许异步操作的实现者对外暴露自定义的执行状态:最后加一个变量保存异步操作结果. 复制代码 代码如下: Async = { Operation: { var callbackQueue = []; this.result = undefined; this.state = "waiting"; th

  • JavaScript 异步调用框架 (Part 4 - 链式调用)

    现实开发中,要按顺序执行一系列的同步异步操作又是很常见的.还是用百度Hi网页版中的例子,我们先要异步获取联系人列表,然后再异步获取每一个联系人的具体信息,而且后者是分页获取的,每次请求发送10个联系人的名称然后取回对应的具体信息.这就是多个需要顺序执行的异步请求. 为此,我们需要设计一种新的操作方式来优化代码可读性,让顺序异步操作代码看起来和传统的顺序同步操作代码一样优雅. 传统做法 大多数程序员都能够很好的理解顺序执行的代码,例如这样子的: 复制代码 代码如下: var firstResult

  • SpringCloud远程服务调用实战笔记

    笔记 在微服务中,若想要使用远程调用,需要引入spring-cloud-starter-openfeign(在使用注册中心的环境下) <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>xxx</version> </depe

  • SpringCloud通过Nacos实现注册中心与远程服务调用详解流程

    目录 1. 基于Nacos实现服务注册与发现 1.1 pom依赖 1.2 yaml配置 1.3 添加启动注解 1.4 启动服务查看控制台 2.基于Nacos实现远程服务调用 2.1 客户端创建RestTemplate Bean 2.2 客户端调用代码 2.3 服务端暴露接口 2.4 服务调用测试 本文主要记录基于Nacos实现服务注册中心和远程服务调用 1. 基于Nacos实现服务注册与发现 基于pring-boot-starter-parent 2.6.8,pring-cloud-depend

  • SpringCloud远程服务调用三种方式及原理

    目录 一个简单的微服务架构图 调用远程服务的三种方式 1.基于 RestTemplate 和 @LoadBalanced 注解 2.基于DiscoveryClient 3.基于 Feign 的声明式调用 原理分析 1.以 @LoadBalanced 为入口开启源码之旅 2.请求调用流程 Spring @Qualifier 注解的妙用 一个简单的微服务架构图 本文设计的 Spring Cloud 版本以及用到的 Spring Cloud 组件 Spring Cloud Hoxton.SR5 eur

  • JavaScript 异步调用框架 (Part 2 - 用例设计)

    传递回调 我们首先要考虑的一个问题是,如何传递回调入口.在最传统的XHR调用当中,回调函数会被作为最后一个参数传递给异步函数: 复制代码 代码如下: function asyncOperation(argument, callback) 在参数相当多的时候,我们可以把参数放到一个JSON里面,这样参数就如同具名参数一样,可以通过参数名选择性的传递参数,不传递的参数相当于使用默认值.这是从Prototype开始就流行起来的做法: 复制代码 代码如下: function asyncOperation

随机推荐