Java-gRPC-Python开发环境安装配置

2023-06-2621:53:12后端程序开发Comments1,342 views字数 25127阅读模式

配置开发环境,跑通从Java EE Tomcat Web应用程序通过gRPC调用服务器Python程序的例子HelloWorld(gRPC自带),然后配置好SSL加密连接及调用用户验证,后面有时间再写一个调用业务功能的例子。因为是严谨的紧耦合方式,建立通道的过程有点繁琐,每增加一个功能函数就要改一堆源码走一串流程,使用上就没有嵌入Shiny APP 或Rserve调用R程序方便,或者用R通过Reticulate调用Python更方便一点。Anyway,架构图上所有的通道都调通了,任督二脉全线贯通,这是一个有竞争力的开源解决方案,显然并不局限于开源产品,商业产品也是可以参考的,主要的价值在于为有效盘活存量的软硬件与数据资产提供了高性价比的解决方案。道理越辩越明,以理服人,按需选用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

可以访问在线示例地址具体了解一下。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
任督二脉全线贯通

一、gRPC Python入门文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

安装,参阅资料文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# python -m pip install grpcio
# python -m pip install grpcio-tools
(base) [root@VM-4-12-centos notebook]# python -m pip install grpcio-tools
Looking in indexes: http://mirrors.tencentyun.com/pypi/simple
Collecting grpcio-tools
  Downloading http://mirrors.tencentyun.com/pypi/packages/3a/51/aeb8418cb5e336aa8df2e6ea55bdab3e041b39a8aa2850df81fcc279aaf5/grpcio_tools-1.51.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB)
     |████████████████████████████████| 2.4 MB 459 kB/s 
Collecting grpcio>=1.51.1
  Downloading http://mirrors.tencentyun.com/pypi/packages/fc/62/bccba142a6ad670727fb329579d18ab44e8b3585b23068baa0b7196b86b6/grpcio-1.51.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.8 MB)
     |████████████████████████████████| 4.8 MB 376 kB/s 
Collecting protobuf<5.0dev,>=4.21.6
  Downloading http://mirrors.tencentyun.com/pypi/packages/4f/e0/9e8b77d79996f86e1ecf6622a693b5b116ea392f23c3758de5e902ba84ad/protobuf-4.21.10-cp37-abi3-manylinux2014_x86_64.whl (408 kB)
     |████████████████████████████████| 408 kB 514 kB/s 
Requirement already satisfied: setuptools in /usr/lib64/anaconda3/lib/python3.9/site-packages (from grpcio-tools) (58.0.4)
Installing collected packages: protobuf, grpcio, grpcio-tools
  Attempting uninstall: protobuf
    Found existing installation: protobuf 3.20.1
    Uninstalling protobuf-3.20.1:
      Successfully uninstalled protobuf-3.20.1
  Attempting uninstall: grpcio
    Found existing installation: grpcio 1.45.0
    Uninstalling grpcio-1.45.0:
      Successfully uninstalled grpcio-1.45.0
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
tensorboard 2.11.0 requires protobuf<4,>=3.9.2, but you have protobuf 4.21.10 which is incompatible.
Successfully installed grpcio-1.51.1 grpcio-tools-1.51.1 protobuf-4.21.10
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

在两个终端窗口中分别运行例子,要先安装git以便下载源码,具体程序就不列出来了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# yum install git
# git clone -b v1.50.0 --depth 1  https://github.com/grpc/grpc

# cd grpc/examples/python/helloworld
# python greeter_server.py
# python greeter_client.py
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

开发自己的例子:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1、建立工作目录文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# mkdir /home/jean/grpcTest
# cd /home/jean/grpcTest

2、定义服务器接口原型文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# vi helloworld.proto

在第一行指定接口原型的语法版本‘syntax = “proto3”;’,参阅资料。这里使用与后面Java版相同的proto文件,否则与Java客户端两边对接会报UNIMPLEMENTED的错误。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
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;
}

3、编译服务器接口原型并产生服务器程序框架文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

一定要用“-I”参数指定接口原型目录,最后的接口原型文件相对于该目录,参阅资料 。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

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

生成的gRPC stub文件:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

helloworld_pb2.pyi包含了request/response对象的接口定义。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

helloworld_pb2.py包含了request/response对象的实现。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

helloworld_pb2_grpc.py包含了client/server对象的定义。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

greeter_server.py实现服务器对象,先直接拷贝使用,我增加了一行输出客户端的名字。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

greeter_client.py实现客户端对象,先直接拷贝使用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

二、gRPC Java 入门文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

安装gradle ,参阅资料文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# mkdir /opt/gradle
# cd /opt/gradle
# wget https://downloads.gradle-dn.com/distributions/gradle-7.6-bin.zip
# unzip -d /opt/gradle gradle-7.6-bin.zip

# vi /etc/profile
export PATH=$PATH:/opt/gradle/gradle-7.6/bin
# gradle -v

下载Java例子。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# git clone -b v1.51.0 --depth 1 https://github.com/grpc/grpc-java

编译例子文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# cd grpc-java/examples
# ./gradlew installDist

在两个终端窗口中分别运行服务器与客户端文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# ./build/install/examples/bin/hello-world-server
# ./build/install/examples/bin/hello-world-client
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

三、运行Python的服务器,Java的客户端,连接O.K.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

四、建立gRPC Maven Jave项目文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

参阅资料文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1、下载Java例子到PC端。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

> git clone -b v1.51.0 --depth 1 https://github.com/grpc/grpc-java

2、建立Maven服务器项目文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Open Eclipse →File →new →other →Maven Project文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java Build Path → Libriaries→ JRE System Library[jdk11]文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1)pom.xml。
<dependencies>一节是最小依赖了,软件都更新到了最新的版本。
<build>一节是要用Maven生成Java gRPC的stub源码。Eclipse Dynamic Web项目里没有Maven build的功能,要先建立Maven Java程序项目生成stub源码,再拷贝过去(说明:Tomcat Web项目也可以由Maven建立与管理,这样可以直接在Tomcat Web项目中build生成stub源码,并编译打包成war文件发布测试,参阅资料,此处是为了说明测试的过程)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jean</groupId>
    <artifactId>grpcserver</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gRPCServer</name>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.21.7</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.51.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.51.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.51.0</version>
        </dependency>
        <!-- Removed from JDK11 and above, need to be added here -->
        <!-- https://blog.csdn.net/ml863606/article/details/109202246?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-109202246-blog-120904477.pc_relevant_3mothn_strategy_recovery&spm=1001.2101.3001.4242.1&utm_relevant_index=3 -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>clean generate-sources compile install</defaultGoal>
        <plugins>
            <!-- compile proto file into java files -->
            <plugin>
                <groupId>com.github.os72</groupId>
                <artifactId>protoc-jar-maven-plugin</artifactId>
                <version>3.11.1</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <includeMavenTypes>direct</includeMavenTypes>
                            <inputDirectories>
                                <include>src/main/resources</include>
                            </inputDirectories>
                            <outputTargets>
                                <outputTarget>
                                    <type>java</type>
                                    <outputDirectory>src/main/java</outputDirectory>
                                </outputTarget>
                                <outputTarget>
                                    <type>grpc-java</type>
                                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.51.0</pluginArtifact>
                                    <outputDirectory>src/main/java</outputDirectory>
                                </outputTarget>
                            </outputTargets>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
Java-gRPC-Python开发环境安装配置
Java Maven服务器项目

项目根目录->右键->Maven->Update Project,下载依赖包。只下载一次,可能需要点时间。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

2)helloworld.proto与Python版接口保持一样,增加了一些Java上的选项设置。Python版的proto文件要更新为与Java版的一致,并重新编译生成接口stub文件。
这个文件放在上面src/main/resources 定义的目录下。
pom.xml->右键-> Run As Maven Build,生成 gRPC的stub类源码,输出在上面定义的目录src/main/java下。因为pom.xml中定义了,直接按“Run”按钮执行即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置

3)拷贝HellowWorldServer.java到项目源码中。
HellowWorldServer.java->右键-> Run As Java Applicaion文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
运行Java gRPC服务器项目

4)用BloomRPC客户端测试gRPC服务器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

主页下载Windows版。点击运行BloomRPC-Setup-1.5.3.exe。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

在Proto中按“+”打开上面的Java版helloworld.proto接口定义文件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

在右面“Env”中输入RPC服务器的网址与端口localhost:50051文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

点击左面的方法,在右面Editor页json格式参数中输入参数的值,按运行按钮调用,Response页会看到返回结果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
用BloomRPC客户端测试gRPC服务器

3、建立Maven gRPC客户端项目文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Open Eclipse →File →new →other →Maven Project文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java Build Path → Libriaries→ JRE System Library[jdk11]文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1)、pom.xml,拷贝服务器项目pom.xml中的与的内容。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

项目根目录->右键->Maven->Update Project文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

2)、拷贝服务器项目生成的stub Java源码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

package io.grpc.examples.helloworld:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

GreeterGrpc.java, HelloReply.java, HelloReplyOrBuilder.java, HelloRequest.java, HelloRequestOrBuilder.java, HelloWorldProto.java文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

3)、拷贝HellowWorldClient.java到项目中。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

HellowWorldClient.java->右键-> Run As Java Applicaion文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
运行Java gRPC客户端项目

4)、测试Java gRPC Client到服务器 Python gRPC Server的调用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

A、服务器防火墙上打开TCP端口50051。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

B、用BloomRPC客户端测试gRPC服务器。更改 Env的服务器地址为 jeanye.cn:50051,测试SayHello()调用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

C、更改HellowWorldClient.java的连接地址:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

    String target = "124.223.110.20:50051";

HellowWorldClient.java->右键-> Run As Java Applicaion文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

五、建立gRPC Tomcat Web项目文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1、建立项目文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Open Eclipse →File →new →Dynamic Web Project文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

在项目properties中设置为Tomcat 7.0 & JDK11文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

2、添加依赖包文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

打开前面建立gRPC客户端项目的pom.xml文件,点击Dependency Hierarchy页,把Resolved Dependencies页中显示的包,从本地Maven Repository中逐个import到项目的/WebContent/WEB-INF/lib目录下,编译和运行gRPC Java客户端程序都需要它们。Maven Repository目录一般是 C:\Users\username\.m2\repository。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

animal-sniffer-annotations-1.21.jar
annotations-4.1.1.4.jar
checker-qual-3.12.0.jar
error_prone_annotations-2.14.0.jar
failureaccess-1.0.1.jar
grpc-api-1.51.0.jar
grpc-auth-1.51.0.jar
grpc-context-1.51.0.jar
grpc-core-1.51.0.jar
grpc-netty-shaded-1.51.0.jar
grpc-protobuf-1.51.0.jar
grpc-protobuf-lite-1.15.1.jar
grpc-stub-1.51.0.jar
gson-2.9.0.jar
guava-20.0.jar
guava-31.1-android.jar
j2objc-annotations-1.3.jar
javax.annotation-api-1.3.2.jar
jsr305-3.0.2.jar
listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
opencensus-api-0.12.3.jar
opencensus-contrib-grpc-metrics-0.12.3.jar
perfmark-api-0.25.0.jar
proto-google-common-protos-2.9.0.jar
protobuf-java-3.21.7.jar
Java-gRPC-Python开发环境安装配置
grpcWeb项目的Java Build Path

3、拷贝gRPC客户端项目的gRPC stub源码文件到Web项目源码中,也可以直接在gRPC客户端项目生成,这样就不用建立gRPC服务器项目,本文建立gRPC服务器项目是为了说明测试的过程。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

GreeterGrpc.java             HelloReply.java
HelloReplyOrBuilder.java     HelloRequest.java            HelloRequestOrBuilder.java   
HelloWorldProto.java
Java-gRPC-Python开发环境安装配置
grpcWeb的项目结构

4、编写在JSP页面中调用gRPC的帮助类HelloWorldClient.java文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

package io.grpc.examples.helloworld;

import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A simple client that requests a greeting from the {@link HelloWorldServer}.
 */
public class HelloWorldClient {
  private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

  private final GreeterGrpc.GreeterBlockingStub blockingStub;

  private ManagedChannel channel=null;
 
  /** Construct client for accessing HelloWorld server by creating a channel. */
  public HelloWorldClient(String server, Integer port) {
 
    channel = ManagedChannelBuilder.forTarget(server+":"+port)
                // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
                // needing certificates.
                .usePlaintext()
                .build();
 
    // 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to
    // shut it down.
    // Passing Channels to code makes code easier to test and makes it easier to reuse Channels.
    blockingStub = GreeterGrpc.newBlockingStub(channel);
  }

  /** Say hello to server. */
  public String 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 (e.toString());
    }
    logger.info("Greeting: " + response.getMessage());
    return (response.getMessage());
  }

  /** Close the channel. */
  public void close() throws Exception {
    try {
      // ManagedChannels use resources like threads and TCP connections. To prevent leaking these
      // resources the channel should be shut down when it will no longer be used. If it may be used
      // again leave it running.
      channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
    }catch(Exception e) {
        e.printStackTrace();
    }
  }
}

5、编写界面页面 index.jsp输入用户名,提交给callgrpc.jsp,该页面通过上面的工具类HelloWorldClient.java调用gRPC服务器端的Python程序。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1)index.jsp:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

<%@ page language="java" contentType="text/html; charset=GBK"
    pageEncoding="GBK"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>Java EE gRPC调用测试</title>
</head>
<body>
<center>
 <form action="callgrpc.jsp" method="post">
 跟用户打招呼 <br/>
  用户名:<input type="text" name="name" value="Jean"><br/>
 <input type="submit" value="打招呼">
 </form>
 </center>
</body>
</html>

2)callgrpc.jsp:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

<%@ page language="java" contentType="text/html; charset=GBK"
    pageEncoding="GBK"%>
<%@ page import="io.grpc.examples.helloworld.*"%>    
 
<%

String resp =null;
try{
 
    String user = new String(request.getParameter("name").getBytes("ISO-8859-1"),"GBK");
    if (user==null) user = "Jean";
    //String server = "localhost";
    String server = "jeanye.cn";
    Integer port = 50051;
 
    // Plain text without SSL & password.
    HelloWorldClient client = new HelloWorldClient(server, port);

    resp = client.greet(user);
    client.close();
 
}catch(Exception e){
    resp =e.toString();
}

%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="GBK">
<title>Java EE 调用gRPC 测试</title>
</head>
<body>
Java gRPC调用服务器端Python程序结果:
<br>
<%= resp %>
<br>
<a href="index.jsp">返回</a>
</body>
</html>

6、在PC端的Tomcat 7服务器上运行页面测试。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

7、部署到虚拟主机的Tomcat 7服务器上运行测试。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

体验网址文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

六、为gRPC添加SSL与口令保护文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

gRPC的用户验证机制默认是没有用户名口令方式的,参阅资料,需要通过扩充gRPC来完成,参阅资料。该资料给出了Python上SSL+Password的实现,通过在gRPC调用的元数据中增加一些定制的header,检查客户端提交的header,来检查客户端是否授权用户,此处是检查’rpc-auth-header’的值是否相符,它的作用相当于检查口令。再增加一个’username’的header,然后Python服务器端检查时提取这两个header,再连接到用户数据库中验证就可以实现用户名口令验证了。用户数据库可以是LDAP/RDBS/Linux系统用户等,按需要实现。此处检查口令已经足够。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1、Python服务端,greeter_server_SSL_Auth.py。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

服务器端具体是通过一个实现intercept_service()接口的AuthInterceptor类实现的,它扩展了grpc.ServerInterceptor类,其中定义了header的名字’rpc-auth-header’与检查的逻辑,就是是否与保存的口存令相符,这个口令是初始化AuthInterceptor类实例时赋予的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Python服务器端的SSL支持通过调用grpc.ssl_server_credentials()来实现,需要生成服务器的证书链,自签证书要把自签根证书带上,最后的False参数表示单向SSL,不需要客户端提供数字证书,此处将使用口令验证。因为要访问服务器证书的私钥,我用root用户来运行它。参阅资料1参阅资料2文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这是增加的部分,实现服务器端调用功能的Greeter类没有变化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# Copyright 2015 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC helloworld.Greeter server."""

from concurrent import futures
import logging

import grpc
import helloworld_pb2
import helloworld_pb2_grpc

# The Class to serve the client with a Function defined by tha proto file.
class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        print(request.name)
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

# For authenticating with a password.
class AuthInterceptor(grpc.ServerInterceptor):
    def __init__(self, key):
        # 'rpc-auth-header' is the header data containing the password.
        self._valid_metadata = ('rpc-auth-header', key)

        def deny(_, context):
            context.abort(grpc.StatusCode.UNAUTHENTICATED, 'Invalid key')

        self._deny = grpc.unary_unary_rpc_method_handler(deny)

    def intercept_service(self, continuation, handler_call_details):
        meta = handler_call_details.invocation_metadata
        print(meta)
        # https://grpc.io/docs/guides/auth/#extending-grpc-to-support-other-authentication-mechanisms
        # There's a bug in the document, meta[0] should be meta[1] for newer versions.
        if meta and meta[1] == self._valid_metadata:
            return continuation(handler_call_details)
        else:
            return self._deny


# The server procedure.
def serve():
    port = '50051'
    # Now requires authentication with a password by attaching an interceptor.
    # 'access_key' is the password.
    server = grpc.server(
                futures.ThreadPoolExecutor(max_workers=10),
                interceptors=(AuthInterceptor('access_key'),)
             )
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    # Load the server's certificate chain & private key.
    # So the server should be run as root.
    with open('/root/cert/server.key', 'rb') as f:
        private_key = f.read()
    with open('/root/cert/server.crt', 'rb') as f:
        certificate_chain = f.read()
    with open('/root/cert/ca.crt', 'rb') as f:
        root_certificates = f.read()
    # False means client cert is not required.
    server_credentials = grpc.ssl_server_credentials(((private_key, certificate_chain),), root_certificates,False)
    # '[::]:' means may be accessed from any IP.
    server.add_secure_port('[::]:' + port, server_credentials)    
    server.start()
    print("Server started, listening on " + port)
    # May be interrupted by Ctrl+C.
    server.wait_for_termination()


if __name__ == '__main__':
    logging.basicConfig()
    serve()

2、Python客户端,greeter_client_SSL_Auth.py。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Python客户端通过一个扩展grpc.AuthMetadataPlugin类的GrpcAuth类来实现口令验证,它需要在建立连接时把口令’access_key’用GrpcAuth()传进去,header的名字就是服务器要求的’rpc-auth-header’。需要注意的是客户端建立SSL连接时,需要带上服务器自签证书的根证书,即CA证书,否则不能验证服务器的证书。我把根证书ca.crt拷贝到程序目录下,这样就可以用普通用户的身份运行Python客户端,因为ca.crt正是要发布给所有客户端的。GrpcAuth()与CA的证书组合成一个grpc.composite_channel_credentials(),然后可以通过grpc.secure_channel()建立SSL+Password的连接通道。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这是增加的部分。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

# Copyright 2015 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC helloworld.Greeter client."""

from __future__ import print_function

import logging

import grpc
import helloworld_pb2
import helloworld_pb2_grpc

# Class to add authentication header to the meta data of every gRPC call.
class GrpcAuth(grpc.AuthMetadataPlugin):
    def __init__(self, key):
        self._key = key

    # 'rpc-auth-header' is the authentication header defined by the server.
    def __call__(self, context, callback):
        callback((('rpc-auth-header', self._key),), None)


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.
    print("Will try to greet world ...")
    # Will fail for enpty credentials.
    # creds = grpc.ssl_channel_credentials()
    # Need to include root certificate in credentials.
    # https://stackoverflow.com/questions/72230151/how-to-open-a-secure-channel-in-python-grpc-client-without-a-client-ssl-certific
    # Copy the selfsigned CA's root cert to the client directory, so that the client doesn't need to be run as root.
    with open('ca.crt', 'rb') as f:
        creds = grpc.ssl_channel_credentials(f.read())
 
    # A composite channel credentials with SSL and password.
    # Host name need to be the same as the server certificate. 
    channel = grpc.secure_channel(
        'jeanye.cn:50051',
        grpc.composite_channel_credentials(
            creds,
            grpc.metadata_call_credentials(
                GrpcAuth('access_key')
                # A worng key will fail then.
                # GrpcAuth('wrong_access_key')
            )
        )
    )
 
    stub = helloworld_pb2_grpc.GreeterStub(channel)
    response = stub.SayHello(helloworld_pb2.HelloRequest(name='Jean'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置

3、Java客户端。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

gRPC的文档里没有直接讲Java客户端通过header验证用户的例子,甚至在grpc-java例子中也没有直接列出来,但确实有实现的例子,只是位于源码树的深处,没有明确标示出来。如下面的代码所示,它是通过在SSL ManagedChannel之外,用ClientInterceptor类再包装为一个Channel来实现的,header就在实现ClientInterceptor接口的HeaderClientInterceptor类中添加。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

1)、HelloWorldClientSSLAuth.java文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

SSL通道的建立用NettyChannelBuilder,与上文明文通道的ManagedChannelBuilder不同,与文档中的说明也稍有不同,因为要通过trustManager()传入自签的CA根证书,参阅资料。因为这是一个Tomcat Web APP,ca.crt打包在其中,后面在JSP页面中通过application.getRealPath(“/WEB-INF/ca.crt”)得到它在操作系统中具体的文件路径,再作为参数传给trustManager(),参阅资料。 然后是把口令”access_key”通过HeaderClientInterceptor()构造函数传入,最后关闭通道时引用未经ClientInterceptor封装的原通道。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这个结构是很简洁的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

package io.grpc.examples.helloworld;

import io.grpc.Channel;
import io.grpc.ClientInterceptor;
import io.grpc.ClientInterceptors;
import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;

import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A simple client that requests a greeting from the {@link HelloWorldServer}.
 */
public class HelloWorldClientSSLAuth {
    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

    private final GreeterGrpc.GreeterBlockingStub blockingStub;

    private ManagedChannel originChannel = null;
 
    private Channel channel = null;

    /** Construct client for accessing HelloWorld server by creating a channel. */
    public HelloWorldClientSSLAuth(String caFile, String server, Integer port) throws Exception {

        /* 
         * Use NettyChannelBuilder to build SSL connection to server.
         * https://stackoverflow.com/questions/42700411/ssl-connection-for-grpc-java-client
         * Need to include selfsigned CA certificate to build a sslContext to verify the server.
         * And the server name must be same as the name in the server certificate.
         */
        originChannel = NettyChannelBuilder.forAddress(server, port)
                .sslContext(GrpcSslContexts.forClient().trustManager(new File(caFile)).build())
                .build();  
        // "access_key" is the password to call gRPC.
        ClientInterceptor interceptor = new HeaderClientInterceptor("access_key");
        // Channel with an interceptor to add an auth header for each gRPC call.
        channel = ClientInterceptors.intercept(originChannel, interceptor);
 
        // 'channel' here is a Channel, not a ManagedChannel, so it is not this code's
        // responsibility to shut it down.
        // Passing Channels to code makes code easier to test and makes it easier to reuse Channels.
        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }

    /** Say hello to server. */
    public String 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 (e.toString());
        }
        logger.info("Greeting: " + response.getMessage());
        return (response.getMessage());
    }

    /** Close the channel. */
    public void close() throws Exception {
        try {
            // ManagedChannels use resources like threads and TCP connections. To prevent leaking these
            // resources the channel should be shut down when it will no longer be used. If
            // it may be used again leave it running.
            originChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、HeaderClientInterceptor.java文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这个类定义了header的名字为Python服务器要求的”rpc-auth-header”,然后在interceptCall()函数中通过headers.put()函数把header的名/值对插入到gRPC调用的meta数据中,如果要增加一个’username’ header,增加一条headers.put()语句就是了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

/*
 * Copyright 2015 The gRPC Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.grpc.examples.helloworld;

import com.google.common.annotations.VisibleForTesting;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import java.util.logging.Logger;

/**
 * A interceptor to handle client header.
 */
public class HeaderClientInterceptor implements ClientInterceptor {

  private static final Logger logger = Logger.getLogger(HeaderClientInterceptor.class.getName());
  private String password = null;

  // This header is defined by the Python side to verify the client with a password.
  @VisibleForTesting
  static final Metadata.Key<String> CUSTOM_HEADER_KEY =
      Metadata.Key.of("rpc-auth-header", Metadata.ASCII_STRING_MARSHALLER);

  public HeaderClientInterceptor(String password) {
      this.password = password;
  }
 
  @Override
  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
      CallOptions callOptions, Channel next) {
    return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {

      @Override
      public void start(Listener<RespT> responseListener, Metadata headers) {
        /* put custom header */
        headers.put(CUSTOM_HEADER_KEY, password);        
        super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
          @Override
          public void onHeaders(Metadata headers) {
            /**
             * if you don't need receive header from server,
             * you can use {@link io.grpc.stub.MetadataUtils#attachHeaders}
             * directly to send header
             */
            logger.info("header received from server:" + headers);
            super.onHeaders(headers);
          }
        }, headers);
      }
    };
  }
}

3、index.jsp。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这个页面没有变化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

<%@ page language="java" contentType="text/html; charset=GBK"
    pageEncoding="GBK"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>Java EE gRPC调用测试</title>
</head>
<body>
<center>
 <form action="callgrpc.jsp" method="post">
 跟用户打招呼 <br/>
  用户名:<input type="text" name="name" value="Jean"><br/>
 <input type="submit" value="打招呼">
 </form>
 </center>
</body>
</html>

4、callgrpc.jsp。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

这个页面也很直观,不用解释了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

<%@ page language="java" contentType="text/html; charset=GBK"
    pageEncoding="GBK"%>
<%@ page import="io.grpc.examples.helloworld.*"%>    
 
<%

String resp =null;
try{
 
    String user = new String(request.getParameter("name").getBytes("ISO-8859-1"),"GBK");
    if (user==null) user = "Jean";
    //String server = "localhost";
    String server = "jeanye.cn";
    Integer port = 50051;
 
    // Plain text without SSL & password.
    // HelloWorldClient client = new HelloWorldClient(server, port);
    String caFile =  application.getRealPath("/WEB-INF/ca.crt");
    // SSL without password.
    // HelloWorldClientSSL client = new HelloWorldClientSSL(caFile,server,port); 
    // SSL with password.
    HelloWorldClientSSLAuth client = new HelloWorldClientSSLAuth(caFile,server,port);    

    resp = client.greet(user);
    client.close();
 
}catch(Exception e){
    resp =e.toString();
}

5、运行效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html

Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置
Java-gRPC-Python开发环境安装配置
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/48513.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/48513.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定