deepfake.杨超越
照片由 托马斯·詹森 在 Unsplas ^ h
当今使用的大多数业务应用程序都使用HTTP 1.1和REST相互通信。 由于它的简单性和几乎不需要集成应用程序的工作量,它已成为行业标准。
但是,一旦您的应用程序达到每秒处理一百万个甚至更多的请求的规模,上述机制的缺点就会变得明显。 让我们首先解决这些问题-
无复用
单个HTTP 1.1连接只能一次发送一个请求和响应。 该连接将被阻塞,直到收到非常低效的响应为止。
有限压缩
HTTP 1.1标头未压缩,这导致请求数据大小不必要的增加。 在HTTP / 2中,它们使用具有99%压缩率的HPACK算法进行压缩。
基于文本的传输
HTTP 1.1是基于文本的,因此在数据传输方面效率很低。 它们需要复杂的解析器,也不支持高级别的压缩。
HTTP 2摆脱了所有这些限制。 它支持多路复用,允许客户端通过单个连接发送多个并行请求。 标头使用HPACK压缩算法压缩并传输二进制数据。
本文提供了两种协议的详细比较以及性能指标。
在本文中,我们将探讨如何在服务中使用HTTP / 2。 当前,最流行的方法是使用Google的gRPC 。
gRPC
简而言之,它是一个用于使用HTTP / 2连接多个服务的Web框架。 目前有数百家公司使用它来有效地连接微服务。
gRPC服务与REST的不同之处在于,它们不公开端点,而是公开方法/过程。 客户端只是简单地调用方法,就好像它是在本地实现的一样,在后台,RPC调用被发送到服务器,其中包含该方法的实际实现。
服务接口及其方法在.proto文件中定义。 这是因为gRPC使用协议缓冲区在各种服务之间传输数据以及生成客户端和服务器存根。 这导致序列化和反序列化速度加快一个数量级。 我可以在本文中找到协议缓冲区,JSON和Flatbuffer之间的详细比较。
RPC调用是通过HTTP / 2完成的。 这使gRPC用户可以自动利用上述协议的所有功能。
让我们使用Java在gRPC中创建一个简单的Hello World服务。 此处提供的示例可以在https://grpc.io/docs/quickstart/java/上找到
定义服务
首先,我们在.proto文件中定义服务的接口。 我们将此文件命名为greeter.proto
我们将服务命名为Greeter。 该服务包含方法SayHello。 该方法接受一个HelloRequest对象,并返回一个HelloReply对象。
接下来,我们定义HelloRequest和HelloReply ProtocolBuffers的格式。
syntax ="proto3" ;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1 ;
}
// The response message containing the greetings
message HelloReply {
string message = 1 ;
}
生成接口
接下来,我们将使用protobuf编译器生成服务接口和客户端存根。 创建您通常的Maven Java项目。 接下来,将greeter.proto文件复制到src / main / proto文件夹中。
一旦存在,我们就可以使用Maven Proto编译器从该文件生成Java类。
< properties >
< project.build.sourceEncoding > UTF-8 </ project.build.sourceEncoding >
< grpc.version > 1.19.0 </ grpc.version > <!-- CURRENT_GRPC_VERSION -->
< protobuf.version > 3.6.1 </ protobuf.version >
< protoc.version > 3.6.1 </ protoc.version >
<!-- required for jdk9 -->
< maven.compiler.source > 1.7 </ maven.compiler.source >
< maven.compiler.target > 1.7 </ maven.compiler.target >
</ properties >
< build >
< sourceDirectory >
${basedir}/target/generated-sources/
</ sourceDirectory >
< extensions >
< extension >
< groupId > kr.motd.maven </ groupId >
< artifactId > os-maven-plugin </ artifactId >
< version > 1.6.2 </ version >
</ extension >
</ extensions >
< plugins >
< plugin >
< groupId > org.xolstice.maven.plugins </ groupId >
< artifactId > protobuf-maven-plugin </ artifactId >
< version > 0.5.1 </ version >
< configuration >
< protocArtifact > com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} </ protocArtifact >
< pluginId > grpc-java </ pluginId >
< pluginArtifact > io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} </ pluginArtifact >
</ configuration >
< executions >
< execution >
< goals >
< goal > compile </ goal >
< goal > compile-custom </ goal >
< goal > test-compile </ goal >
< goal > test-compile-custom </ goal >
</ goals >
</ execution >
</ executions >
</ plugin > sasas
</ plugins >
</ build >
这些类将在target / generate-sources / protobuf目录中生成。
实施服务
现在,我们可以扩展生成的服务接口以实现这些方法。
private class GreeterImpl extends GreeterGrpc . GreeterImplBase {
@Override
public void sayHello (HelloRequest req, StreamObserver<HelloReply> responseObserver) {
//Build Proto Messages Object
HelloReply reply = HelloReply.newBuilder().setMessage( "Hello " + req.getName()).build();
//Send the response, you can send multiple objects which will be trasmitted in streaming fashion
responseObserver.onNext(reply);
//Indicate client that response has finished
responseObserver.onCompleted();
}
}
实施客户
客户端现在可以简单地调用此sayHello方法,该服务将通过HTTP / 2返回响应
public class Client {
private final ManagedChannel channel;
private final GreeterBlockingStub blockingStub;
private final GreeterStub asyncStub;
public Client (String host, String port) {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); //remove .usePlainText for secure connections
blockingStub = GreeterGrpc.newBlockingStub(channel); // use it to make blocking calls
asyncStub = GreeterGrpc.newStub(channel); // or use this to make async calls
}
public void blockingGreet (String name) {
logger.info( "Will try to greet " + name + " ..." );
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}" , e.getStatus());
return ;
}
logger.info( "Greeting: " + response.getMessage());
}
public void asyncGreet (String name) {
StreamObserver<HelloReply> responseObserver = new StreamObserver<HelloReply>() {
@Override
public void onNext (HelloReply response) {
logger.info( "Greeting: " + response.getMessage());
}
@Override
public void onError (Throwable t) {
Status status = Status.fromThrowable(t);
logger.log(Level.WARNING, "RPC Failed: {0}" , status);
}
@Override
public void onCompleted () {
info( "Finished Greeting" );
}
};
logger.info( "Will try to greet " + name + " ..." );
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
asyncStub.sayHello(request, responseObserver);
}
}
运行服务器
您可以使用与grpc捆绑在一起的服务器,也可以使用已经提供grpc绑定的外部框架,例如Vert.x Java。
public GreeterServer ( int port) throws IOException {
this (ServerBuilder.forPort(port), port);
}
public GreeterServer (ServerBuilder<?> serverBuilder, int port) {
this .port = port;
server = serverBuilder.addService( new GreeterImpl()).build();
}
public void start () throws IOException {
server.start();
logger.info( "Server started, listening on " + port);
}
现在,您已经成功实现了基本的gRPC服务。
负载均衡
在生产环境中运行http / 2服务的重要部分是负载平衡。 gRPC打破了标准的连接级负载平衡,即为Kubernetes或HAProxy中默认提供的请求创建到另一个实例的新连接。 这是因为gRPC基于HTTP / 2构建,并且HTTP / 2设计为具有单个长期TCP连接。
解决方案是执行请求级别的负载平衡。 这意味着创建长期连接,然后在这些连接之间分配请求。
最简单,最有效的方法是使用linkerd2 。 它是一个服务网格,可以在您的Kubernetes / Mesos或任何其他集群旁边运行。 Linkerd2充当传入请求的代理。 由于它是生锈的,因此可以在主机上添加最小延迟(<1ms)并实现负载平衡请求,并且可以通过k8s API或DNS进行检测。
您无需在Linkerd2中配置任何其他内容,它默认处理HTTP 1和2流量。
如果您想了解有关gRPC,linkerD或http / 2的更多信息,可以参考以下链接:
HTTP / 2:HTTP / 1.1,好处和使用方法之间的区别
在 LinkedIn 或 Twitter上 与我联系, 或 发送邮件至 kharekartik@gmail.com
翻译自: https://hackernoon.com/heres-how-you-can-go-beyond-http-11-p23g53z2e
deepfake.杨超越