初探grpc-web(服务端go)

从 https://github.com/improbable-eng/grpc-web/releases/tag/v0.13.0 按操作系统选择下载,如:grpcwebproxy-v0.13.0-win64.exe.zip 。

下载完成后把 grpcwebproxy.exe 放到自建的文件夹,并添加到环境变量的path中。

使用grpcwebproxy代理服务,命令为:
示例:grpcwebproxy --allow_all_origins --backend_addr=localhost:50051 --run_tls_server=false --server_http_debug_port=5005
本地实际为:
grpcwebproxy --allow_all_origins --backend_addr=192.168.1.103:8090 --run_tls_server=false --server_http_debug_port=5005

其中用到的参数说明:
在这里插入图片描述
使用命令把当前文件夹下面所有的.proto文件转成XXX_pb.js和XXX_grpc_web_pb.js,命令为:
protoc --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. ./*.proto

hello.proto 文件内容为:

syntax = "proto3";

package hello;

option go_package=".;hello";

// 请求参数
message HelloRequest {
    string name = 1;
}

// 响应消息
message HelloInfo {
    string hello_data = 1;
}

//服务定义
service HelloService {
    rpc Hello (HelloRequest) returns (HelloInfo);
}

转成 XXX_pb.js和XXX_grpc_web_pb.js:

  1. hello_grpc_web_pb.js:
/**
 * @fileoverview gRPC-Web generated client stub for hello
 * @enhanceable
 * @public
 */

// GENERATED CODE -- DO NOT EDIT!


/* eslint-disable */
// @ts-nocheck



const grpc = {};
grpc.web = require('grpc-web');

const proto = {};
proto.hello = require('./hello_pb.js');

/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?Object} options
 * @constructor
 * @struct
 * @final
 */
proto.hello.HelloServiceClient =
    function(hostname, credentials, options) {
  if (!options) options = {};
  options['format'] = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};


/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?Object} options
 * @constructor
 * @struct
 * @final
 */
proto.hello.HelloServicePromiseClient =
    function(hostname, credentials, options) {
  if (!options) options = {};
  options['format'] = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};


/**
 * @const
 * @type {!grpc.web.MethodDescriptor<
 *   !proto.hello.HelloRequest,
 *   !proto.hello.HelloInfo>}
 */
const methodDescriptor_HelloService_Hello = new grpc.web.MethodDescriptor(
  '/hello.HelloService/Hello',
  grpc.web.MethodType.UNARY,
  proto.hello.HelloRequest,
  proto.hello.HelloInfo,
  /**
   * @param {!proto.hello.HelloRequest} request
   * @return {!Uint8Array}
   */
  function(request) {
    return request.serializeBinary();
  },
  proto.hello.HelloInfo.deserializeBinary
);


/**
 * @const
 * @type {!grpc.web.AbstractClientBase.MethodInfo<
 *   !proto.hello.HelloRequest,
 *   !proto.hello.HelloInfo>}
 */
const methodInfo_HelloService_Hello = new grpc.web.AbstractClientBase.MethodInfo(
  proto.hello.HelloInfo,
  /**
   * @param {!proto.hello.HelloRequest} request
   * @return {!Uint8Array}
   */
  function(request) {
    return request.serializeBinary();
  },
  proto.hello.HelloInfo.deserializeBinary
);


/**
 * @param {!proto.hello.HelloRequest} request The
 *     request proto
 * @param {?Object<string, string>} metadata User defined
 *     call metadata
 * @param {function(?grpc.web.Error, ?proto.hello.HelloInfo)}
 *     callback The callback function(error, response)
 * @return {!grpc.web.ClientReadableStream<!proto.hello.HelloInfo>|undefined}
 *     The XHR Node Readable Stream
 */
proto.hello.HelloServiceClient.prototype.hello =
    function(request, metadata, callback) {
  return this.client_.rpcCall(this.hostname_ +
      '/hello.HelloService/Hello',
      request,
      metadata || {},
      methodDescriptor_HelloService_Hello,
      callback);
};


/**
 * @param {!proto.hello.HelloRequest} request The
 *     request proto
 * @param {?Object<string, string>} metadata User defined
 *     call metadata
 * @return {!Promise<!proto.hello.HelloInfo>}
 *     Promise that resolves to the response
 */
proto.hello.HelloServicePromiseClient.prototype.hello =
    function(request, metadata) {
  return this.client_.unaryCall(this.hostname_ +
      '/hello.HelloService/Hello',
      request,
      metadata || {},
      methodDescriptor_HelloService_Hello);
};


module.exports = proto.hello;


  1. hello_pb.js:
// source: hello.proto
/**
 * @fileoverview
 * @enhanceable
 * @suppress {missingRequire} reports error on implicit type usages.
 * @suppress {messageConventions} JS Compiler reports an error if a variable or
 *     field starts with 'MSG_' and isn't a translatable message.
 * @public
 */
// GENERATED CODE -- DO NOT EDIT!
/* eslint-disable */
// @ts-nocheck

var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();

goog.exportSymbol('proto.hello.HelloInfo', null, global);
goog.exportSymbol('proto.hello.HelloRequest', null, global);
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.hello.HelloRequest = function(opt_data) {
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.hello.HelloRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
  /**
   * @public
   * @override
   */
  proto.hello.HelloRequest.displayName = 'proto.hello.HelloRequest';
}
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.hello.HelloInfo = function(opt_data) {
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.hello.HelloInfo, jspb.Message);
if (goog.DEBUG && !COMPILED) {
  /**
   * @public
   * @override
   */
  proto.hello.HelloInfo.displayName = 'proto.hello.HelloInfo';
}



if (jspb.Message.GENERATE_TO_OBJECT) {
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.hello.HelloRequest.prototype.toObject = function(opt_includeInstance) {
  return proto.hello.HelloRequest.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.hello.HelloRequest} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloRequest.toObject = function(includeInstance, msg) {
  var f, obj = {
    name: jspb.Message.getFieldWithDefault(msg, 1, "")
  };

  if (includeInstance) {
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.hello.HelloRequest}
 */
proto.hello.HelloRequest.deserializeBinary = function(bytes) {
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.hello.HelloRequest;
  return proto.hello.HelloRequest.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.hello.HelloRequest} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.hello.HelloRequest}
 */
proto.hello.HelloRequest.deserializeBinaryFromReader = function(msg, reader) {
  while (reader.nextField()) {
    if (reader.isEndGroup()) {
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    case 1:
      var value = /** @type {string} */ (reader.readString());
      msg.setName(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.hello.HelloRequest.prototype.serializeBinary = function() {
  var writer = new jspb.BinaryWriter();
  proto.hello.HelloRequest.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.hello.HelloRequest} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloRequest.serializeBinaryToWriter = function(message, writer) {
  var f = undefined;
  f = message.getName();
  if (f.length > 0) {
    writer.writeString(
      1,
      f
    );
  }
};


/**
 * optional string name = 1;
 * @return {string}
 */
proto.hello.HelloRequest.prototype.getName = function() {
  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};


/**
 * @param {string} value
 * @return {!proto.hello.HelloRequest} returns this
 */
proto.hello.HelloRequest.prototype.setName = function(value) {
  return jspb.Message.setProto3StringField(this, 1, value);
};





if (jspb.Message.GENERATE_TO_OBJECT) {
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.hello.HelloInfo.prototype.toObject = function(opt_includeInstance) {
  return proto.hello.HelloInfo.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.hello.HelloInfo} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloInfo.toObject = function(includeInstance, msg) {
  var f, obj = {
    helloData: jspb.Message.getFieldWithDefault(msg, 1, "")
  };

  if (includeInstance) {
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.hello.HelloInfo}
 */
proto.hello.HelloInfo.deserializeBinary = function(bytes) {
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.hello.HelloInfo;
  return proto.hello.HelloInfo.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.hello.HelloInfo} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.hello.HelloInfo}
 */
proto.hello.HelloInfo.deserializeBinaryFromReader = function(msg, reader) {
  while (reader.nextField()) {
    if (reader.isEndGroup()) {
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    case 1:
      var value = /** @type {string} */ (reader.readString());
      msg.setHelloData(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.hello.HelloInfo.prototype.serializeBinary = function() {
  var writer = new jspb.BinaryWriter();
  proto.hello.HelloInfo.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.hello.HelloInfo} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloInfo.serializeBinaryToWriter = function(message, writer) {
  var f = undefined;
  f = message.getHelloData();
  if (f.length > 0) {
    writer.writeString(
      1,
      f
    );
  }
};


/**
 * optional string hello_data = 1;
 * @return {string}
 */
proto.hello.HelloInfo.prototype.getHelloData = function() {
  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};


/**
 * @param {string} value
 * @return {!proto.hello.HelloInfo} returns this
 */
proto.hello.HelloInfo.prototype.setHelloData = function(value) {
  return jspb.Message.setProto3StringField(this, 1, value);
};


goog.object.extend(exports, proto.hello);

编写client.js(只先执行一个hello方法):

var {
  HelloServiceClient
} =require('./hello_grpc_web_pb');

var { HelloRequest } =require('./hello_pb');

let client = new HelloServiceClient('http://localhost:5005');

let helloRequest = new HelloRequest();
helloRequest.setName('tom');

client.hello(helloRequest, {}, (err, response) => {
  console.log(err, response);
});

打包client.js放到dist/main.js并在index.html头部引入:
npx webpack client.js

运行index.html:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
go写的服务端:

package main

import (
	"context"
	"fmt"
	"golang-grpc-demo/hello"
	"net"

	"google.golang.org/grpc"
)

// HelloServerImpl 定义hello接口实现
type HelloServerImpl struct {
	hello.UnimplementedHelloServiceServer
}

// Hello 定义hello的rpc方法
func (h *HelloServerImpl) Hello(ctx context.Context, request *hello.HelloRequest) (*hello.HelloInfo, error) {
	name := request.GetName()
	fmt.Println(name)
	return &hello.HelloInfo{HelloData: "hello " + name}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":8090")
	if err != nil {
		fmt.Println(err)
	}
	server := grpc.NewServer()
	hello.RegisterHelloServiceServer(server, &HelloServerImpl{})

	if err := server.Serve(lis); err != nil {
		fmt.Println(err)
	}
}

使用这个:
在这里插入图片描述

也可以监听:
在这里插入图片描述
在这里插入图片描述

再写一个clientold.js(去执行sayRepeatHello方法):

var {
  GreeterClient
} =require('./helloworld_grpc_web_pb');

var { HelloRequest,RepeatHelloRequest } =require('./helloworld_pb');

let client = new GreeterClient('http://localhost:5005');

let helloRequest = new HelloRequest();
helloRequest.setName('kitty');
// helloRequest.setCity('合肥');

client.sayHello(helloRequest, {}, (err, response) => {
  console.log(err, 22,response,response.array);
});

// client.sayRepeatHello(repeatHelloRequest, {});
// server streaming call
var streamRequest = new RepeatHelloRequest();
streamRequest.setName('World');
streamRequest.setCount(7);

var stream = client.sayRepeatHello(streamRequest, {});
stream.on('data', (response) => {
  console.log(response.getMessage());
});
stream.on('error', (err) => {
  console.log(`Unexpected stream error: code = ${err.code}` +
              `, message = "${err.message}"`);
});

在这里插入图片描述
服务端go代码:

package main

import (
	"context"
	"fmt"
	"golang-grpc-demo/hello"
	"net"
	"strconv"

	"google.golang.org/grpc"
)

// HelloServerImpl 定义hello接口实现
type HelloServerImpl struct {
	hello.UnimplementedGreeterServer
}

// SayHello 定义hello的rpc方法
func (h *HelloServerImpl) SayHello(ctx context.Context, request *hello.HelloRequest) (*hello.HelloReply, error) {
	name := request.GetName()
	fmt.Println("SayHello方法接收到请求参数:", name)
	return &hello.HelloReply{Message: "SayHello: " + name}, nil
}

// SayRepeatHello 定义hello的rpc方法
func (h *HelloServerImpl) SayRepeatHello(request *hello.RepeatHelloRequest, stream hello.Greeter_SayRepeatHelloServer) error {
	name := request.GetName()
	count := request.GetCount()
	fmt.Println("SayRepeatHello方法接收到请求参数:", name, count)
	for i := 0; i < int(count); i++ {
		stream.Send(&hello.HelloReply{Message: "Count:" + strconv.Itoa(i) + " SayHello: " + name + "\n"})
	}
	return nil
}

func main() {
	lis, err := net.Listen("tcp", ":8090")
	if err != nil {
		fmt.Println(err)
	}
	server := grpc.NewServer()
	hello.RegisterGreeterServer(server, new(HelloServerImpl))

	if err := server.Serve(lis); err != nil {
		fmt.Println(err)
	}
}

要求的类型:
在这里插入图片描述

测试一下,故意写一个字符串类型的次数:
在这里插入图片描述
结果可见:控制台报错了,请求并没有发到服务端:

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值