为何RPC

在微服务这个时代,不论是传输还是内网调用,以及跨语言的传输,RPC都是不二的选择。GRPC是Google基于protocol buffer传输协议开发的一个RPC框架,支持多语言之间的通信,下面,我会基于Java语言和golang语言做一个跨语言调用例子,Java做client端,golang做服务端

计算器proto接口定义:

syntax = "proto3";
option go_package = "client";
//请求
message Request {
    double num1 = 1;
    double num2 = 2;
    OperateType opType = 3;
}

//操作类型
enum OperateType {
    Addition = 0;
    Division = 1;
    Multiplication = 2;
    Subtraction = 3;
}
message Response {
    double result = 1;
}

//定义服务
service Operate {
    rpc Calculate (Request) returns (Response);
}

生成go源码:

官方给go语言的生成提供了插件,用这个命令就可以快速生成go端的相关代码,为了简便期间,我写成了一个简单的脚本:

#!/usr/bin/env bash
find ./ -name '*.proto'|xargs protoc --go_out=plugins=grpc:.

这个脚本的作用是寻找当前文件下的所有proto文件,并在其所在文件夹下生成源码
gRPC实现跨语言(golang&&java)服务调用

编写server端:

接口:

package main

import (
	"context"
	"errors"
	"google.golang.org/grpc"
	"log"
	"net"
	"testgo/proto"
	"testgo/service"
)

const (
	port = ":9543"
)

type server struct {
}

func (server) Calculate(ctx context.Context, in *client.Request) (resp *client.Response, e error) {
	log.Printf("Received request")
	defer func() { // 必须要先声明defer,否则不能捕获到panic异常
		if err := recover(); err != nil {
			e = errors.New(err.(string)) // 这里的err其实就是panic传入的内容
		}
	}()
	c := service.Calculate{Num1: in.Num1, Num2: in.Num2,}
	var result float64
	switch in.OpType {
	case client.OperateType_Addition:
		result = c.Operate(service.Addition)
	case client.OperateType_Division:
		result = c.Operate(service.Division)
	case client.OperateType_Multiplication:
		result = c.Operate(service.Multiplication)
	case client.OperateType_Subtraction:
		result = c.Operate(service.Subtraction)
	}

	return &client.Response{Result: result,}, nil
}
func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	client.RegisterOperateServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

业务代码:

package service

type Calculate struct {
	Num1 float64
	Num2 float64
}

type Operate func(num1, num2 float64) (result float64)

//操作接口
func (c Calculate) Operate(op Operate) (result float64) {
	return op(c.Num1, c.Num2)
}
package service

//定义操作类型
func Addition(num1, num2 float64) (result float64) {
	return num1 + num2
}
func Division(num1, num2 float64) (result float64) {
	if num2 == 0 {
		panic("division by zero")
	}
	return num1 / num2
}
func Multiplication(num1, num2 float64) (result float64) {
	return num1 * num2
}

func Subtraction(num1, num2 float64) (result float64) {
	return num1 - num2
}

如果server端代码有问题的童鞋,可以参考go官方的rpc调用example

编写go client端

package main

import (
	"context"
	"google.golang.org/grpc"
	"log"
	"testgo/proto"
	"time"
)

const (
	address = "localhost:9543"
)

func main() {
	// Set up a connection to the server.
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := client.NewOperateClient(conn)

	// Contact the server and print out its response.
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.Calculate(ctx, &client.Request{Num1: 1, Num2: 0, OpType: client.OperateType_Division})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("result: %f", r.Result)
}

实现java client端:

引入gprc的maven依赖:

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

引入编译proto文件的maven插件:

<extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
</extensions>
 <plugin>
       <groupId>org.xolstice.maven.plugins</groupId>
         <artifactId>protobuf-maven-plugin</artifactId>
         <version>0.5.1</version>
         <configuration>
             <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}</protocArtifact>
             <pluginId>grpc-java</pluginId>
             <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier}</pluginArtifact>
         </configuration>
         <executions>
             <execution>
                 <goals>
                     <goal>compile</goal>
                     <goal>compile-custom</goal>
                 </goals>
             </execution>
         </executions>
</plugin>
mvn clean install

编写java Client端:


package com.tangbaobao.rpc.service;

import com.tangbaobao.client.Calcualte;
import com.tangbaobao.client.OperateGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.concurrent.TimeUnit;

/**
 * @author tangxuejun
 * @version 2019-04-16 11:25
 */
public class CalculateService {
    private final ManagedChannel channel;
    private final OperateGrpc.OperateBlockingStub blockingStub;

    private CalculateService(ManagedChannel channel) {
        this.channel = channel;
        blockingStub = OperateGrpc.newBlockingStub(channel);
    }

    public CalculateService(String host, int port) {
        this(ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build());
    }

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

    public float operate(float num1, float num2, Calcualte.OperateType operateType) {
        Calcualte.Request request = Calcualte.Request.newBuilder().setNum1(num1).setNum2(num2).setOpType(operateType).build();
        Calcualte.Response response = blockingStub.calculate(request);
        return (float) response.getResult();
    }

    public static void main(String[] args) {
        try {
            CalculateService service = new CalculateService("localhost", 9543);
            System.out.println(service.operate(100, 0, Calcualte.OperateType.Division));
            service.shutdown();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

运行结果

先启动server端:
然后启动client端

gRPC实现跨语言(golang&&java)服务调用

gRPC实现跨语言(golang&&java)服务调用