gRPC与区块链

Posted 美的金融科技

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gRPC与区块链相关的知识,希望对你有一定的参考价值。

说到RPC框架大家可能会想到一堆相关通信技术,SOAP WebService、RMI、Hessian、HTTP Rest、Thrift、ZeroICE... ,因为在分布式系统开发中经常与其打交道,它解决了不同系统之间远程通信和相互调用的问题。

gRPC是google新推出来的开源RPC框架,2016年8月发布了1.0正式版本。它的技术栈非常新,基于HTTP/2、netty4.1、proto3实现的一个高性能、跨语言的RPC框架。同时带来很多功能特性:

1) 高效的编解码机制

基于Proto3编解码速度和效率非常接近thrift,远超JSON;对于App端更小的CPU使用,节能省电,网络带宽消耗少,省流量。

2) 有效的网络利用

单连接多路复用,无需多条链接或者多次请求排队;HTTP/2无需像HTTP/1需要多次TCP握手和销毁。

3) 灵活的编程API

服务器端和客户端支持异步编程模型,从.proto契约文件自动生成的代码,已经同时包含同步和异步两种API。

4) 支持Stream

支持服务器端stream、客户端stream和双向stream,特别适合用来做服务器端主动消息推送,如变更通知等。

目前Google已在多个云产品和面向外部的Google API中使用gRPC,它同时也被Square, Netflix、CoreOS、Docker、Kubernetes、Cockroachdb、Cisco、Juniper Networks、猎豹移动七牛云阿里云微服务和许多其他组织及新平台,微服务构架,Hyperledger fabric(超级账本)著名的开源区块链项目 点对点通信使用。

与许多RPC框架类似,gRPC客户端应用可以像本地对象一样直接调用另一台不同的机器上服务端应用的方法(不同进程及内存空间)。 而调用者不用了解各种底层网络协议,不用去拼REST风格的动态URL,也不用管各种的HTTP状态码,所有需要服务端处理的网络请求跟调用本地方法一样简单。

 

 

其基本原理是定义一个语言中立的服务描述,指定其能够被远程调用的方法、参数和返回类型。在服务端实现这个接口,并运行一个 GRPC 服务器来处理客户端调用,在客户端就能拥有一个像服务端一样的方法存根。

gRPC服务描述是基于Protocol Buffers来定义的,它也是google公司开发的一种数据描述语言,可用于序列化结构化数据、数据存储和通信协议等,它不依赖于语言和平台且可扩展性极强。支持自定义数据结构,使用相应语言平台代码生成器可生成代码来读写这些数据结构,而系统运行时,甚至可以在无需重新部署程序的情况下更新数据结构。

接下来通过构建一个简单的RPC服务来体检一下gRPC的使用方法,使用Go语言实现服务端与客户端,同时使用Java语言实现客户端以验证gRPC跨语言特性。

1) 服务接口定义

新建命名为helloworld.proto文件

// 指定使用proto3语法

syntax = "proto3";

// 设置java包及类等信息
option java_multiple_files = true;
option java_package = "com.midea.jr.test.grpc";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// 定义服务接口
service Greeter {
    // 定义方法,参数及返回类型
    rpc SayHello (HelloRequest) returns (HelloReply) {
    }
}

// 定义请求消息类型
message HelloRequest {
    string name = 1;
}

// 定义返回消息类型
message HelloReply {
    string message = 1;
}

2) 本地代码生成

protocol-buffers官网下载安装GO语言对应代码生成工具:

go get -u github.com/golang/protobuf/protoc-gen-go

// 执行命令生成代码

protoc -I pb/ pb/*.pb --go_out=plugins=grpc:grpc

pb目录将生成helloworld.pb.go文件

 

此文件已封装好proto定义数据结构的API,接下来只需专注服务接口业务实现即可。

3) GO语言服务端实现

新建helloworldserver.go文件

package main

import (
   "log"
   "net"
   "golang.org/x/net/context"
   "google.golang.org/grpc"
   "google.golang.org/grpc/reflection"
   "com.midea/jr/grpclb/example/pb"
)
//定义端口常量
const (
   port = ":8080"
)

// 定义一个结构体实现 helloworld.GreeterServer.
type server struct{}

// 实现 helloworld.GreeterServer 方法SayHello
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
   return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
   lis, err := net.Listen("tcp", port)
   if err != nil {
      log.Fatalf("failed to listen: %v", err)
   }
   s := grpc.NewServer()
   pb.RegisterGreeterServer(s, &server{})
   // 通过反射将服务注册gRPC服务
   reflection.Register(s)
   if err := s.Serve(lis); err != nil {
      log.Fatalf("failed to serve: %v", err)
   }
}

执行build编译及运行rungo命令,程序将启用一个监听8080端口的服务

4) GO语言客户端实现

新建helloworldclient.go文件

package main
import (
   "log"
   "os"
   "golang.org/x/net/context"
   "google.golang.org/grpc"
   "com.midea/jr/grpclb/example/pb"
)
//定义常量
const (
   address     = "localhost:8080"
   defaultName = "world"
)

func main() {
   // 建立一个到服务器的连接
   conn, err := grpc.Dial(address, grpc.WithInsecure())
   if err != nil {
      log.Fatalf("did not connect: %v", err)
   }
   defer conn.Close()
   c := pb.NewGreeterClient(conn)

   // 连接服务器和打印响应信息
   name := defaultName
   if len(os.Args) > 1 {
      name = os.Args[1]
   }
   r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
   if err != nil {
      log.Fatalf("could not greet: %v", err)
   }
   log.Printf("Greeting: %s", r.Message)
}

执行build编译及运行rungo命令,正常情况下控制台将输出:

2017/03/06 18:50:15 Greeting: Hello world

5) Java语言客户端实现

a) Protobuf已为Java语言版本提供了maven插件,因此在pom配置相应插件即可,如下:

<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.5.0</version>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact>
        <pluginId>grpc-java</pluginId>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.3:exe:${os.detected.classifier}</pluginArtifact>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
            </goals>
        </execution>
    </executions>
</plugin>

b) 1)中定义好的proto文件放入工程proto目录中,结构如下:

 

c) 执行maven的compile命令,相应gRPC-Java代码API将自动生成,目录文件如下:

 

d) 编写客户端代码,创建类HelloWorldClient

package com.midea.jr.test.grpc;

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;

/**
 * Java客户端实现(demo)
 */
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) {
        channel = ManagedChannelBuilder
                .forAddress(host, port)
                .usePlaintext(true)
                .build();
        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }

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

    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;
        }
        logger.info("Greeting: " + response.getMessage());
    }

    public static void main(String[] args) throws Exception {
        String address = "localhost";
        int port = 8080;
        String msg = "world";

        HelloWorldClient client = new HelloWorldClient(address, port);
        try {
            client.greet(msg);
        } finally {
            client.shutdown();
        }
    }
}

6) 跨语言调用验证

启动Go语言实现的服务端3),启动Java语言客户端5),正常情况下控制台将输出:

 

至处已完成Java版的gRPC客户端成功调用Go语言实现的gRPC服务端。


 

参考:

GRPC官网文档 http://www.grpc.io/docs/

GRPC的产生动机和设计原则(中文)http://www.jianshu.com/p/8cc077f6dbb9

Protocol Buffers  https://developers.google.com/protocol-buffers/docs/proto3

Golang  http://golang.com/



以上是关于gRPC与区块链的主要内容,如果未能解决你的问题,请参考以下文章

区块链:Hyperledger Fabric智能合约(链码)

区块链:Hyperledger Fabric智能合约(链码)

区块链技术与应用

[技术发展-5]:《区块链与加快推动区块链技术和产业创新发展》

《区块链应用指南方法与实践》读书笔记

《区块链应用指南方法与实践》读书笔记