Set Up
The final directory structure will look like this.
go |_prj |_gRPC_prjweb_tmp |-api.proto |-client | |_client.go |-docker-compose.yaml |-go.mod |-go.sum |-html | |-dist | | |_main.js | |-index.html | |_index.js |-Makefile |-pb | |-api_grpc_web_pb.js | |-api.pb.go | |-api_pb.js | |-node_modeles | |-package.json | |_package-lock.json |-proxy | |-conf | | |_envoy.yaml | |_Dockerfile |_server |_server.go
And network structure will look like this.
gRPC_web(4444)<-->envoy(8080)<-->gRPC_server(8000)
First, build the environment.
go mod init web_tmpgo get google.golang.org/protobuf github.com/sirupsen/logrus golang.org/x/net/context google.golang.org/grpc google.golang.org/grpc/codes google.golang.org/grpc/status
gRPC
Create a proto file.
[api.proto]
syntax = "proto3";service Greeter { rpc Hello (HelloRequest) returns (HelloReply) {}}message HelloRequest { string name = 1;}message HelloReply { string message = 1;}
Create a pb directory, and generate Protocol Buffers code from api.proto file.
mkdir pbprotoc --go_out=plugins=grpc:pb api.proto
Create a server directory and a client directory.
mkdir servermkdir client
Then we'll write the server and client code.
[sever/server.go]
package mainimport ( "fmt" pb "web_tmp/pb" "net" "github.com/sirupsen/logrus" "gINFO[0000] Greeting: Hello lupinolang.org/x/net/context" "google.golang.org/grpc")func main() { listener, err := net.Listen("tcp", ":8000") if err != nil { panic(err) } server := grpc.NewServer() greeterService := &GreeterService{} pb.RegisterGreeterServer(server, greeterService) logrus.Info(fmt.Sprintf("start server: %#v", listener.Addr().String())) server.Serve(listener)}type GreeterService struct {}func (s *GreeterService) Hello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil}
[client/client.go]
package mainimport ( "context" pb "web_tmp/pb" "github.com/sirupsen/logrus" "google.golang.org/grpc")func main() { conn, err := grpc.Dial("localhost:8000", grpc.WithInsecure()) if err != nil { panic(err) } defer conn.Close() c := pb.NewGreeterClient(conn) name := &pb.HelloRequest{Name: "lupin"} r, err := c.Hello(context.TODO(), name) if err != nil { logrus.Error(err) } logrus.Info("Greeting: ", r.GetMessage())}
Create a makefile.
[Makefile]
.PHONY: server clientserver: go run ./server/server.goclient: go run ./client/client.go
Start the server.
make server
Run the client code.
make client
We can see the gRPC server returning "Hello lupin".
INFO[0000] Greeting: Hello lupin
gRPC-web
Next, we'll write the code for gRPC-web communication.
protoc --js_out=import_style=commonjs:pb --grpc-web_out=import_style=commonjs,mode=grpcwebtext:pb api.proto
The api_grpc_web_pb created here under pb directory will be imported into the js file to be created later. Also, install the modules grpc-web and google-protobuf as they are required for webpack.
cd pbnpm install grpc-webnpm install google-protobuf
Create an html directory and create a js file.
mkdir html
[html/index.js]
import proto from '../pb/api_grpc_web_pb';var client = new proto.GreeterClient('http://localhost:8080');var request = new proto.HelloRequest();request.setName("lupin");client.hello(request, {}, function(err, response) { if (err) { console.log(err.code); console.log(err.message); } else { console.log(response.getMessage()); }});
Proxy
Create a proxy directory and a conf directory under it for prepareing envpy proxy. Write the envoy.yaml file under the conf directory you created.
[proxy/conf/envoy.yaml]
admin: access_log_path: /tmp/admin_access.log address: socket_address: { address: 127.0.0.1, port_value: 9901 }static_resources: listeners: - name: listener_0 address: socket_address: { address: 127.0.0.1, port_value: 8080 } filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager codec_type: auto stat_prefix: ingress_http access_log: - name: envoy.access_loggers.file typed_config: "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog path: "/dev/stdout" route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: { prefix: "/" } route: cluster: greeter_service max_grpc_timeout: 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 - name: envoy.filters.http.cors - name: envoy.filters.http.router clusters: - name: greeter_service connect_timeout: 0.25s type: logical_dns http2_protocol_options: {} lb_policy: round_robin dns_lookup_family: V4_ONLY upstream_connection_options: tcp_keepalive: keepalive_time: 300 load_assignment: cluster_name: cluster_0 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 172.17.0.1 port_value: 8000
Administration interface of envoy is 9901 port, and envoy proxy is 8080 port. Envoy passes the received communication to the server on port 8000. The docker address may vary depending on your environment, so check it.
ip a | grep docker
Create the Docker file.
[proxy/Dockerfile]
FROM envoyproxy/envoy:v1.15.0COPY ./conf/envoy.yaml /etc/envoy/envoy.yamlCMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml
Start up the container in a proxy directory.
docker build -t envoy/hello_lupin .docker run -d --rm -p 8080:8080 -p 9901:9901 -v ~/go/prj/gRPC_prj/web_tmp/proxy/conf:/etc/envoy --name Greeter envoy/hello_lupin
Or create docker-compose.yaml in web_tmp directory.
[docker-compose.yaml]
version: '3'services: envoy: build: context: ./proxy image: hello_lupin container_name: Greeter ports: - 8080:8080
Then,
docker-compose up
By the way, if you want to stop the container
docker stop Greeter
Add the webpack command and the command to build the server to the make file and run it.
[Makefile]
.PHONY: server client make_webclientserver: go run ./server/server.goclient: go run ./client/client.goweb_client: cd ./html && webpack ./index.js && static -p 4444
Then,
make web_client
Access 4444 port on localhost with a browser and check the Developer Tools console.
Top comments(3)

- Email
- LocationItaly
- WorkComputer science student
- Joined
Seems to early for grpc on browser... I made an app, initially the idea was create a site with grpc, but I ended up with an app, only for the grpc native

Hi@ikk_hck ,
it's a very interesting article!
I tried to follow this tutorial but I had a problem at the end of this steps.
When I run the following command:
make web_client
I received this error:
ERROR in ../pb/node_modules/google-protobuf/google-protobuf.js
Module not found: Error: Can't resolve './../../../html/buffer' in '/home/ubuntu/gitprojects/grpc-test/tutorialprj/pb/node_modules/google-protobuf'
@ ../pb/node_modules/google-protobuf/google-protobuf.js 1:0-33
@ ../pb/api_pb.js
@ ../pb/api_grpc_web_pb.js
@ ./index.js
It's due to "webpack ./index.js" command.
Do you know how I could fix this error?
Thank you very much!
For further actions, you may consider blocking this person and/orreporting abuse