NodeJs 与 Java 服务互调

调用模式

Http

最轻量,选择最普遍。

需要在Node.js和Java上实现相应的服务端代码,用于处理HTTP请求和响应。对于Node.js,你可以使用Express框架或其他框架来实现服务端。对于Java,你可以使用Spring框架或其他框架来实现服务端。

RPC

gRPC 官方文档中文版_V1.0

在使用gRPC进行跨语言通信时,不同语言之间的数据类型可能存在差异,需要进行相应的转换。例如,Java中的字符串和Node.js中的字符串可能需要进行编码和解码操作。同时,在使用gRPC进行通信时,还需要考虑一些其他因素,例如服务发现、负载均衡、安全认证等

基本步骤:

1.定义协议
使用Protocol Buffers定义跨语言的接口协议。Protocol Buffers是Google开发的一种轻量级、高效的序列化格式和RPC协议。

例如,在Node.js中,可以使用以下代码定义一个简单的协议:

protobuf
syntax = "proto3";

message Request {
  string name = 1;
}

message Response {
  string message = 1;
}

service Greeter {
  rpc SayHello (Request) returns (Response);
}

2.生成代码
使用Protocol Buffers编译器将协议文件生成相应的代码。例如,在Node.js中,可以使用以下命令生成协议文件的客户端和服务端代码:

grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./generated --grpc_out=./generated --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` greeter.proto

3.实现服务端
在Node.js中实现服务端代码。例如,可以使用以下代码实现一个简单的服务端:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const PROTO_PATH = './greeter.proto';
const packageDefinition = protoLoader.loadSync(PROTO_PATH);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const greeterProto = protoDescriptor.Greeter;

function sayHello(call, callback) {
  const name = call.request.name;
  const message = `Hello, ${name}!`;
  callback(null, { message });
}

function main() {
  const server = new grpc.Server();
  server.addService(greeterProto.service, { sayHello });
  server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
    server.start();
  });
}
main();

4.实现客户端
在Java中实现客户端代码。例如,可以使用以下代码实现一个简单的客户端:

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

import com.example.grpc.GreeterGrpc;
import com.example.grpc.GreeterOuterClass;

public class GreeterClient {
  private final ManagedChannel channel;
  private final GreeterGrpc.GreeterBlockingStub blockingStub;

  public GreeterClient(String host, int port) {
    channel = ManagedChannelBuilder.forAddress(host, port)
        .usePlaintext()
        .build();
    blockingStub = GreeterGrpc.newBlockingStub(channel);
  }

  public void shutdown() throws InterruptedException {
    channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
  }

  public void greet(String name) {
    GreeterOuterClass.Request request = GreeterOuterClass.Request.newBuilder().setName(name).build();
    GreeterOuterClass.Response response;
    try {
      response = blockingStub.sayHello(request);
      System.out.println(response.getMessage());
    } catch (StatusRuntimeException e) {
      System.out.println("RPC failed: " + e.getStatus());
    }
  }

  public static void main(String[] args) throws Exception {
    GreeterClient client = new GreeterClient("localhost", 50051);
    try {
      String user = "world";
      if (args.length > 0) {
        user = args[0];
      }
        client.greet(user);
	} finally {
  		client.shutdown();
    }
  }
}

5.运行程序

在Node.js中启动服务端程序,然后在Java中启动客户端程序,两个程序就可以通过gRPC进行通信了。

JNI

nodejs-java 运行原理:使用 C++ 通过 jvmti JNI 去调用 java 的类,nodejs 再调用 C++,用 C++ 做中转实现的一个功能。JNI 是 Java 本地开发接口,JNI 是一个协议,这个协议用来沟通 Java 代码和外部的本地代码 (C/C++),通过这个协议,Java 代码就可以调用外部的 C/C++ 代码,外部的 C/C++ 代码也可以调用 Java 代码,Android 系统源码中随处可见 JNI 的使用。

使用JNI进行跨语言调用可能会涉及到复杂的数据类型转化,如果只是 nodejs 和 java 互调的场景不建议使用,过于复杂会有性能瓶颈

基本步骤:

  1. 编写 C++ 代码实现 Node.js 的功能,并将其编译成动态链接库(DLL)。

  2. 编写 Java 代码,并使用 JNI 库将 Java 程序连接到动态链接库。

详细的步骤:

1.编写C++代码
假设您已经有一个用 C++ 编写的 Node.js 程序,您需要将其编译成一个动态链接库。请确保您在编译时使用了正确的编译器和选项。

例如,如果您使用的是 gcc 编译器,可以使用以下命令编译成动态链接库:

gcc -shared -o libnode.so node.cpp -I/usr/include/node -L/usr/lib -lnode

其中,-I选项指定了Node.js头文件的路径,-L选项指定了Node.js库文件的路径,-l选项指定了Node.js库的名称。

2.编写Java代码
接下来,您需要编写Java代码,使用JNI库将Java程序连接到动态链接库。

在Java中,您可以使用java.lang.System.loadLibrary()方法加载动态链接库。例如:

public class NodeWrapper {
    static {
        System.loadLibrary("node");
    }

    public static native void startNode();
}

其中,static代码块中的System.loadLibrary()方法会加载动态链接库,而startNode()方法则是用于调用C++代码的本地方法。

3.生成头文件
使用Java的javah命令可以生成JNI头文件,该文件包含了Java程序与C++程序之间的接口。例如:

javah -jni NodeWrapper

将生成一个名为NodeWrapper.h的头文件。

4.实现本地方法
使用C++实现Java程序中声明的本地方法。这些方法的名称和参数必须与头文件中声明的方法相匹配。例如:

#include 

JNIEXPORT void JNICALL Java_NodeWrapper_startNode(JNIEnv *env, jclass cls) {
    // 在这里编写调用Node.js程序的代码
}

其中,Java_NodeWrapper_startNode()方法是Java程序中声明的本地方法的C++实现。

5.编译C++代码
使用C++编译器编译C++代码,并将其链接到动态链接库。例如:

gcc -shared -o libnode.so NodeWrapper.cpp -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L/usr/lib -lstdc++

其中,-I选项指定了Java头文件的路径,-L选项指定了C++库文件的路径,-l选项指定了C++库的名称。

6.运行Java程序
将生成的动态链接库和Java程序一起运行,例如:

java -Djava.library.path=. NodeWrapper

其中,-Djava.library.path选项指定了动态链接库的路径。

你可能感兴趣的:(服务架构,java,开发语言)