为何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文件,并在其所在文件夹下生成源码
编写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端