零基础入门gRPC:从 0 实现一个Hello World
在之前讲解 Nacos 注册中心的过程中,我曾简要提到过 gRPC,主要是因为 Nacos 的最新版已经采用了 gRPC 作为其核心通信协议。这一变化带来了显著的性能优化,尤其在心跳检测、健康检查等接口的消息传输上,gRPC 可以有效减少网络负担和延迟,从而提高系统的整体效率。
所以,今天我们将简要了解一下 gRPC 这种通信协议是如何运作的,并通过一个简单的 HelloWorld 示例来展示它的基本使用方式。
gRPC
gRPC(全称:gRPC Remote Procedure Call)是一个由 Google 开发的高性能、开源的远程过程调用(RPC)框架,它基于 HTTP/2 协议,并且采用了 Protocol Buffers(Protobuf)作为接口定义语言。gRPC 旨在简化和优化微服务架构中的服务间通信,提供高效、可靠的通信机制,适用于大规模分布式系统。
gRPC 的通信流程大致如下:
- 接口定义:开发者使用 Protobuf 定义服务接口(.proto 文件)。该文件描述了服务的 RPC 方法、请求和响应消息类型。
- 代码生成:使用 Protobuf 编译器(protoc)根据 .proto 文件生成对应语言的客户端和服务器代码。
- 客户端调用:客户端通过 gRPC 客户端 API 调用远程方法,发送请求并接收响应。gRPC 客户端和服务器之间的通信是透明的,客户端只需要像调用本地方法一样调用远程方法。
- 服务器实现:服务器端实现 .proto 文件中定义的 RPC 方法,并通过 gRPC 框架处理客户端的请求。
- 通信协议:gRPC 使用 HTTP/2 协议进行高效的通信,基于 Protobuf 编码和解码请求和响应数据。
快速入门
新建项目
按照我们刚才讨论的通信流程,接下来我们将通过一个简单的示例来实现这一过程。首先,我们需要创建一个新的项目,项目的名称可以根据个人喜好进行命名。如图所示:
项目结构
接下来,我们将在刚才创建的项目中进一步细化结构,我们整体的项目结构如下:
│ pom.xml
│
├─grpc-api
│ │ .gitignore
│ │ pom.xml
│ │
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─org
│ │ │ │ └─xiaoyu
│ │ │ │ │ Main.java
│ │ │ │ └─test
│ │ │ └─proto
│ │ │ hello.proto
├─grpc-client
│ │ pom.xml
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─org
│ │ │ │ └─xiaoyu
│ │ │ │ │ Main.java
│ │ │ └─resources
│ │ │ application.yml
└─grpc-server
│ pom.xml
├─src
│ ├─main
│ │ ├─java
│ │ │ └─org
│ │ │ └─xiaoyu
│ │ │ │ Main.java
│ │ │ └─service
│ │ │ HelloWorldController.java
│ │ └─resources
│ │ application.yaml
跟着上面的目录结构,我们需要创建子项目,如图所示:
接下来,我们将配置父项目与各子项目之间的依赖关系,以确保它们能够正确地协同工作。
项目依赖
父项目依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>org.xiaoyu</groupId>
<artifactId>grpc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>grpc-client</module>
<module>grpc-server</module>
<module>grpc-api</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.51.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.51.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.51.0</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.7.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<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.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}</pluginArtifact>
<!--设置grpc生成代码到指定路径-->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!--生成代码前是否清空目录-->
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 设置多个源文件夹 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<!-- 添加主源码目录 -->
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/src/main/gen</source>
<source>${project.basedir}/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
client项目依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.xiaoyu</groupId>
<artifactId>grpc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>grpc-client</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.xiaoyu</groupId>
<artifactId>grpc-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
server的项目依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.xiaoyu</groupId>
<artifactId>grpc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>grpc-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.xiaoyu</groupId>
<artifactId>grpc-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
proto接口定义
接下来,我们需要生成客户端和服务端的接口定义,并基于这些接口定义自动生成相关的代码。接口定义是整个 gRPC 通信的核心,它将明确服务端提供的 API 接口及其对应的消息格式,这为客户端和服务端之间的通信提供了基础。
为了高效地生成这些接口代码,我们可以借助一些自动化工具和 AI 助手来加速这一过程,简单问下即可。如图所示:
然后复制过来简单改一下,代码如下:
syntax = "proto3";
import "google/protobuf/any.proto";
package org.xiaoyu.test;
option java_multiple_files = true;
option java_package = "org.xiaoyu.test";
option objc_class_prefix = "HelloWorld";
// 定义服务
service Greeter {
// 定义一个 SayHello 方法,接收一个 HelloRequest 类型的请求,并返回一个 HelloReply 类型的响应
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 定义请求消息类型
message HelloRequest {
string name = 1;
}
// 定义响应消息类型
message HelloReply {
string message = 1;
}
完成接口定义后,我们只需通过执行 mvn compile
命令,直接对 API 项目进行编译,即可自动生成与接口定义相关的所有代码类。这个过程将会根据我们在 .proto
文件中定义的 gRPC 服务和消息结构,自动生成相应的客户端和服务端代码,包括 Java 类、存根(stub)、消息类等。
服务端
接下来,如果在编译或生成代码的过程中仍然遇到问题,或者对某些步骤存在疑问,完全可以继续向 AI 助手寻求帮助。AI 助手可以为我们提供针对性的问题解决方案和调试建议,无论是编译错误、依赖问题,还是代码生成后的一些配置问题,都能快速给出指导。如图所示:
在生成了相关代码后,我们可以直接将这些代码复制到 server
项目中,方便我们进行服务端的开发和集成。同样地,当我们开始进行具体业务逻辑的实现时,AI 助手也能发挥重要作用。在实现服务端逻辑时,AI 助手不仅能自动补全代码,还可以基于项目的上下文和需求,智能推荐最佳的实现方式。
最终代码如下,很简单:
@GrpcService
public class HelloWorldController extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
responseObserver.onNext(HelloReply.newBuilder().setMessage("xiaoyu: Hello " + request.getName()).build());
responseObserver.onCompleted();
}
}
服务端配置
我们需要简单配置一下服务端的监听接口,如下:
grpc:
server:
port: 9090
剩下的步骤就是启动我们已经集成了 gRPC 服务的 Spring 项目。当我们运行项目时,Spring Boot 应用会自动加载并初始化相关的 gRPC 服务配置,并开始监听指定的端口。
客户端
在客户端部分,我们需要进行一些手动配置,虽然目前尚未找到能够直接启动并自动配置的注解或工具。在这个阶段,客户端代码的编写相对简单,主要是利用从 API 项目生成的客户端代码来完成对服务端的请求调用。尽管这部分无法完全自动化,我们可以通过直接使用生成的客户端代码来手动构建请求对象,并发起 gRPC 调用,从而实现与服务端的通信。如下:
public class Main {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090)
.usePlaintext()
.build();
// 创建一元式阻塞式存根
GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);
// 创建请求对象
HelloRequest request = HelloRequest.newBuilder()
.setName("World")
.build();
// 发送请求信息并接收响应
HelloReply response = blockingStub.sayHello(request);
// 处理信息响应
System.out.println("Received response: " + response);
}
}
只需直接运行项目,即可成功启动并完成服务的所有配置。与其他服务接口的调用方式类似,我们在这里无需编写任何额外的接口调用代码,看下运行结果,如图所示:
总结
通过本文的讲解,我们了解了 gRPC 作为一种高效的通信协议在微服务架构中的应用,特别是在与 Nacos 集成时带来的性能优化。gRPC 的高效性,得益于其基于 HTTP/2 协议和 Protobuf 的数据传输方式,使得跨服务通信更加迅速且可靠。本文通过 HelloWorld 示例演示了从接口定义到服务端和客户端的实现流程,充分展示了 gRPC 的强大功能和易用性。
掌握这一协议,不仅能够提升服务间的通信效率,也为开发更具扩展性的分布式系统奠定了基础。最终,gRPC 作为微服务架构中的关键组件,其提供的性能优化和便捷性,必将在未来的项目中发挥重要作用。
我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位腾讯云创作之星、阿里云专家博主、华为云云享专家、掘金优秀作者。
我将不吝分享我在技术道路上的个人探索与经验,希望能为你的学习与成长带来一些启发与帮助。
欢迎关注努力的小雨!
零基础入门gRPC:从 0 实现一个Hello World的更多相关文章
- 零基础入门 实战mpvue2.0多端小程序框架
第1章 课程快速预览(必看!!!)在这一章节中,老师讲带领你快速预览课程整体.其中,涉及到为什么要做这么一门实战课程.制作一个小程序的完整流程是怎么样的,以及如何做项目的技术选型. 第2章 30 分钟 ...
- 从零3D基础入门XNA 4.0(2)——模型和BasicEffect
[题外话] 上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上.本文接着上一篇文章继续,介绍XNA中模型的结构.BasicEffect的使用以 ...
- 从零3D基础入门XNA 4.0(1)——3D开发基础
[题外话] 最近要做一个3D动画演示的程序,由于比较熟悉C#语言,再加上XNA对模型的支持比较好,故选择了XNA平台.不过从网上找到很多XNA的入门文章,发现大都需要一些3D基础,而我之前并没有接触过 ...
- 从零基础入门JavaScript(1)
从零基础入门JavaScript(1) 1.1 Javascript的简史 1995年的时候 由网景公司开发的,当时的名字叫livescript 为了推广自己的livescript,搭了j ...
- Cloudera Manager、CDH零基础入门、线路指导 http://www.aboutyun.com/thread-9219-1-1.html (出处: about云开发)
Cloudera Manager.CDH零基础入门.线路指导http://www.aboutyun.com/thread-9219-1-1.html(出处: about云开发) 问题导读:1.什么是c ...
- 【JAVA零基础入门系列】Day2 Java集成开发环境IDEA
开发环境搭建好之后,还需要一个集成开发环境也就是IDE来进行编程.这里推荐的IDE是IDEA,那个老掉牙的Eclipse还是先放一边吧,(手动滑稽). IDEA的下载地址:http://www.jet ...
- 【JAVA零基础入门系列】Day4 变量与常量
这一篇主要讲解Java中的变量,什么是变量,变量的作用以及如何声明,使用变量. 那么什么是变量?对于初学者而言,可以将变量理解为盒子,这些盒子可以用来存放数据,不同类型的数据需要放在对应类型的盒子里. ...
- 【JAVA零基础入门系列】Day5 Java中的运算符
运算符,顾名思义就是用于运算的符号,比如最简单的+-*/,这些运算符可以用来进行数学运算,举个最简单的栗子: 已知长方形的长为3cm,高为4cm,求长方形的面积. 好,我们先新建一个项目,命名为Rec ...
- 【JAVA零基础入门系列】Day6 Java字符串
字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串.Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组.至于什么叫做类,暂时不做过多介绍,在之后的篇章中 ...
- 【JAVA零基础入门系列】Day8 Java的控制流程
什么是控制流程?简单来说就是控制程序运行逻辑的,因为程序一般而言不会直接一步运行到底,而是需要加上一些判断,一些循环等等.举个栗子,就好比你准备出门买个苹果,把这个过程当成程序的话,可能需要先判断一下 ...
随机推荐
- python配置pip镜像
Python配置pip的镜像 国内的网络通过pip下载软件包只有不到10k的下载速度.不仅下载的慢,还容易引发超时错误,导致下载失败.而将给pip配置国内的镜像源可以完美的解决这个问题.本文讲解了pi ...
- 原生JavaScript实现可旋转立方体效果基础示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JavaScript – 数据类型
前言 写着 TypeScript 学习笔记, 顺便也写点 JS 的呗. 参考 JS数据类型分类和判断 阮一峰 – 数据类型 JS 数据类型 string number boolan undefined ...
- 祝福 Eric 的下一段旅程,Flutter 3.3 现已发布
Flutter 团队及社区成员们在美丽的城市挪威奥斯陆向您发来问候,我们正在此参加社区举办的 Flutter Vikings 活动,这是一个为期两天的开发技术交流盛会,虽然线下门票已经售罄,但您还可以 ...
- 系统编程-进程-当文件操作遇上fork
我的关联博文: 系统编程-进程-fork深度理解.vfork简介 系统编程-进程-先后fork或open一个文件的区别 test1: lseek基本使用 #include <stdio.h& ...
- 基于Keras-YOLO实现目标检测
Keras-YOLO 3项目使用Python语言实现了YOLO v3网络模型,并且可以导入Darknet网络预先训练好的权重文件信息直接使用网络进行目标识别. 1. 下载Keras-YOLO 3项目 ...
- Pytorch 基于加权平滑过渡的无缝拼接
基于加权平滑过渡的无缝拼接 背景 在做照片数字人视频生成的时候,为了达到快速响应实时播放的需求,即视频的生成速度 必须小于 音频的播放速度. 因此,我们截取了一部分较小的可动区域进行推理生成,然后把生 ...
- iOS中搜索框EVNCustomSearchBar使用小结
最近在项目开发中用到了搜索框,之前都是用的系统的searchbar,现有项目中用的是EVNCustomSearchBar,我试了一下还挺方便,下面说一下具体的用法. 第一步:引入添加相关的委托代理EV ...
- 小程序的json文件
json文件是页面的描述文件,对本页面的窗口外观设置,页面的配置可以覆盖全局的配置 (app.json);
- 让查询可以使用 json path
记录一下最近sv.db的完善 1. 让查询可以使用 json path 有时候我们会存储 json 到 db,也有时会只取json部分数据,或者通过json部分数据进行过滤 所以sv.db 也支持这些 ...