
本文档将向您展示如何使用 Vert.x 和 gRPC Web 构建一个浏览器/服务器应用程序。
该应用程序涉及一个客户端(浏览器)和一个 Vert.x 服务器
用户在文本字段中输入名称并点击发送按钮
浏览器使用 gRPC Web 协议将名称发送到服务器
服务器回复问候语
浏览器显示问候语
在服务器端,您将创建一个 Vert.x gRPC 服务器服务,该服务
实现 gRPC 服务器存根
配置一个 HTTP 服务器,用于响应 gRPC Web 和静态文件请求
在客户端,您将创建一个使用 gRPC Web Javascript 客户端的网页。
文本编辑器或 IDE
Java 17 或更高版本
您无需安装 protoc
或像 vertx-grpc-protoc-plugin2、protobuf-javascript 和 protoc-gen-grpc-web 这样的 protoc 插件,因为它们将由 Maven 插件管理。
gRPC Greeter
服务包含一个单独的 SayHello
RPC 方法。 HelloRequest
消息包含客户端发送的名称。 HelloReply
消息包含服务器生成的问候语。
service.proto
文件syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.vertx.howtos.grpcweb";
option java_outer_classname = "HelloWorldProto";
package helloworld;
// The greeting service definition.
service Greeter {
// Ask for 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 greeting
message HelloReply {
string message = 1;
}
根据服务定义,必须生成几个文件
Java 消息和服务器类
Javascript 文件
gRPC Web 特定 (Javascript) 文件。
protoc
的调用由 protobuf-maven-plugin
管理。
protobuf-maven-plugin
进行代码生成<plugin>
<groupId>io.github.ascopes</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<protocVersion>4.29.3</protocVersion>
<sourceDirectories>src/main/proto</sourceDirectories>
<javaEnabled>false</javaEnabled>
</configuration>
<executions>
<execution>
<id>compile-java</id>
<configuration>
<javaEnabled>true</javaEnabled>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<jvmMavenPlugins>
<jvmMavenPlugin>
<groupId>io.vertx</groupId>
<artifactId>vertx-grpc-protoc-plugin2</artifactId>
<version>${vertx.version}</version>
<mainClass>io.vertx.grpc.plugin.VertxGrpcGenerator</mainClass>
<jvmArgs>
<jvmArg>--grpc-client=false</jvmArg>
<jvmArg>--grpc-service</jvmArg>
<jvmArg>--service-prefix=Vertx</jvmArg>
<jvmArg>--vertx-codegen=false</jvmArg>
</jvmArgs>
</jvmMavenPlugin>
</jvmMavenPlugins>
</configuration>
<goals>
<goal>generate</goal>
</goals>
</execution>
<execution>
<id>compile-javascript</id>
<configuration>
<outputDirectory>${project.basedir}/src/main/web</outputDirectory>
<binaryUrlPlugins>
<binaryUrlPlugin>
<url>${protoc.gen.js.url}</url>
<options>import_style=commonjs</options>
</binaryUrlPlugin>
</binaryUrlPlugins>
</configuration>
<goals>
<goal>generate</goal>
</goals>
</execution>
<execution>
<id>compile-javascript-web</id>
<configuration>
<outputDirectory>${project.basedir}/src/main/web</outputDirectory>
<binaryUrlPlugins>
<binaryUrlPlugin>
<url>${protoc.gen.grpc.web.url}</url>
<options>import_style=typescript,mode=grpcwebtext</options>
</binaryUrlPlugin>
</binaryUrlPlugins>
</configuration>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
我们选择使用 CommonJS
模块生成风格而不是闭包。
我们需要一些依赖项才能编译项目
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-grpc-server</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-api</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
</dependencies>
服务器端代码在一个单独的 ServerVerticle
类中。
首先,是 gRPC 服务器存根实现。
VertxGreeterGrpcService service = new VertxGreeterGrpcService() {
@Override
public Future<HelloReply> sayHello(HelloRequest request) {
return Future.succeededFuture(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
}
};
GrpcServer grpcServer = GrpcServer.server(vertx);
grpcServer.addService(service);
这里没有什么特定于 gRPC Web 的内容。
注意
|
GrpcServer 默认启用 gRPC Web 协议支持。 |
然后我们必须配置一个 Vert.x Web Router
以同时接受 gRPC Web 和静态文件请求。
Router router = Router.router(vertx);
router.route()
.consumes("application/grpc-web-text") // (1)
.handler(rc -> grpcServer.handle(rc.request()));
router.get().handler(StaticHandler.create()); // (2)
return vertx.createHttpServer()
.requestHandler(router)
.listen(8080);
所有内容类型为 application/grpc-web-text
的请求都将交给 grpcServer
处理。
所有其他 GET
请求将由 Vert.x Web StaticHandler
处理。
在编写代码之前,我们必须设置项目以构建客户端代码。为简单起见,我们选择使用 esbuild-maven-plugin
。简而言之,它是一个 Maven 插件,封装了 esbuild
,一个用于 Web 的快速打包器。
需要几个依赖项,我们通过 mvnpm
将它们作为 Maven 依赖项获取
esbuild-maven-plugin
构建客户端代码<plugin>
<groupId>io.mvnpm</groupId>
<artifactId>esbuild-maven-plugin</artifactId>
<version>0.0.2</version>
<executions>
<execution>
<id>esbuild</id>
<goals>
<goal>esbuild</goal>
</goals>
</execution>
</executions>
<configuration>
<entryPoint>index.js</entryPoint>
<outputDirectory>${project.build.outputDirectory}/webroot/js</outputDirectory> <!--(1)-->
</configuration>
<dependencies>
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>grpc-web</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>google-protobuf</artifactId>
<version>3.21.4</version>
</dependency>
</dependencies>
</plugin>
webroot
是 Vert.x Web StaticHandler
提供静态文件的默认基本目录。
用户界面代码在一个单独的 index.html
文件中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Echo Example</title>
<script type="module">
import {sayHello} from "/js/index.js"; // (1)
window.sayHello = sayHello; // (2)
</script>
</head>
<body>
<div>
<p>Type a name in the input field and press enter, or click the send button.</p>
<div class="input-group">
<!-- Invoke javascript function on submit -->
<form onsubmit="return sayHello();">
<input type="text" id="name">
<input type="submit" value="Send">
</form>
</div>
<p id="msg"></p>
</div>
</body>
</html>
从我们的 Javascript 模块中导入 sayHello
函数(见下文)。
使 sayHello
函数成为全局函数。
最后但同样重要的是,让我们实现 sayHello
函数
const {HelloRequest} = require("./service_pb"); // (1)
const {GreeterClient} = require("./service_grpc_web_pb"); // (2)
const greeterClient = new GreeterClient("http://" + window.location.hostname + ":8080", null, null); // (3)
export function sayHello() {
const request = new HelloRequest();
request.setName(document.getElementById("name").value);
greeterClient.sayHello(request, {}, (err, response) => {
const msgElem = document.getElementById("msg");
if (err) {
msgElem.innerText = `Unexpected error for sayHello: code = ${err.code}` + `, message = "${err.message}"`;
} else {
msgElem.innerText = response.getMessage();
}
});
return false; // prevent form posting
}
从生成的 Javascript 文件中导入 HelloRequest
对象。
从 gRPC Web(Javascript)生成的文件中导入 GreeterClient
对象。
配置客户端以向 Web 服务器发送请求。
您可以使用 Maven 运行应用程序
./mvnw compile exec:java
您应该会看到
Server started, browse to https://:8080
您现在可以浏览到 https://:8080 并按照说明操作。
使用浏览器的开发者工具检查 gRPC Web 流量。
本文档涵盖了
实现一个 Vert.x Web 服务器,该服务器同时响应静态文件和 gRPC Web 请求,
创建一个使用 gRPC Web 客户端的网页。
最后发布时间:2025-07-23 02:24:47 +0000。