您可以使用 Thrift 作为 Cloud Foundry 中应用程序之间的通信方法吗?
Can you use Thrift as a method of communication between apps in Cloud Foundry?
Cloud Foundry 是否支持 Thrift 在应用程序之间进行通信?我找不到任何相关信息,似乎 REST/HTTP 或消息传递是应用程序间通信的首选机制。
有人试过在 CF 中使用 Thrift 提供服务吗?不支持节俭有什么充分的理由吗?或者有什么计划支持它吗?对于跨语言通信,我更喜欢 Thrift 而不是 REST,因为在 Thrift 中很容易生成 API 调用中使用的所有客户端 类。此外,它支持二进制数据传输,因此可能比 REST 更快。另外我们已经有了一个 Thrift API:).
我猜测 - 理论上 - 客户端应用程序可以直接与另一个 CF 应用程序实例对话 运行 Thrift 服务;但是您将失去 CF 当前为 HTTP 或消息传递提供的负载平衡优势。
Cloud Foundry Router 当前仅支持路由 HTTP/HTTPS 请求。目前正在努力创建一个 TCP 路由器来处理将非 HTTP 流量路由到 CF 上的应用 运行。
如果您要通过 BOSH 部署自己的 CF,则 video that previews some of the capabilities the TCP Router will provide, and an incubating release 包含 TCP 路由器。
cf-dev mailing list 是询问有关此问题的其他问题的好地方。
I'm guessing that - in theory - a client app could talk directly to another CF app instance running a Thrift service
这是正确的 - CF 上的应用程序 运行 可以使用任何协议相互通信,只要配置的 security groups 有正确的端口打开。
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?
在处理传入请求和 returns 结果的代码下方。该代码是我前段时间为德国开发杂志撰写的一篇文章的一部分。整个包由一些 Java 脚本、一些 C# 和一些 Delphi 代码组成。我可能会强调,我们也在实际代码中使用此处显示的相同方法。
Delphi 内置了对 ISAPI 的支持。 OnHandlerAction
基本上是一个事件处理程序,可以分配给选定的 URL 端点,或捕获所有请求。这也意味着,我们不需要关心线程管理,因为那是 IIS 为我们所做的。
服务器端
我决定使用 TJSONProtocol
有两个原因。首先,其中一个客户是 Java 脚本部分,当时只能说 JSON。第二个目标是消除任何乱码数据的风险。我从未在那种情况下测试过 TBinaryProtocol
或 TCompactProtocol
,所以我不能对这种组合说太多。
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;
剩下的部分是执行(反)序列化并提供给处理器的部分。在此示例代码中,为每次调用创建一个处理器实例。如果那不合适,可以考虑处理器实例池等。在我们的例子中,特定于会话的数据保存在别处,因此处理器实际上非常轻量级。
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;
仅此而已,其余的是标准 processor/handler 实现。
示例客户端
客户端的对应部分可以像这段Java脚本一样简单。不幸的是,我手头没有 Java 示例,但它看起来非常相似。
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)
}
}
根据@JensG 建议的解决方案,我想找到一个使用 Thrift/HTTP 的解决方案。如果您的 CF 管理员不想支持 Thrift/TCP,那么这是一个回退,例如,如果您的 CF 部署中缺少 .Net 构建包意味着必须在 CF 外部托管 Thrift 服务。我在这里分享它是因为我不清楚如何让 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 已经提供了一个 class 实现了 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 'Thrift.Transport.THttpHandler' 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 网络应用程序的 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 instead. But, although the correct Thrift response was coming back in the HTTP response, the HTTP header was always 405: Method Not Allowed
(the Thrift client uses POST to send its requests). There's another SO post about this. However using an HTTP handler might be better than using a module in the long run because you have the option of creating an asynchronous handler。
Cloud Foundry 是否支持 Thrift 在应用程序之间进行通信?我找不到任何相关信息,似乎 REST/HTTP 或消息传递是应用程序间通信的首选机制。
有人试过在 CF 中使用 Thrift 提供服务吗?不支持节俭有什么充分的理由吗?或者有什么计划支持它吗?对于跨语言通信,我更喜欢 Thrift 而不是 REST,因为在 Thrift 中很容易生成 API 调用中使用的所有客户端 类。此外,它支持二进制数据传输,因此可能比 REST 更快。另外我们已经有了一个 Thrift API:).
我猜测 - 理论上 - 客户端应用程序可以直接与另一个 CF 应用程序实例对话 运行 Thrift 服务;但是您将失去 CF 当前为 HTTP 或消息传递提供的负载平衡优势。
Cloud Foundry Router 当前仅支持路由 HTTP/HTTPS 请求。目前正在努力创建一个 TCP 路由器来处理将非 HTTP 流量路由到 CF 上的应用 运行。
如果您要通过 BOSH 部署自己的 CF,则 video that previews some of the capabilities the TCP Router will provide, and an incubating release 包含 TCP 路由器。
cf-dev mailing list 是询问有关此问题的其他问题的好地方。
I'm guessing that - in theory - a client app could talk directly to another CF app instance running a Thrift service
这是正确的 - CF 上的应用程序 运行 可以使用任何协议相互通信,只要配置的 security groups 有正确的端口打开。
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?
在处理传入请求和 returns 结果的代码下方。该代码是我前段时间为德国开发杂志撰写的一篇文章的一部分。整个包由一些 Java 脚本、一些 C# 和一些 Delphi 代码组成。我可能会强调,我们也在实际代码中使用此处显示的相同方法。
Delphi 内置了对 ISAPI 的支持。 OnHandlerAction
基本上是一个事件处理程序,可以分配给选定的 URL 端点,或捕获所有请求。这也意味着,我们不需要关心线程管理,因为那是 IIS 为我们所做的。
服务器端
我决定使用 TJSONProtocol
有两个原因。首先,其中一个客户是 Java 脚本部分,当时只能说 JSON。第二个目标是消除任何乱码数据的风险。我从未在那种情况下测试过 TBinaryProtocol
或 TCompactProtocol
,所以我不能对这种组合说太多。
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;
剩下的部分是执行(反)序列化并提供给处理器的部分。在此示例代码中,为每次调用创建一个处理器实例。如果那不合适,可以考虑处理器实例池等。在我们的例子中,特定于会话的数据保存在别处,因此处理器实际上非常轻量级。
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;
仅此而已,其余的是标准 processor/handler 实现。
示例客户端
客户端的对应部分可以像这段Java脚本一样简单。不幸的是,我手头没有 Java 示例,但它看起来非常相似。
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)
}
}
根据@JensG 建议的解决方案,我想找到一个使用 Thrift/HTTP 的解决方案。如果您的 CF 管理员不想支持 Thrift/TCP,那么这是一个回退,例如,如果您的 CF 部署中缺少 .Net 构建包意味着必须在 CF 外部托管 Thrift 服务。我在这里分享它是因为我不清楚如何让 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 已经提供了一个 class 实现了 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 'Thrift.Transport.THttpHandler' 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 网络应用程序的 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 instead. But, although the correct Thrift response was coming back in the HTTP response, the HTTP header was always 405: Method Not Allowed
(the Thrift client uses POST to send its requests). There's another SO post about this. However using an HTTP handler might be better than using a module in the long run because you have the option of creating an asynchronous handler。