跨语言RPC:使用Java客户端调用Go服务端的JSON-RPC服务

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

一、背景介绍

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

二、Go服务端实现

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

Go 复制代码
package main

import (
	"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 " + request
	return 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代码示例:

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); // 请求ID

            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 (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客户端程序。如果一切配置正确,你应该能够看到类似以下的输出结果:

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技术。

相关推荐
JWASX1 小时前
【RocketMQ 生产者和消费者】- 消费者重平衡(1)
java·rocketmq·重平衡
剽悍一小兔1 小时前
自动化文档生成工具(亲测可运行)
java
程序员皮皮林1 小时前
使用 Java + WebSocket 实现简单实时双人协同 pk 答题
java·websocket
栗然1 小时前
Spring Boot 项目中使用 MyBatis 的 @SelectProvider 注解并解决 SQL 注入的问题
java·后端
im_AMBER1 小时前
java复习 19
java·开发语言
陆少枫1 小时前
JDBC强化关键_009_连接池
java·数据库·mysql
安迪小宝1 小时前
2 geotools入门示例
java·spring boot
Moshow郑锴1 小时前
IDEA高效快捷键指南
java·ide·intellij-idea
小猫咪怎么会有坏心思呢1 小时前
华为OD机考-异常的打卡记录-字符串(JAVA 2025B卷)
java·开发语言·华为od
炎码工坊1 小时前
Java 时间处理指南:从“踩坑”到“填坑”实战
java·java-ee