【项目实战】SpringBoot整合Protobuf,实现基于RestTemplate的模拟客户端与服务端的远程调用

news2024/11/25 23:17:02

一、背景说明

项目中使用到了Protobuf,但是因为不知道怎么使用它,所以看起来很高大上,现在写一个简单的Demo来实现基于RestTemplate的模拟客户端与服务端的远程调用!

二、实操代码

2.1 定义依赖POM文件

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.3.2.RELEASE</version>
    </dependency>
    <!-- Protobuf 依赖 -->
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.19.4</version>
    </dependency>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java-util</artifactId>
        <version>3.19.4</version>
    </dependency>
    <dependency>
        <groupId>com.googlecode.protobuf-java-format</groupId>
        <artifactId>protobuf-java-format</artifactId>
        <version>1.4</version>
    </dependency>
    <!-- 网络请求依赖 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
        <version>4.4.15</version>
        <optional>true</optional>
    </dependency>
</dependencies>

2.2 定义protobuf-maven-plugin

该配置先使用os-maven-plugin插件,该插件主要是为了识别不同的操作系统,这样插件可以根据不同的平台加载不同protoc编译器文件。google的protobuf团队也是用该插件来识别protoc的运行操作系统信息

<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.6.1</version>
    <configuration>
        <!-- protoc安装位置 -->
        <protocExecutable>
            D:\Project\protobuf\protoc-3.19.4-win64\bin\protoc.exe
        </protocExecutable>
        <pluginId>protoc-java</pluginId>
        <!-- proto文件放置的目录 -->
        <protoSourceRoot>${project.basedir}/src/main/resources/proto</protoSourceRoot>
        <!-- 生成文件的目录 -->
        <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
        <!-- 生成文件前是否把目标目录清空, 这个最好设置为false, 以免误删项目文件 -->
        <clearOutputDirectory>false</clearOutputDirectory>
    </configuration>
</plugin>

2.3 定义BurtProtoController

@RestController
public class BurtProtoController {
    private final Logger logger = LoggerFactory.getLogger(BurtProtoController.class);

    @Resource
    private RestTemplate restTemplate;

    @PostMapping(value = "/protobuf/message", consumes = "application/x-protobuf", produces = "application/x-protobuf")
    public BurtProto.Response message(HttpEntity<BurtProto.Request> requestPayload) {
        BurtProto.Request request = requestPayload.getBody();
        logger.info("Received following request: \n{}", request);
        BurtProto.Resource requestResource = BurtProtoParser.extractResource(request.getResourcesList(), "AAAAA");
        logger.info("BurtProto.Resource: AAAAA: \n{}", requestResource);

        BurtProto.Resource responseResource = BurtProto.Resource.newBuilder()
                .setChannelId(requestResource.getChannelId())
                .setValue("Successful")
                .build();

        return BurtProto.Response.newBuilder()
                .addResources(responseResource)
                .build();
    }

    @GetMapping(value = "/helloProtoBuf")
    public BurtProto.Response test() {
        String url = "http://127.0.0.1:8080/protobuf/message";

        BurtProto.Resource responseResource000 = BurtProto.Resource.newBuilder()
                .setChannelId("AAAAA")
                .setValue("AAAAA ChannelId Request")
                .build();

        BurtProto.Resource responseResource001 = BurtProto.Resource.newBuilder()
                .setChannelId("BBBBB")
                .setValue("BBBBB ChannelId Request")
                .build();

        BurtProto.Request request = BurtProto.Request.newBuilder()
                .addAllResources(Lists.newArrayList(responseResource000, responseResource001))
                .build();
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, "application/x-protobuf");
        HttpEntity<BurtProto.Request> httpEntity = new HttpEntity<>(request, headers);
        final ResponseEntity<BurtProto.Response> responseEntity = restTemplate.postForEntity(url, httpEntity, BurtProto.Response.class);
        logger.info("ResponseEntity: \n{}", responseEntity.getBody());
        return responseEntity.getBody();
    }
}

2.4 定义BurtProto

syntax = "proto3";

package com.demo.protobuf;

option java_package = "com.demo.protobuf.proto";
option java_outer_classname = "BurtProto";

message Request {
    repeated Resource resources = 1;
}

message Response {
    repeated Resource resources = 1;
}

message Resource {
    string channelId = 1;
    string value = 2;
}

这个由IDEA生成即可,参考文章
【项目实战】Protobuf入门介绍以及如何生成proto对象文件

2.5 定义MessageProtoParser

public class MessageProtoParser {

    private MessageProtoParser() {
    }

    public static MessageProto.Resource extractResource(List<MessageProto.Resource> resources, String channelId) {
        return resources.stream()
                .filter(res -> Objects.equals(channelId, res.getChannelId()))
                .findFirst()
                .orElse(null);
    }
}

2.6 添加protobuf序列化支持

@EnableWebMvc
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(protobufHttpMessageConverter());
        converters.add(mappingJackson2HttpMessageConverter());
    }
    /**
     * protobuf 序列化
     */
    @Bean
    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }
    /**
     * protobuf 反序列化
     */
    @Bean
    public RestTemplate restTemplate(ProtobufHttpMessageConverter protobufHttpMessageConverter) {
        return new RestTemplate(Lists.newArrayList(protobufHttpMessageConverter));
    }
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        return new MappingJackson2HttpMessageConverter();
    }
}

三、功能验证

看到响应体的内容返回为protobuf格式的内容。说明完成了一次请求与交互
在这里插入图片描述
注意Content-Type是:application/x-protobuf

这个格式是APIFOX不支持的格式哦、

在这里插入图片描述

以下是控制台中所有的输出,

2023-03-09 23:04:25.925  INFO 26644 --- [nio-8080-exec-2] c.d.p.controller.BurtProtoController     : Received following request: 
resources {
  channelId: "AAAAA"
  value: "AAAAA ChannelId Request"
}
resources {
  channelId: "BBBBB"
  value: "BBBBB ChannelId Request"
}

2023-03-09 23:04:25.926  INFO 26644 --- [nio-8080-exec-2] c.d.p.controller.BurtProtoController     : BurtProto.Resource: AAAAA: 
channelId: "AAAAA"
value: "AAAAA ChannelId Request"

2023-03-09 23:04:25.928  INFO 26644 --- [nio-8080-exec-3] c.d.p.controller.BurtProtoController     : ResponseEntity: 
resources {
  channelId: "AAAAA"
  value: "Successful"
}

解释一下,A发送给B一段内容,然后B将其打印出来,内容如下

resources {
  channelId: "AAAAA"
  value: "AAAAA ChannelId Request"
}
resources {
  channelId: "BBBBB"
  value: "BBBBB ChannelId Request"
}

B调用了BurtProtoParser下的extractResource方法,使用了流操作,并且只取了第一条数据findFirst(),并将其打印出来

2023-03-09 23:04:25.926  INFO 26644 --- [nio-8080-exec-2] c.d.p.controller.BurtProtoController     : BurtProto.Resource: AAAAA: 
channelId: "AAAAA"
value: "AAAAA ChannelId Request"

B将内容从“AAAAA ChannelId Request”改成了“Successful” 并且变成了响应体,传回给了A。A收到响应体的内容,并将其打印

2023-03-09 23:04:25.928  INFO 26644 --- [nio-8080-exec-3] c.d.p.controller.BurtProtoController     : ResponseEntity: 
resources {
  channelId: "AAAAA"
  value: "Successful"
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

花菁染料IR:IR808 Maleimide,IR 808 Mal,IR-808马来酰亚胺具有光稳定性好

【中文名称】IR-808马来酰亚胺【英文名称】 IR-808 Maleimide&#xff0c;IR-808 Mal【IR-808结 构 式】【CAS号】N/A【基团部分】IR-808【纯度标准】95%【包装规格】10mg&#xff0c;25mg&#xff0c;50mg&#xff0c;可以提供核磁图谱及相关质量证明图谱【是否接受定制】是【…

自动组策略(GPO)备份工具

Active Directory&#xff08;AD&#xff09;中的组策略对象&#xff08;GPO&#xff09;在控制用户和计算机账户的工作环境中起着至关重要的作用。单个GPO可以包含数百或数千个设置。它还可以链接到OU或域&#xff0c;从而控制位于其中的用户或计算机。当这样一个功能强大的AD…

C++:异常

在学习异常之前&#xff0c;来简单总结一下传统的处理错误的方式&#xff1a; 1. 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除0错误时就会终止程序。 2. 返回错误码&#xff0c;缺陷&#xff1a;需要程序员自己去查找…

利用蜜罐捕捉攻击实验(31)

预备知识 1、蜜罐的含义和作用 蜜罐(Honeypot)是一种在互联网上运行的计算机系统。它是专门为吸引并诱骗那些试图非法闯入他人计算机系统的人(如电脑黑客)而设计的&#xff0c;蜜罐系统是一个包含漏洞的诱骗系统&#xff0c;它通过模拟一个或多个易受攻击的主机&#xff…

数据分析:基于K-近邻(KNN)对Pima人糖尿病预测分析

数据分析&#xff1a;基于K-近邻(KNN)对Pima人糖尿病预测分析 作者&#xff1a;AOAIYI 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;AOAIYI首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x…

小型双轮差速底盘三灰度循迹功能的实现

1. 功能说明 在小型双轮差速底盘样机前方安装3个 灰度传感器 &#xff0c;实现机器人沿下图所指定的跑道路线进行运动的效果。 2. 使用样机 本实验使用的样机为R023样机。 3. 功能实现 3.1 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#…

likeshop社区团购v1.5.1更新

likeshop社区团购系统适用于初创业或者个人创业者&#xff0c;希望以低成本、高效益的方式开展本地电商业务&#xff0c;或者已经拥有线下实体店或实体店经验的商家&#xff0c;希望将线上业务扩展到社区团购平台&#xff0c;提升线上业务的营收。 likeshop社区团购V1.5.1版本…

第十五章 opengl之高级OpenGL(模板测试)

OpenGL模板测试模板函数物体轮廓模板测试 当片段着色器处理完一个片段后&#xff0c;模板测试就会开始执行。类似于深度测试&#xff0c;模板测试也可能会丢弃片段。被保留的片段会进入深度测试&#xff0c;可能会丢弃更多的片段。 模板测试是根据模板缓冲来进行的。一个模板缓…

什么是ChatGPT?

目录前言一、什么是GPT&#xff1f;二、什么是ChatGPT&#xff1f;三、ChatGPT应用场景四、ChatGPT未来展望五、OpenAI介绍前言 3月3号&#xff0c;早上6:30就有人发消息给我&#xff0c;来问我有关GPT API的事件。 那是因为3月2号&#xff0c;OpenAI 发布了ChatGPT 3.5的开放…

提高办公效率的免费网站有哪些

收藏一些免费好用的网站&#xff0c;在我们工作中需要用到的时候可以直接使用&#xff0c;提高我们的工作效率。小编就和大家分享10个可以提高我们办公效率的免费网站。 1.羽兔网软件下载-以设计类软件为主的免费软件下载网站 很多小白都不知道怎么下载软件&#xff0c;往往搜…

【Python】AES加解密代码,文章还有加密串等你来解密,等你来挑战

&#x1f366;&#x1f366;写这篇AES文章也是有件趣事&#xff0c;有位小伙伴发了段密文&#xff0c;看看谁解密速度快&#xff0c;学过Python的小伙伴一下子就解开来了&#xff0c;内容也挺有趣的。 &#x1f35f;&#x1f35f;原来加解密也可以这么有趣&#xff0c;虽然看起…

【专项训练】字典树和并查集

字典树和并查集 二叉搜索树查询效率更高 前缀推后面,这种情况怎么办,选什么数据结构呢 每个节点不存单词本身!他只存到下一个路径上面,这个路径代表的字符! Trie树是多叉树! 任何一个节点,他代表的单词是走过的这条边 统计频次 208. 实现 Trie (前缀树)

ViewBinding 和 DataBinding的使用

1.ViewBinding:视图绑定 通过视图绑定功能&#xff0c;您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后&#xff0c;系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。在大多数情况下&…

容器方式搭建免费的表白网站--阿狸心图--背景音乐《英文歌》(2023.310更新)

效果图 源码 源文件:https://download.csdn.net/download/zhanremo3062/87554456 目录结构 ├── ali2 │ ├── css │ │ └── all.min.css │ ├── fonts │ │ └── RuiHeiXiTi.otf │ ├── img │ │ ├── 14915.jpg . . . │ ├── index.html │ ├──…

【JVM】字节码指令

文章目录1. 方法的执行流程1.1 常量池载入运行时常量池1.2 方法字节码载入方法区1.3 main线程开始运行&#xff0c;分配栈帧内存1.4 执行引擎开始执行字节码2. 条件判断2.1 源码分析3. 循环控制指令3.1 源码分析1. 方法的执行流程 原始Java代码 public class Demo3_1 {public…

vue el-switch 列表开关状态显示有误 全部关闭的问题

后台使用int类型传状态status的值 但是前端列表展示的开关状态是未开启&#xff0c;实际上&#xff0c;后台传的都是开启的状态 结果应该是这样 确定后台传的status值 在 el-switch 标签中是否使用了正确的值判断&#xff0c;比如 后台用的是字符串、布尔 或者是 数值类型&…

2-7 SpringCloud快速开发入门: Eureka 注册中心高可用集群搭建

接上一章节Eureka 服务注册中心发现与消费服务&#xff0c;这里讲讲Eureka 注册中心高可用集群搭建 Eureka 注册中心高可用集群搭建 Eureka 注册中心高可用集群就是各个注册中心相互注册 Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己&#xff0c…

LeetCode——203. 移除链表元素

对于初学链表的学者来学&#xff0c;链表是比较困难的&#xff0c;这部分对指针结构体的要求比较高。我们通过练习是掌握知识的重要途经203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09;我们在数组中去除某元素是遍历一遍数组&#xff0c;如果某位置是要去除的元素&a…

[Linux]应用部署部分流程命令备忘

备忘一下常用的Linxu应用部署命令&#xff0c;Java应用版。 目录1、环境查询1.1、端口占用查询1.2、环境变量查询与设置设置局部用户定义变量设置全局环境变量删除环境变量2、执行命令保存日志并查看3、查看java应用内存使用情况1、环境查询 1.1、端口占用查询 lsof -i 命令 …

leetcode-每日一题-2379(简单,字符串)

久违的简单题......给你一个长度为 n 下标从 0 开始的字符串 blocks &#xff0c;blocks[i] 要么是 W 要么是 B &#xff0c;表示第 i 块的颜色。字符 W 和 B 分别表示白色和黑色。给你一个整数 k &#xff0c;表示想要 连续 黑色块的数目。每一次操作中&#xff0c;你可以选择…