gRPC远程实现Java调用python服务-demo

标签: java  python

GRPC学习

1 GRPC配合java的简单应用

1.1 项目生成

用IDEA生成的一个Springboot应用。(这边是因为要做一个web项目,也可以只生成一个普通的带maven的java项目)。先放出一个整体的项目结构图,这个结构图的一些文件是整个项目编译(target文件夹中的文件)好才生成的。
在这里插入图片描述

1.2 添加依赖

首先是添加一下grpc对应的依赖。

		<dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.26.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.26.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.26.0</version>
        </dependency>

除了以上方法,还可以像如下的方式添加全面的依赖,但是导入的依赖项比较多。

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-all</artifactId>
    <version>1.26.0</version>
</dependency>

添加完依赖后,需要再添加一个build项,这些build的配置,是我们在做这个GRPC项目之前要用maven先compile一下,不然很多依赖是缺少的。

1.3 编写proto文件

helloworld.proto文件的路径是src/main/java/proto,在我的理解看来,这就是一个配置文件,包括生成的类的路径,内容如下。

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

1.4 服务端

HelloWorldServer.java,其中 io.grpc.examples 是先编译完才有的,一开始没有编译是会报错的。

package com.example.lbh;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;

import java.io.IOException;

public class HelloWorldServer {


    private int port = 50051;
    private Server server;

    private void start() throws IOException {
        server = ServerBuilder.forPort(port)
                .addService(new GreeterImpl())
                .build()
                .start();

        System.out.println("service start...");

        Runtime.getRuntime().addShutdownHook(new Thread() {

            @Override
            public void run() {

                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                HelloWorldServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    // block 一直到退出程序
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        final HelloWorldServer server = new HelloWorldServer();
        server.start();
        server.blockUntilShutdown();
    }

    // 实现 定义一个实现服务接口的类
    private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            System.out.println("service:"+req.getName());
            HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

1.5 客户端

HelloWorldClient.java,与服务端类似,一部分内容都是需要先编译的。

package com.example.lbh;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;


public class HelloWorldClient {



    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

    private final ManagedChannel channel;
    private final GreeterGrpc.GreeterBlockingStub blockingStub;

    /**
     * 用与创建客户端到服务端的通道.
     */
    public HelloWorldClient(String host, int port) {
        this(ManagedChannelBuilder.forAddress(host, port)
                // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
                // needing certificates.
                .usePlaintext()
                .build());
    }

    /**
     * 构造函数.
     */
    HelloWorldClient(ManagedChannel channel) {
        this.channel = channel;
        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }

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

    /**
     * Say hello to server.
     */
    public void greet(String name) {
        logger.info("Will try to greet " + name + " ...");
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        HelloReply response;
        try {
            response = blockingStub.sayHello(request);
        } catch (StatusRuntimeException e) {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        System.out.println("服务端返回结果:"+response.getMessage());
    }

    public static void main(String[] args) throws Exception {
        // 接入可以访问的服务端主机,localhost就是IP地址,50051是端口号
        HelloWorldClient client = new HelloWorldClient("localhost", 50051);
        try {
            for(int i=0;i<5;i++){
                String user = "user"+i;
                // 如果有参数,就把参数当作user进行请求
                if (args.length > 0) {
                    user = args[0];
                }
              	//客户端向服务端请求
                client.greet(user);
            }
        } finally {
            client.shutdown();
        }
    }
}

1.6 compile

完成如上的工作,代码中还是报错的,需要先将项目编译,用maven的complie进行编译。

1.7 运行

分别运行server和client,其中server是会一直运行的,client调用server的seyHello方法。

2 Java-Java的远程调用

刚刚展示的是本地调用的方式,Java服务远程调用Java服务的做法很简单,只需要把项目clone到另一台子网内的服务器,删除掉客户端对应的代码,然后编译一些依赖项,直接运行server即可。

但是请求的客户端在构建通道的时候,需要修改IP地址,因为现在不是访问localhost了。

我用Win作为服务端,mac作为客户端,在编译项目依赖的时候遇到了一个bug,对应的依赖项下载不下来,也没见报错,但是看到路径中的中文变成乱码。之后,将项目移动到英文路径下,就不报错了。

3 Java-Python的远程调用

题主使用的是Java客户端访问python的服务,因此就只需要再写一个python的客户端即可,但是为了更全面的应用,就把python端的客户端和服务端都完成。

与java相同,首先生成一个python项目,项目的目录大概是这样的:
在这里插入图片描述
但是其中的helloworld_pb2.py和helloworld_pb2_grpc.py都是后面编译生成的。只有其他三个文件需要自己写。其他的依赖全都用pip直接install就好。

3.1 依赖项

首先是依赖项的安装,运行如下三个命令即可:

pip3 install grpcio
pip3 install protobuf
pip3 install grpcio-tools

题主之前安装其他内容时已经附带安装了,因此都是已经满足的状态。但是在程序运行时报错,报错是protobuf的某些属性是缺少的,估计是protobuf的版本不够,因此更新了下protobuf,运行如下命令:

pip3 install -upgrade protobuf

3.2 proto文件

这边的proto文件与java的一样,不过是把java的一些参数拿掉了。

syntax = "proto3";
package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

写好这个文件之后需要先编译一下,进入proto目录运行如下命令:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./helloworld.proto

运行完成后会生成helloworld_pb2.py和helloworld_pb2_grpc.py文件。这两个文件在客户端和服务端中需要用到。注意,如果在导包的时候有报错,修改一下导包的路径即可。

3.3 客户端文件

这里的客户端也是用本机做的测试,因此ip就是localhost。

# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# from __future__ import print_function
import grpc
from proto import helloworld_pb2, helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

3.4 服务端文件

# ! /usr/bin/env python
# -*- coding: utf-8 -*-

import time

import grpc
from concurrent import futures
from proto import helloworld_pb2, helloworld_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        print("服务端接收到用户请求:"+request.name)
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    serve()

3.5 测试

题主的测试是在Mac的java端访问Win的python端,结果是可以运行的。

结语

目前只是grpc的学习,之后如果在项目中有深入的使用,会继续更新grpc的学习和使用。

参考:
https://blog.csdn.net/qq_22222499/article/details/103773998
https://blog.csdn.net/whzhaochao/article/details/52421867

版权声明:本文为qq_34651592原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34651592/article/details/109623645

智能推荐

bireme数据源同步工具--debezium+kafka+bireme

1、介绍 Bireme 是一个 Greenplum / HashData 数据仓库的增量同步工具。目前支持 MySQL、PostgreSQL 和 MongoDB 数据源 官方介绍文档:https://github.com/HashDataInc/bireme/blob/master/README_zh-cn.md 1、数据流 Bireme 采用 DELETE + COPY 的方式,将数据源的修改记...

一致性hash算法

散列(hash)在我看来就是一个数组,而与数组不同的点在于数组是按顺序写入的,而hash是按照一定的hash算法确定元素在数组中的位置的。hash最难的问题在于会有冲突出现,如果两个object根据相应的hash算法得出的值一样便产生了hash冲突。在所有解决hash冲突的方法中,我最欣赏的是链式解决法,即将hash到同一位置的元素用链表连接。当然还有其它几种处理hash冲突的算法,比如建立公共溢...

OpenCV-Python learning-1.安装,图片读取显示

1. OpenCV与OpenGL区别 https://www.zhihu.com/question/20212016 一个是让机器识别东西的,OpenCV是给电脑做眼睛的。 一个是让机器计算出更好画面的,OpenGL用在游戏渲染方面很多。 OpenCV(Open Source Computer Vision Library)是一个基于(开源)发行的跨平台计算机视觉库,OpenGL(全写Open G...

Mycat+Mysql分布式架构改造和性能压力测试

架构实现 Mycat作为数据库高可用中间件具备很多的功能,如负载均衡,分库分表,读写分离,故障迁移等。结合项目的实际情况,分库分表功能对于关联查询有很高的要求,需要从业务角度考虑分库分表后的关联查询SQL的分析,业务代码动作较大,所以在此方案中我们不考虑分库分表。主要应用Mycat的负载均衡及故障迁移的功能即可。 整个架构改造包括两个部分,第一是单例Mysql改为多个Mysql,同时负载均衡,并且...

人脸识别之疲劳检测(二)阈值法、KNN分类和K-means聚类

Table of Contents 1、均值法 2、中值法 3、KNN 4、K-means 结合上一节在获得人眼特征点后需要对睁眼闭眼状态做出判断,方法的选择需要经验结合公平的评价方法,使用大量测试集得到不同方法下的精确度并做出比较: 1、均值法 50帧睁眼数据取均值,得到不同阈值下精确度。 2、中值法 50帧睁眼数据取中值,得到不同阈值下精确度。 3、KNN KNN是一种ML常用分类算法,通过测...

猜你喜欢

CodeForce Tic-Tac-Toe

Two bears are playing tic-tac-toe via mail. It's boring for them to play usual tic-tac-toe game, so they are a playing modified version of this game. Here are its rules. The game is played on the foll...

Python雾里看花-抽象类ABC (abstract base class)

首先认识模块 abc,python中没有提供抽象类与抽象方法,然而提供了内置模块abc来模拟实现抽象类,例如提供泛映射类型的抽象类 abc.MutableMapping 继承abc.MutableMapping构造一个泛映射类型(类似python中的dict) 当然继承abc.Mapping 也可以,毕竟MutableMapping是其子类 dict是python中典型的映射类型数据结构,其接口的...

python 文件操作

2, with open (‘xx.txt’,‘w’,encoding=‘utf-8’) as f: f.write(‘文件内容或对象’)...

【Python基础】使用统计函数绘制简单图形

机器学习算法与自然语言处理出品 @公众号原创专栏作者 冯夏冲 学校 | 哈工大SCIR实验室在读博士生 2.1 函数bar 用于绘制柱状图 2.2 函数barh 用于绘制条形图 2.3 函数hist 用于绘制直方图 直方图与柱状图的区别 函数pie 用于绘制饼图 2.5 函数polor 用于绘制极线图 极线图是在极坐标系上绘出的一种图。在极坐标系中,要确定一个点,需要指明这个点距原点的角...

css:顶部按钮固定,上面内容滑动

这种需求我们平时见到很多的,实现方法也多的参差不齐,下面我说一种简单的。如图: 可以看到只有红线部分滚动,底下按钮是固定的。 代码...