Pull to refresh

Использование Thrift в .NET

Reading time 5 min
Views 12K
Хочу поделиться с вами примером того как можно использовать такую удобную штуку как Thrift в своих .NET проектах.

Для тех кто не знает, Thrift — это фреймворк для облегчения взаимодействия между кодом написанным на разных языках, а именно C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk и OCaml.

Thrift используется и был изначально создан Facebook. Так же неоднократно упоминался здесь, на Хабре, но примеров для .NET я не нашел, кстати пошаговое руководство для .NET отсутствует и на официальном сайте. В гугле если честно, тоже не смог найти, хотя может плохо искал.

Thrift позволяет один раз описать сервис, структуры данных и даже исключения, а потом сгенерировать код для всех поддерживаемых языков. Таким образом, если вы, например, напишете сервер с использованием Thrift на .NET, то вы
  1. Сэкономите кучу времени для написания клиент серверного приложения, например с использованием сокетов.
  2. Почти автоматически получаете клиентов на всех поддерживаемых языках.
В своем примере я реализую простейший сервис, который просто будет возвращать время. Но имея такой макет достаточно легко расширить его до чего-нибудь полезного.


Итак нам понадобится:
  1. Исходники Thrift — они нужны нам для того что бы собрать библиотеку (class library) для подключения в наш проект.
  2. Thrift компилятор — консольная утилита которая генерирует код на необходимом нам языке из .thrift файлов

Оба можно взять отсюда

Так же в конце статьи я дам исходники рабочего приложения, в них есть уже собранная библиотека и компилятор, можно взять все оттуда.

Шаг 1: Подготовка
Итак после того как вы скачали исходники, распаковывайте их в любую директорию и заходите в
thrift-0.5.0\lib\csharp\src\
Открывайте солюшен и собирайте проект. После чего у вас есть библиотека Thrift.dll.

Шаг 2: Создаем наши проекты
Создайте 3 проекта в Visual Studio и объедините их в солюшен.
  • TimeServer — консольное приложение. Сервер. Будет ждать соединения
  • TimeServerClient — консольное приложение. Клиент. Будет соединятся с сервером и спрашивать время.
  • TimeServerCore — библиотека (class library). В ней будут лежать сгенерированные C# классы и исходный файл .thrift(о нем далее)
У меня получилось так:
image

Берете полученную ранее библиотеку Thrift.dll и скачанный Thrift компилятор и кладете их в директорию в солюшене.

Шаг 3: Генерируем код из .thrift файла
Опишем наш сервис и структуру данных. Для этого создадим файл TimeService.thrift с таким содержанием:
namespace csharp TimeServer.Thrift

//Structure for returning Time
struct TimeInfoStruct{
1: string Time
}

//Service
service TimeService
{
  TimeInfoStruct GetTime()
}


* This source code was highlighted with Source Code Highlighter.


и положим его в проект TimeServerCore . Так же добавьте в проект ссылку(reference) на Thrift.dll полученную в Шаге №1.

Важно: Не сохраняйте .thrift файл в Unicode кодировке, у меня Thrift компилятор не захотел ничего из него генерировать, до тех пор пока я в студии в File->Advanced Save Options не сменил Unicode на другую кодировку.

В этом файле и содержится практически все описание нашего клиент-серверного приложения.

Можно вручную вызвать Thrift компилятор указав ему .thrift файл и получить необходимые классы, но лучше добавить pre-build event в наш проект, на случай если мы будем менять TimeService.thrift

С моей иерархией директорий, получился вот такой вызов
$(SolutionDir)\Thrift\thrift-0.5.0.exe -gen csharp -o $(ProjectDir) $(ProjectDir)\TimeService.thrift

«thrift-0.5.0.exe» — собственно сам компилятор, опция "-gen csharp" говорит ему что нам нужны классы для C#, опция "-o $(ProjectDir)" говорит куда положить результат, ну и остаток "$(ProjectDir)\TimeService.thrift" указывает какой собственно файл компилировать.

Таким образом после билда(на самом деле до, у нас же pre-build event) TimeServerCore у нас создадутся 2 класса
TimeInfoStruct.cs — структура для передачи времени, в принципе метод сервиса мог просто вернуть строку, но со структурой интерестнее.
TimeService.cs — сервис, с нашим единственным методом.

Так же в них есть код отвечающий за сериализацию/десериализайию, наглядный ToString, и кое-что еще.

Лежать они будут в \TimeServerCore\gen-csharp\TimeServer\Thrift. Директория gen-csharp будет всегда, видимо для того что бы раскидать по разным папкам код для разных языков, если указано больше одного языка, а две директории ниже по иерархии (\TimeServer\Thrift) создаются из за namespace, указанного в .thrift файле.

Необходимо добавить эти 2 файла в проект, у меня после этого получилось так:
image

Шаг 4: Сервер
Займемся сервером. Добавьте в TimeServer ссылку(reference) на Thrift.dll, а так же на проект TimeServerCore в нашем солюшене. После чего создайте новый класс TimeServiceImplementation.cs.

В этом классе мы реализуем методы нашего сервиса. В сгенерированном файлике TimeService.cs Thrift для нас создал специальный интерфейс, который мы должны реализовать.

Вот интерфейс:
 public class TimeService { //лежит внутри класса

  public interface Iface {
   TimeInfoStruct GetTime();
  }
//...
}


* This source code was highlighted with Source Code Highlighter.


как видим там есть всего 1 функция, которую мы описали в .thrift файле.

Вот моя реализация этого интерфейса:
class TimeServiceImplementation : TimeService.Iface
  {
    public TimeInfoStruct GetTime()
    {
      return new TimeInfoStruct() { Time = DateTime.Now.ToString() };
    }
  }


* This source code was highlighted with Source Code Highlighter.


И так все почти готово, необходимо заставить сервер запускаться при старте приложения и ждать клиентов:
static void Main(string[] args)
    {
      TimeServiceImplementation service = new TimeServiceImplementation();  
      TProcessor processor = new TimeService.Processor(service);
      TServerTransport transport = new TServerSocket(1337, 1000);
      TServer server = new TSimpleServer(processor, transport);

      server.Serve();
    }


* This source code was highlighted with Source Code Highlighter.


Мы создаем нашу реализацию интерфейса сервера, после чего запускаем сервер на 1337 порту и ждем соединений.Вот и весь код необходимый для создания простейшего сервера.
Помимо реализации сервера TSimpleServer в библиотеке Thrift.dll есть TThreadedServer и TThreadPoolServer.

Шаг 5: Клиент

Реализация клиента еще проще:

  static void Main(string[] args)
    {
      TTransport transport = new TSocket("localhost", 1337);
      TProtocol proto = new TBinaryProtocol(transport);
      TimeService.Client client = new TimeService.Client(proto);
      
      transport.Open();
      TimeInfoStruct result = client.GetTime();

      Console.WriteLine(result.ToString());
      Console.ReadKey();
    }


* This source code was highlighted with Source Code Highlighter.


Не забудьте добавить в проект клиента ссылку(reference) на Thrift.dll и проект TimeServerCore.

После чего, можно запустить сначала сервер, а затем клиент. Который должен вывести текущее время.

Вот и все. Причем большую часть времени мы потратили на первоначальную настройку и знакомство с Thrift, да и то не так много. Теперь можно добавлять в наш .thirft файл новые структуры и методы в сервис, после чего лишь писать реализации серверных методов.
Ну а если понадобятся клиенты или серверы написанные на других языках, то классы для них так же можно сгенерировать c помошью Thrift.

Например, с использованием Thrift был получен .NET клиент для БД Cassandra. Хотя создавая Cassandra вряд ли Facebook планировали поддерживать .NET клиентов :)

Вот обещанные исходники:
dl.dropbox.com/u/3945288/Thirft-Time-Server-.NET-Sample.zip
github.com/kmuzykov/Thirft-Time-Server-.NET-Sample

Вот ссылка на Thrift Wiki, хотя там не много информации для .NET.
Tags:
Hubs:
+21
Comments 28
Comments Comments 28

Articles