protoBuf 实现客户端与服务端
Posted 香吧香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了protoBuf 实现客户端与服务端相关的知识,希望对你有一定的参考价值。
转载请注明出处:
1.定义消息格式
在 src/main/proto 目录下创建 person.proto 文件,并定义消息格式,例如:
syntax = "proto3"; package example; message Person string name = 1; int32 age = 2; repeated string interests = 3;
这个文件定义了一个名为 Person 的消息类型,包括三个字段:name、age 和 interests
2.生成代码
使用 protoc 工具来生成 Java 代码,需要安装相应的插件和工具,可以通过 Maven 或 Gradle 等构建工具自动下载和配置。这里演示手动下载和安装的方式。
首先下载 protoc 工具及其插件,例如从官方网站下载对应版本的 protoc-3.x.x-linux-x86_64.zip,以及 protoc-gen-grpc-java 插件,例如从 Maven 中央仓库下载最新版的 protoc-gen-grpc-java-1.42.0-linux-x86_64.exe。
然后解压 protoc 工具,将 protoc 命令所在的路径添加到环境变量 PATH 中,例如:
export PATH="/path/to/protoc/bin:$PATH"
接下来安装 protobuf-java 和 grpc-java 两个依赖,例如通过 Maven 引入以下依赖:
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.17.3</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.42.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.42.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.42.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-testing</artifactId> <version>1.42.0</version> <scope>test</scope> </dependency>
接着使用 protoc 生成 Java 代码,例如:
protoc --java_out=src/main/java --grpc-java_out=src/main/java src/main/proto/person.proto
这个命令会在 src/main/java/example 目录下生成 Person.java、PersonGrpc.java 和 PersonGrpc$PersonStub.java 等文件。
3.实现服务端
package example; import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; public class PersonServer private final int port; private final Server server; public PersonServer(int port) throws IOException this.port = port; this.server = ServerBuilder.forPort(port) .addService(new PersonServiceImpl()) .build(); public void start() throws IOException server.start(); System.out.println("Server started, listening on " + port); Runtime.getRuntime().addShutdownHook(new Thread(() -> System.err.println("*** shutting down gRPC server since JVM is shutting down"); PersonServer.this.stop(); System.err.println("*** server shut down"); )); public void stop() if (server != null) server.shutdown(); private static class PersonServiceImpl extends PersonGrpc.PersonImplBase @Override public void getMessage(PersonRequest request, StreamObserver<PersonResponse> responseObserver) String name = request.getName(); int age = request.getAge(); String interests = String.join(", ", request.getInterestsList()); // 将请求的内容响应回去 PersonResponse response = PersonResponse.newBuilder() .setMessage("Received person info: name=" + name + ", age=" + age + ", interests=[" + interests + "]") .build(); responseObserver.onNext(response); responseObserver.onCompleted(); public static void main(String[] args) throws IOException, InterruptedException PersonServer server = new PersonServer(8989); server.start(); server.blockUntilShutdown();
4.实现客户端
package example; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class PersonClient private final ManagedChannel channel; private final PersonGrpc.PersonStub stub; public PersonClient(String host, int port) this.channel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); this.stub = PersonGrpc.newStub(channel); public void shutdown() throws InterruptedException channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); public void sendMessage(String name, int age, String... interests) throws InterruptedException final CountDownLatch latch = new CountDownLatch(1); StreamObserver<PersonResponse> responseObserver = new StreamObserver<PersonResponse>() @Override public void onNext(PersonResponse response) System.out.println("Received response: " + response.getMessage()); @Override public void onError(Throwable t) System.err.println("Received error: " + t.getMessage()); latch.countDown(); @Override public void onCompleted() System.out.println("Request completed"); latch.countDown(); ; List<String> interestList = Arrays.asList(interests); PersonRequest request = PersonRequest.newBuilder() .setName(name) .setAge(age) .addAllInterests(interestList) .build(); stub.getMessage(request, responseObserver); latch.await(); public static void main(String[] args) throws InterruptedException PersonClient client = new PersonClient("localhost", 8989); // 向服务器发送请求 client.sendMessage("Alice", 20, "reading", "swimming"); // 关闭连接 client.shutdown();
这个客户端会向服务器发送一个包含 name、age 和 interests 字段的 PersonRequest 消息,并等待接收服务器的响应信息。
Erik Bergstedt的工具库简化了在.NET中使用Protobuf的方式
Protobuf是由Google所创建的一种编码格式,它的正式名称叫做。这是一种紧凑的二进制格式,它牺牲了内容的可读性与可扩展性,换来了更小的空间占用与更好的性能。为了对某个Protobuf消息进行反序列化,客户端与服务端必须事先对消息中的特定字段达成一致。(与XML或JSON格式相反,客户端可以在运行时对后者的消息结构进行分析。)
.NET版本的Protobuf实现利用了属性的自定义attribute,为每个属性设置了一个“id”值。这些attribute将用于构建包含Protobuf消息的二进制数组。以下是Protobuf的一个应用示例:
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
在C#中使用Protobuf的方式有些繁琐,因为开发者需要对流(stream)进行操作。而通过使用Erik Bergstedt所开发的,只需一行代码就可以完成序列化或反序列化工作。
var serialize = _simpleSerializer.ToByteArray(GetPerson());
Person deserialize = _simpleDeserializer.FromByteArray<Person>(serialize);
开发者可能会感到疑惑,为什么不将这些API直接加入Protobuf项目中呢?Erik对此写道:
因为这个库的API是写死的,它隐藏了某些功能,这对于某些场景来说可能并不合适。它为了换取简便性而牺牲了某些功能。
举例来说,在这个库里对于流的创建方式是写死的,并且对GZIP的支持也超出了原始的protobuf的功能范围。
我们需要小心地区分特性与特性膨胀的区别。如果将这个库的功能添加至原始的protobuf项目中,我会认为它不是一种特性,而是一种特性膨胀。
Protobuffer是一个基于MIT协议的开源软件。而Protobuf工具本身是由Google所开发的,它支持多种平台与编程语言,包括C++、Java、JavaScript、Python和Ruby等等。
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
以上是关于protoBuf 实现客户端与服务端的主要内容,如果未能解决你的问题,请参考以下文章
Erik Bergstedt的工具库简化了在.NET中使用Protobuf的方式