您可以使用Thrift作为Cloud Foundry中应用程序之间的通信方式吗?

时间:2016-02-11 10:48:09

标签: rest thrift cloudfoundry

Cloud Foundry是否支持Thrift在应用之间进行通话?我无法找到任何相关内容,似乎REST / HTTP或消息传递是应用程序间通信的首选机制。

有没有人试图在CF中使用Thrift提供服务?是否有任何理由不支持Thrift;或者有任何支持它的计划吗?我更喜欢Thrift到REST进行跨语言通信,因为在Thrift中生成API调用中使用的所有客户端类很容易。此外,它支持二进制数据传输,因此可能比REST更快。另外,我们已经有了一个Thrift API:)。

我猜测 - 理论上 - 客户端应用程序可以直接与运行Thrift服务的另一个CF应用程序实例对话;但是,您现在正在失去CF目前为HTTP或消息传递提供的负载平衡优势。

3 个答案:

答案 0 :(得分:2)

Cloud Foundry Router目前仅支持HTTP / HTTPS请求的路由。正在努力创建一个TCP路由器来处理非CF流量到CF上运行的应用程序的路由。

video预览TCP路由器将提供的一些功能,如果您通过BOSH部署自己的CF,则incubating release包含TCP路由器。

cf-dev mailing list是一个提出更多相关问题的好地方。

  

我猜测 - 理论上 - 客户端应用可以直接与运行Thrift服务的另一个CF应用实例对话

这是正确的 - 只要配置的security groups打开了正确的端口,在CF上运行的应用就可以使用任何协议相互通信。

答案 1 :(得分:1)

Were there important restrictions over what you could send over HTTP vs Thrift? Also, I'd like to know more about how a client would call different methods on your service. Did you have one generalised HTTP method or did you end up with a REST-like API that mirrored your Thrift API's methods?

Below the code that handles an incoming request and returns the results. The code is part of an article I wrote for a german dev magazine some time ago. The whole package consisted of some JavaScript, some C# and some Delphi code. I might emphasize that we also use that same approach shown here in real code.

Delphi comes with built-in support for ISAPI. The OnHandlerAction is basically an event handler that can be assigned to selected URL endpoints, or to catch all requests. This also implies, that we don't need to care about thread management, because that's what the IIS does for us.

Server side

I decided to use the TJSONProtocol for two reasons. First, one of the clients was the JavaScript part which at that time was only capable to speak JSON. Second goal was to eliminate any risk of garbled data. I never tested TBinaryProtocol nor TCompactProtocol in that context, so I can't say much about that combination.

procedure TSample.OnHandlerAction( Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
const GENERIC_ERROR_JSON = '[1,"TextToolIsapi",2,0,{"2":{"rec":{"1":{"str":"Internal error: %s"}}}}]';
var b : TBytes;
    s : string;
begin
  try
    Response.Content := TTextToolJsonServer.Process( Request.Content);
  except
    on e:Exception do begin
      b := ( SysUtils.TEncoding.UTF8.GetBytes(e.message));
      SetString( s, PAnsiChar(@b[0]), Length(b));
      Response.Content := Format( GENERIC_ERROR_JSON, [s]);
    end;
  end;
end;

The remaining part is the piece that does the (de)serializing stuff and feeds the processor. In this sample code a processor instance is created for every call. If that does not fit, one can think of a pool of processor instances or the like. In our case the session-specific data are held elsewhere, so the processors are in fact very lightweight.

class function TTextToolJsonServer.Process( const aRequest : string) : string;
var handler   : Samples.TTextTool.Iface;
    processor : IProcessor;
    protIn, protOut : IProtocol;
    stmIn, stmOut : TStringStream;
begin
  stmIn  := nil;
  stmOut := nil;
  try
    stmIn  := TStringStream.Create( aRequest);
    stmOut := TStringStream.Create;

    protIn  := TJSONProtocolImpl.Create(
                 TStreamTransportImpl.Create(
                   TThriftStreamAdapterDelphi.Create( stmIn, FALSE), nil));

    protOut := TJSONProtocolImpl.Create(
                 TStreamTransportImpl.Create(
                   nil, TThriftStreamAdapterDelphi.Create( stmOut, FALSE)));

    handler   := TTextToolHandler.Create;
    processor := Samples.TTextTool.TProcessorImpl.Create( handler);
    processor.Process( protIn, protOut);

    result := stmOut.DataString;

  finally
    stmIn.Free;
    stmOut.Free;
  end;
end;

That's all, the rest is standard processor/handler implementation.

Sample client

The client side counterpart could be as simple as this piece of JavaScript. Unfortunately I don't have a Java example at hand, but it would look very similar.

  function MakeClient()
  {
    var transport = new Thrift.Transport("bin/TextToolSample.dll"); 
    var protocol  = new Thrift.Protocol(transport);
    var client = new Samples.TextToolClient(protocol);
    return client;
  }

  function CountWords() 
  {
    try
    {
      var client = MakeClient();
      var nCount = client.CountWords( document.EDITOR.textinput.value);
      alert("Text contains "+nCount.toString()+" words.");
    }
    catch (error) 
    {
      HandleError( error) 
    }
  }

答案 2 :(得分:0)

继@ JensG建议的解决方案后,我想找到一个使用Thrift / HTTP的解决方案。如果您的CF管理员不想支持Thrift / TCP,这是一个后备,例如,如果您的CF部署中缺少.Net buildpack意味着Thrift服务必须在CF外部托管。我在这里分享它是因为我不明白如何让Thrift / HTTP为C#服务实现工作。整个过程中都使用了Thrift 0.9.3。

示例Thrift IDL文件

namespace csharp AuthServiceGenCSharp
namespace * AuthServiceGenCSharp.thrift

struct Request
{
    1:string name
    2:i32 age
}

struct Result
{
    1:string reply
    2:bool permissionGranted
}

service AuthorizationService
{
    Result AskPermission(1:Request req)
}

使用Thrift编译器正常生成C#存根。然后实现服务:

示例服务实现(C#)

using AuthServiceGenCSharp;

namespace AuthServiceImplementation
{
    /// <summary>
    /// Implementation of the Thrift interface.
    /// </summary>
    public class AuthorizationServiceImpl : AuthorizationService.Iface
    {
        public Result AskPermission(Request req)
        {
            Result result = new Result();
            result.Reply = "Hello " + req.Name;
            result.PermissionGranted = true;
            return result;
        }
    }
}

创建自定义HTTP处理程序

幸运的是,Thrift已经提供了一个实现IHttpHandler接口的类:Thrift.Transport.THttpHandler。您所要做的就是从THttpHandler派生您的处理程序,并传入Thrift编译器从您的IDL生成的处理器。并指定JSON协议:

using AuthServiceGenCSharp;
using AuthServiceImplementation;
using Thrift.Protocol;
using Thrift.Transport;

// See also https://codealoc.wordpress.com/2012/04/06/thrift-over-http-with-iis/
namespace AuthServiceHTTPHandler.App_Code
{
    public class AuthServiceHandler : THttpHandler
    {
        public AuthServiceHandler()
            : base(CreateAuthProcessor(), CreateJSONFactory())
        {
            // We call the constructor for THttpHandler, passing in the processor we want to use (i.e., the one that
            // the Thrift compiler generated from our IDL) and the protocol factory for JSON in this case.
            // We can't use THttpHandler directly for 2 reasons. First, it doesn't have a no-arg constructor which
            // all proper handlers must have. (At runtime you'll get this error:
            // HTTP/1.1 500 Internal Server Error [MissingMethodException: Constructor on type &#39;Thrift.Transport.THttpHandler&#39; not found.])
            // Second, we need to tell THttpHandler which processor to use at the very least.
            // Maybe Thrift should make their THttpHandler class abstract to prevent people like me from trying to use it directly!?
        }

        private static AuthorizationService.Processor CreateAuthProcessor()
        {
            return new AuthorizationService.Processor(new AuthorizationServiceImpl());
        }

        private static TJSONProtocol.Factory CreateJSONFactory()
        {
            return new TJSONProtocol.Factory();
        }
    }
}

(在codealoc博客上给出了类似的示例。)将此处理程序放在ASP.Net Web应用程序的App_Code文件夹中。在web.config中注册处理程序。我在集成模式下使用IIS 7.5,所以我这样做了:

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <handlers>
      <remove name="WebDAV" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="AuthServiceHandler"
           path="*."
           verb="*"
           type="AuthServiceHTTPHandler.App_Code.AuthServiceHandler"
           preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>

示例Java客户端代码段

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TJSONProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import AuthServiceGenCSharp.thrift.AuthorizationService.Client;
import AuthServiceGenCSharp.thrift.Request;
import AuthServiceGenCSharp.thrift.Result;

...

TTransport transport = null;
try {
    transport = new THttpClient("http://localhost:9999");
} catch (TTransportException e) {
    e.printStackTrace();
}
TProtocol protocol = new TJSONProtocol(transport);
Client client = new Client(protocol);

try {
    transport.open();
    Request request = new Request("Fred", 32);
    Result result = client.AskPermission(request);
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(result);
    log.info("Response in JSON format: " + json);
    return json;
} catch (TException e) {
    e.printStackTrace();                
} catch (JsonProcessingException e) {                               
    e.printStackTrace();
} finally {
    if (transport != null && transport.isOpen()) {
        transport.close();
    }
}

当C#ASP.NET应用程序从Visual Studio 2010发布到IIS 7.5并且Java客户端指向它时,HTTP响应正文中的Thrift响应是:

[1,"AskPermission",2,1,{"0":{"rec":{"1":{"str":"Hello Fred"},"2":{"tf":1}}}}]

和Java日志输出:

Response in JSON format: {"reply":"Hello Fred","permissionGranted":true,"setPermissionGranted":true,"setReply":true}

我最初尝试实施custom HTTP module。但是,尽管HTTP响应中返回了正确的Thrift响应,但HTTP标头始终为405: Method Not Allowed(Thrift客户端使用POST发送其请求)。关于此问题another SO post。但是,使用HTTP处理程序可能比长期使用模块更好,因为您可以选择为长时间运行的请求创建asynchronous handler