在分布式系统开发中,不同编程语言之间进行通信是一个常见的需求。通过远程过程调用(RPC)技术,我们可以让不同的程序像调用本地方法一样调用远程的服务。本文将介绍如何使用Go语言编写一个简单的JSON-RPC服务,并使用Java作为客户端来跨语言调用这个服务。

一、背景介绍

在之前的文章中,我们已经了解了如何使用Go语言构建一个基本的RPC服务。然而,默认情况下,Go语言的net/rpc包使用的是一种名为Gob的序列化格式,这限制了它只能与支持Gob编码的语言进行交互。为了实现跨语言的RPC调用,我们可以采用更通用的数据交换格式——如JSON。Go语言的net/rpc/jsonrpc包就提供了这样的功能,允许我们在不修改服务逻辑的情况下,轻松地与其他语言进行交互。

二、Go服务端实现

首先,我们需要创建一个Go服务端,该服务端将提供一个简单的“Hello”服务。以下是完整的代码示例:

package mainimport ("fmt""net""net/rpc""net/rpc/jsonrpc"
)// 定义一个服务结构体
type HelloService struct{}// 定义一个远程可调用的方法
func (s *HelloService) Hello(request string, reply *string) error {fmt.Printf("Received request: %s\n", request)*reply = "hello " + requestreturn nil
}func main() {// 监听本地 1234 端口listen, err := net.Listen("tcp", ":1234")if err != nil {panic(err)}defer listen.Close()// 注册服务err = rpc.RegisterName("HelloService", new(HelloService))if err != nil {panic(err)}// 接受连接并处理for {conn, err := listen.Accept()if err != nil {continue}go jsonrpc.ServeConn(conn)}
}

关键点解释:

  • 服务定义:我们定义了一个HelloService结构体,并为其添加了一个Hello方法。这个方法接收一个字符串参数,并返回一个经过处理后的字符串。
  • 注册服务:使用rpc.RegisterName函数将服务注册到RPC框架中,这里我们指定了服务名称为"HelloService"
  • 启动服务:监听指定端口并接受连接,然后使用jsonrpc.ServeConn来处理每一个新的连接。

代码解析

代码片段作用说明
type HelloService自定义的服务结构体,用于封装远程方法
func (s *HelloService) Hello(...)这是一个“导出方法”,可以被外部调用
net.Listen("tcp", ":1234")创建 TCP 监听器,监听本地 1234 端口
rpc.RegisterName("HelloService", ...)将服务注册为 RPC 框架的一部分,服务名是 "HelloService"
jsonrpc.ServeConn(conn)使用 JSON-RPC 协议处理每个连接,自动解析请求、调用方法并返回结果

✅ 总结:Go 服务端本质上是一个 TCP 服务器,使用 JSON-RPC 协议与客户端通信。客户端发送 JSON 请求,服务端执行对应方法后,返回 JSON 响应。

三、Java客户端实现

接下来,我们将编写一个Java客户端来调用上述Go服务端提供的“Hello”服务。以下是完整的Java代码示例:

import org.json.JSONObject;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class GoJsonRpcClient {
import org.json.JSONObject;import java.io.*;
import java.net.Socket;public class GoJsonRpcClient {public static void main(String[] args) throws IOException {String hostname = "127.0.0.1"; // Go服务端IP地址int port = 1234; // Go服务端监听端口try (Socket socket = new Socket(hostname, port)) {OutputStream output = socket.getOutputStream();PrintWriter writer = new PrintWriter(output, true);InputStream input = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(input));JSONObject jsonRequest = new JSONObject();jsonRequest.put("jsonrpc", "2.0");jsonRequest.put("method", "HelloService.Hello");jsonRequest.put("params", new String[]{"Bob"});jsonRequest.put("id", 1);writer.println(jsonRequest.toString());String jsonResponse = reader.readLine();if (jsonResponse != null && !jsonResponse.isEmpty()) {JSONObject jsonObject = new JSONObject(jsonResponse);System.out.println("Received response: " + jsonObject.toString());if (jsonObject.has("result")) {System.out.println("Result: " + jsonObject.getString("result"));}} else {System.out.println("No response or empty response.");}} catch (IOException e) {throw new RuntimeException(e);}}
}public static void main(String[] args) {String hostname = "127.0.0.1"; // Go服务端IP地址int port = 1234; // Go服务端监听端口try (Socket socket = new Socket(hostname, port)) {PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));JSONObject jsonRequest = new JSONObject();jsonRequest.put("jsonrpc", "2.0");jsonRequest.put("method", "HelloService.Hello");jsonRequest.put("params", new String[]{"Bob"}); // 请求参数jsonRequest.put("id", 1); // 请求IDwriter.println(jsonRequest.toString());String jsonResponse = reader.readLine();if (jsonResponse != null && !jsonResponse.isEmpty()) {JSONObject jsonObject = new JSONObject(jsonResponse);System.out.println("Received response: " + jsonObject.toString());if (jsonObject.has("result")) {System.out.println("Result: " + jsonObject.getString("result"));}} else {System.out.println("No response or empty response.");}} catch (Exception e) {throw new RuntimeException(e);}}
}

关键点解释:

  • 请求构建:我们使用org.json.JSONObject来构造符合JSON-RPC规范的请求对象。在这个例子中,我们的请求包含了一个方法名(对应于Go服务端的Hello方法)、一组参数以及一个唯一的请求ID。
  • 发送请求:通过套接字连接向Go服务端发送构造好的请求。
  • 接收响应:从输入流中读取服务端返回的响应,并解析其中的结果字段。

代码解析

代码片段作用说明
Socket socket = new Socket(hostname, port)创建一个 TCP 连接到 Go 服务端
PrintWriter writer = new PrintWriter(output, true)获取输出流,用于向服务端发送请求
BufferedReader reader = new BufferedReader(...)获取输入流,用于接收服务端响应
JSONObject jsonRequest = new JSONObject()构造 JSON 请求对象
jsonRequest.put("method", "HelloService.Hello")设置要调用的服务方法
jsonRequest.put("params", new String[]{"Bob"})设置参数列表
writer.println(jsonRequest.toString())发送请求
String jsonResponse = reader.readLine()读取服务端返回的 JSON 响应
jsonObject.getString("result")提取返回值中的 result 字段

✅ 总结:Java 客户端通过 Socket 建立 TCP 连接,构造符合 JSON-RPC 规范的请求,发送给 Go 服务端,并等待响应。

四、测试与验证

确保Go服务端正在运行后,执行Java客户端程序。如果一切配置正确,你应该能够看到类似以下的输出结果:

Received response: {"jsonrpc":"2.0","result":"hello Bob","id":1}
Result: hello Bob

这表明Java客户端成功地调用了Go服务端的“Hello”方法,并收到了预期的响应。

五、总结

JSON-RPC 的核心思想

角色功能
服务端(Go)监听 TCP 端口,接收 JSON-RPC 请求,调用本地方法,返回 JSON-RPC 响应
客户端(Java)建立 TCP 连接,构造 JSON-RPC 请求,发送并读取响应
关键点使用 JSON 作为数据格式,TCP 作为传输层,实现跨语言通信

通过这篇文章,我们学习了如何使用Go语言构建一个JSON-RPC服务端,并使用Java作为客户端进行跨语言调用。这种方法不仅打破了语言之间的界限,还利用了JSON这种轻量级的数据交换格式,使得不同平台和语言之间的集成变得更加简单高效。

关于使用JSON-RPC替换RPC原本的序列化协议Gob的目的和优点可以看上一篇内容。

希望这篇博客能帮助你在实际项目中更好地应用RPC技术。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE5 创建AI控制器、AI行为树和黑板

UE5 创建AI控制器、AI行为树和黑板 一、创建AI控制器AIController(大脑) 二、创建AI行为树和黑板 1:AI人工智能 2:行为树 3:黑板 三、AI行为树蓝图和添加黑板 1:添加黑板(脑电波)…

CDN加速导致CLS升高图片托管服务器的3个选择标准!

许多网站为了提升加载速度,会采用CDN加速服务分发图片等静态资源 这样做可能导致CLS(累积布局偏移)指标升高,拖累SEO评分。 这一问题通常源于CDN的异步加载机制或图片尺寸未预定义,使得页面布局在渲染过程中频繁变动。…

MySQL(77)如何设置自动备份任务?

设置自动备份任务可以确保你的数据库定期备份,防止数据丢失。以下是如何使用 Bash 脚本和 Cron 任务在 Linux 系统上设置 MySQL 数据库的自动备份任务的详细步骤和代码示例。 1. 编写备份脚本 首先,我们需要编写一个备份脚本。这个脚本将包含执行备份的…

.NET 开发中全局数据存储的几种方式

文章目录 一、静态类与静态成员实现方式特点优缺点 二、应用程序配置系统1. appsettings.json (ASP.NET Core)使用方式2. 用户设置 (WinForms/WPF)特点 三、依赖注入容器ASP.NET Core 示例特点 四、内存缓存 (IMemoryCache)实现方式特点 五、分布式缓存 (IDistributedCache)实现…

人才争夺战关键期,AI如何赋能招聘效率倍增、精准选拔

数智化转型浪潮席卷全球的今天,人才作为企业核心竞争力的地位日益凸显。而在传统招聘流程,尤其是面试环节正面临效率瓶颈、体验短板等多项挑战,典型如: 耗时冗长的筛选与安排;难以避免的主观评价偏差;海量…

介绍下分布式ID的技术实现及应用场景

什么是分布式ID 分布式ID是指在分布式系统中生成的特定范围内唯一的标识符,如订单号、商品ID、链路追踪TraceID。 随着业务发展,对分布式ID的要求越来越高,其中最基本的要求如下 全局唯一:在任何节点、任何时间生成的ID都必须是…

【leetcode-字母异位词分组】

排序法 public List<List<String>> groupAnagrams(String[] strs) {//最终值List<List<String>> result new ArrayList<>();//排序法HashMap<String,List<String>> map new HashMap<>(); //遍历strfor(String str : strs){/…

langchain从入门到精通(九)——ChatGPT/Playground手动模拟记忆功能

1. 摘要缓冲混合记忆 摘要缓冲混合记忆中&#xff0c;所需的模块有&#xff1a; chat_message_history&#xff1a;存储历史消息列表。moving_summary_buffer&#xff1a;移除消息的汇总字符串。summary_llm&#xff1a;生成摘要的 LLM&#xff0c;接收 summary&#xff08;当…

docker单点安装Hadoop

1、Docker中拉取jdk8镜像 拉取镜像 docker pull openjdk:8-jdk 查看jdk docker run -it openjdk:8-jdk bash which java 2、安装ubuntu源 拉取镜像 docker pull ubuntu:22.04 保存 docker save -o ubuntu-22.04.tar.gz ubuntu:22.04 移动到自己想要的目录 mv /roo…

uniapp项目之小兔鲜儿小程序商城(二) 首页的实现:自定义导航栏,轮拨图,前台分类,热门推荐,猜你喜欢,下拉刷新,骨架屏

文章目录 零.首页最终效果一.自定义导航栏1.新建pages/index/components/CustomNavbar.vue首页子组件2.在首页pages/index/index.vue中引入3.隐藏默认导航栏修改标题颜色4.适配不同机型使用到了uniapp的一个api:获取屏幕边界到安全区域的距离在子组件中使用 二.轮拨图1.新建 sr…

RustDesk自建远程服务器

目录 服务端 环境linux 安装 开放端口 客户端配置 下载客户端 安装后配置网络 参考&#xff1a;RustDesk自建远程服务器_rustdesk自建服务器-CSDN博客 服务端 环境 linux 安装 下载 wget https://github.com/rustdesk/rustdesk-server/releases/download/1.1.8-2/r…

【Axure高保真原型】图片伸缩展示列表

今天和大家分享图片伸缩展示列表的3个原型案例&#xff0c;模版都是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;在中继器表格里导入对应的图片&#xff0c;即可自动生成交互效果&#xff0c;具体效果可以点击下方视频观看或打开下方预览地址查看哦 【原型效果】 …

keil新建工程文件结构和每个文件的作用解析(标准库版本)

通过网盘分享的文件:STM32工程模板 链接:https://pan.baidu.com/s/1YPFgXu1kwuwsCVxrXFSjZg?pwd=1111 提取码: 1111 --来自百度网盘超级会员v5的分享 这个工程模版是来源于B站江科大的模版,每个人搭建工程文件结构不一样,仅供参考。 工程文件目录结构如图所示 1、DebugC…

【AI论文】Saffron-1:LLM安全保证的推理缩放范例

摘要&#xff1a;现有的安全保证研究主要集中在培训阶段的协调&#xff0c;以向LLM灌输安全行为。 然而&#xff0c;最近的研究表明这些方法容易受到各种越狱攻击。 同时&#xff0c;推理扩展显著提高了LLM推理能力&#xff0c;但在安全保证方面仍未得到探索。 为了解决这一差距…

LLM 支持的基于意图的分类 网络钓鱼电子邮件

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 网络钓鱼攻击仍然是现代网络安全的重大威胁&#xff0c;因为它们成功地欺骗了人类和旨在保护他们的防御机制。传统的检测系统主要关注用户在收件箱中看不到的电子邮件元数据。此外&#xff0c;这些…

C++新特性技术发展路径和时间

C 的新特性发展路径和时间线是一个持续演进的过程。以下是一个概览&#xff0c;涵盖了主要的 C 标准及其关键特性&#xff0c;以及它们发布的时间&#xff1a; C 标准版本及发布时间线: C98 (ISO/IEC 14882:1998): 第一个正式的 C 标准。 发布时间: 1998年关键特性: 标准模板库…

OpenAI 如何在激烈的AI人才争夺战中抢占先机?

在这个快速发展的人工智能时代&#xff0c;OpenAI 正处于一个至关重要的发展阶段。随着技术的不断进步&#xff0c;人工智能行业的竞争日益激烈。如何在这场巨大的竞争中立于不败之地&#xff0c;成为了每一个AI公司的核心挑战。就在近日&#xff0c;OpenAI 的新招聘主管华金・…

【Java学习笔记】Java绘图基础

Java绘图基础 一、Java 坐标体系 1. 像素的概念 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的 例如&#xff0c;计算机显示器的分辨率是 800600&#xff0c;表示计算机屏幕上的每一行由 800 个点组成&#xff0c;共有 600 行&#xff0c;整个计算机屏幕共有 480…

资深Java工程师的面试题目(一)基础到高级概述

以下是几道面向资深Java工程师的面试题目&#xff0c;涵盖了从基础知识到高级概念及参考答案&#xff1a; 1. Java内存模型和垃圾回收 问题: 请解释一下Java的内存模型&#xff0c;并描述不同类型的内存区域。如何选择适合特定应用需求的垃圾收集器&#xff1f;请比较几种常…

Spring Retry:优雅地实现方法重试机制

前言 在实际的软件开发中&#xff0c;尤其是在涉及网络请求、数据库操作或外部服务调用的场景下&#xff0c;我们常常会遇到一些临时性故障&#xff08;Transient Failures&#xff09;&#xff0c;例如网络波动、数据库连接超时、第三方 API 暂时不可用等。面对这些问题&…