grpc web example

官网代码

下载代码git clone https://github.com/grpc/grpc-web.git
进到grpc-web/net/grpc/gateway/examples/hellpworld/
按照README.md运行示例

proto文件定义

请求 HelloRequest、RepeatHelloRequest
响应 HelloReply
方法定义 SayHello、SayRepeatHello
hello.proto

syntax = "proto3";

package helloworld;

service Greeter { //定义了2个rpc:SayHello、SayRepeatHello
  // unary call
  rpc SayHello(HelloRequest) returns (HelloReply);  //请求只有一个参数name
  
  // server streaming call
  rpc SayRepeatHello(RepeatHelloRequest) returns (stream HelloReply);  //请求有2个参数:name、count(重复几次)
}

message HelloRequest {
  string name = 1;
}

message RepeatHelloRequest {
  string name = 1;
  int32 count = 2;
}

message HelloReply {
  string message = 1;
}

实现server

node.js实现server,实现2个rpc的具体操作
server.js


var PROTO_PATH = __dirname + '/helloworld.proto';                                                                   

var assert = require('assert');                                                                                     
var async = require('async');                                                                                       
var _ = require('lodash');
var grpc = require('@grpc/grpc-js');
var protoLoader = require('@grpc/proto-loader');                                                                    
var packageDefinition = protoLoader.loadSync(                                                                       
    PROTO_PATH,
    {keepCase: true,                                                                                                
     longs: String,                                                                                                 
     enums: String,
     defaults: true,                                                                                                
     oneofs: true                                                                                                   
    });
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);                                                
var helloworld = protoDescriptor.helloworld;                                                                        

/**
 * @param {!Object} call
 * @param {function():?} callback                                                                                   
 */
//实现rpc:SayHello
function doSayHello(call, callback) { 
   //取出请求中的参数,组装发回响应response
  callback(null, {message: 'Hello! '+ call.request.name});                                                   
}                                                                                                                   

/**
 * @param {!Object} call                                                                                            
 */
//实现rpc:SayRepeatHello
function doSayRepeatHello(call) {                                                                             
  var senders = [];
  function sender(name) {
    return (callback) => {                                                                                          
      call.write({
        message: 'Hey! ' + name                                                                                     
      });
      _.delay(callback, 500); // in ms                                                                              
    };                                                                                                              
  }
  for (var i = 0; i < call.request.count; i++) {    //取出请求中参数:重复次数,返回多次                                                               
    senders[i] = sender(call.request.name + i);                                                                     
  }
  async.series(senders, () => {                                                                                     
    call.end();                                                                                                     
  });                                                                                                               
}                                   
/**
 * @return {!Object} gRPC server                                                                                    
 */
function getServer() {
  var server = new grpc.Server();
  server.addService(helloworld.Greeter.service, {
    sayHello: doSayHello,
    sayRepeatHello: doSayRepeatHello,
  });
  return server;
}

if (require.main === module) {
  var server = getServer();
  server.bindAsync(
    '0.0.0.0:9090', grpc.ServerCredentials.createInsecure(), (err, port) => {
      assert.ifError(err);
      server.start();
  });
}

exports.getServer = getServer;

Envoy proxy

监听8080,转发浏览器的grpc请求给后端,cluster端口9090
envoy.yaml

static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                  - name: local_service
                    domains: ["*"]
                    routes:
                      - match: { prefix: "/" }
                        route:
                          cluster: greeter_service
                          max_stream_duration:
                            grpc_timeout_header_max: 0s
                    cors:
                      allow_origin_string_match:
                        - prefix: "*"
                      allow_methods: GET, PUT, DELETE, POST, OPTIONS
                      allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                      max_age: "1728000"
                      expose_headers: custom-header-1,grpc-status,grpc-message
              http_filters:
                - name: envoy.filters.http.grpc_web
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
                - name: envoy.filters.http.cors
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
                - name: envoy.filters.http.router
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
    - name: greeter_service
      connect_timeout: 0.25s
      type: logical_dns
      http2_protocol_options: {}
      lb_policy: round_robin
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 0.0.0.0
                    port_value: 9090

protob编译js

protoc -I=. helloworld.proto \
  --js_out=import_style=commonjs:. \
  --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

得到2个新文件
helloworld_grpc_web_pb.js //定义GreeterClient
helloworld_pb.js //定义HelloRequest, HelloReply

client实现

用到上面生成的那2个文件
client.js

const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

var client = new GreeterClient('http://localhost:8080');

var request = new HelloRequest(); 
request.setName('World'); 

client.sayHello(request, {}, (err, response) => {
  console.log(response.getMessage());
});

----
const {HelloRequest, RepeatHelloRequest,
       HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

var client = new GreeterClient('http://' + window.location.hostname + ':8080',
                               null, null);  //请求发送到8080端口

// simple unary call
var request = new HelloRequest(); //构建请求
request.setName('World'); //给请求中的参数赋值,设置name为World

client.sayHello(request, {}, (err, response) => {
  if (err) {
    console.log(`Unexpected error for sayHello: code = ${err.code}` +
                `, message = "${err.message}"`);
  } else {
    console.log(response.getMessage());
  }
});


// server streaming call
var streamRequest = new RepeatHelloRequest();
streamRequest.setName('World');
streamRequest.setCount(5); //重复5次

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}"`);
});
"client.js" 52L, 1615C              

编译client js代码

npm install

多了文件夹node_modules、package-lock.json

npx webpack client.js
把client.js编译为main.js,可在浏览器执行

输出

$ npx webpack client.js
Hash: 311d4a8b03eac66704bc
Version: webpack 4.43.0
Time: 1542ms
Built at: 2022/06/14 17:22:34
  Asset     Size  Chunks                    Chunk Names
main.js  293 KiB       0  [emitted]  [big]  main
Entrypoint main [big] = main.js
[0] (webpack)/buildin/global.js 472 bytes {0} [built]
[1] ./helloworld_pb.js 14.1 KiB {0} [built]
[2] ./client.js 1.58 KiB {0} [built]
[8] ./helloworld_grpc_web_pb.js 4.69 KiB {0} [built]
    + 6 hidden modules

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  main.js (293 KiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  main (293 KiB)
      main.js


WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/

生成 dist/main.js

运行

启grpc server,监听9090端口

$ node server.js &

运行envoy代理,运行一个docker镜像,把8080转到9090端口

 $ docker run -d -v "$(pwd)"/envoy.yaml:/etc/envoy/envoy.yaml:ro \
     --network=host envoyproxy/envoy:v1.22.0

运行web server
开启文件服务,浏览器解析index.html,脚本main.js(由client.js编译得到)

 $ python3 -m http.server 8081 &

打开浏览器,F12打开调试看输出
地址 localhost:8081

结果

在这里插入图片描述

中间遇到的问题

浏览器打不开localhost:8081
phthon -m client.js报错输出 code 400, message Bad request syntax 乱码
http与https地址栏https改为http

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值