1. Java
maven配置
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>grpc-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>grpc-demo</name><description>grpc-demo</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version><protobuf.version>3.23.4</protobuf.version><protobuf-plugin.version>0.6.1</protobuf-plugin.version><grpc.version>1.58.0</grpc.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-netty-shaded</artifactId><version>1.29.0</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>${grpc.version}</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>${grpc.version}</version></dependency><dependency><groupId>org.apache.tomcat</groupId><artifactId>annotations-api</artifactId><version>6.0.53</version><scope>provided</scope></dependency><dependency><groupId>net.devh</groupId><artifactId>grpc-server-spring-boot-starter</artifactId><version>3.1.0.RELEASE</version></dependency></dependencies><build>
<!-- os-maven-plugin:此插件可以检测当前系统信息-->
<!-- ${os.detected.classifier}:这个变量获取操作系统的版本,例如osx-x86_64--><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.7.0</version></extension></extensions><plugins><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>${protobuf-plugin.version}</version><configuration><!-- 设置 protoc 的版本 --><protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact><!-- 设置插件的版本 --><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact><!-- proto文件目录 --><protoSourceRoot>${project.basedir}/src/main/resources</protoSourceRoot><!-- 设置输出目录 --><outputDirectory>${project.basedir}/src/main/java</outputDirectory><clearOutputDirectory>false</clearOutputDirectory></configuration><executions><execution><goals><!--编译消息对象--><goal>compile</goal><!--依赖消息对象,生成接口服务--><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build></project>
application.yml
grpc:server:port: 18081
proto文件
我的放在了resource目录下,是在maven配置中设置的
syntax = "proto3";option java_multiple_files = true;
option java_package = "com.example.grpcdemo.grpc.user.auto";
option java_outer_classname = "UserProto";service UserService {rpc saveUser (SaveUserRequest) returns (SaveUserReply);rpc queryUser (QueryUserRequest) returns (QueryUserReply);
}message SaveUserRequest {string name = 1;string age = 2;string sex = 3;
}message SaveUserReply {string status = 1;
}message QueryUserRequest{string name = 1;
}message QueryUserReply{string name = 1;string age = 2;string sex = 3;
}
打包之后auto文件夹下会自动生成编译后的文件
编辑你的服务类UserService
package com.example.grpcdemo.grpc.user;import com.example.grpcdemo.grpc.user.auto.QueryUserReply;
import com.example.grpcdemo.grpc.user.auto.QueryUserRequest;
import com.example.grpcdemo.grpc.user.auto.SaveUserReply;
import com.example.grpcdemo.grpc.user.auto.SaveUserRequest;
import com.example.grpcdemo.grpc.user.auto.UserServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;@GrpcService
public class UserService extends UserServiceGrpc.UserServiceImplBase {@Overridepublic void saveUser(SaveUserRequest request, StreamObserver<SaveUserReply> responseObserver) {System.out.println(request.toBuilder().build());SaveUserReply success = SaveUserReply.newBuilder().setStatus("success").build();responseObserver.onNext(success);responseObserver.onCompleted();}@Overridepublic void queryUser(QueryUserRequest request, StreamObserver<QueryUserReply> responseObserver) {System.out.println(request.getName());QueryUserReply reply = QueryUserReply.newBuilder().setAge("12").setName("小王").build();responseObserver.onNext(reply);responseObserver.onCompleted();}}
使用apiPost调用接口
附带一些常用的格式
@Overridepublic void query1(Query1Request request, StreamObserver<Query1Response> responseObserver) {Query1Response response = Query1Response.newBuilder().addUserArr(UserObj.newBuilder().setName("小王").setAge(20).build()).addUserArr(UserObj.newBuilder().setName("小李").setAge(21).build()).addUserArr(UserObj.newBuilder().setName("小赵").setAge(22)).addStringList("string1").addStringList("string2").putAMap("key1", "value1").putAMap("key2", "value2").build();responseObserver.onNext(response);responseObserver.onCompleted();}
接口返回:
2. 代理
浏览器目前不支持发送http2.0的请求,所以前端使用grpc-web封装请求,经过代理请求grpc服务端。nginx代理和envoy代理都可以。
2.1 Nginx
server {# TODO 1/2 配置监听的浏览器端口号listen 19090;http2 on;server_name localhost;location / {# 重点!!需要将Content-Type更改为 application/grpc# grpc-web过来的是application/grpc-web+proto || application/grpc-web+text (取决于生成js代码时grpc-web_out 的mode选项,本文用grpcweb 则为application/grpc-web+proto)grpc_set_header Content-Type application/grpc;# grpc_set_header TE trailers;# TODO 2/2 配置要转到的grpc服务和端口号grpc_pass grpc://192.168.3.100:19094;# grpc_ssl_verify off; # 关闭对grpc服务器的ssl证书验证# 因浏览器有跨域限制,这里直接在nginx支持跨域if ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,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';add_header 'Access-Control-Max-Age' 1728000;add_header 'Content-Type' 'text/plain charset=UTF-8';add_header 'Content-Length' 0;return 204;}if ($request_method = 'POST') {add_header 'Access-Control-Allow-Origin' '*';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,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';add_header 'Access-Control-Expose-Headers' 'Content-Transfer-Encoding, grpc-message,grpc-status';add_header 'Content-Type' 'text/plain charset=UTF-8';}}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
2.2 Envoy
envoy构建困难,好像目前只支持在docker部署
envoy中文网:https://icloudnative.io/envoy-handbook/docs/gettingstarted/setup/
envoy安装
# 获取镜像
docker pull envoyproxy/envoy# 启动 Envoy 容器时,可以用本地的 envoy.yaml 覆盖镜像中的 envoy.yaml
docker run -it -d \
--name envoy \
-p 19090:19090 \
-v /home/yk/envoyConfig/envoy.yaml:/etc/envoy/envoy.yaml\envoyproxy/envoy# 启动envoy
envoy -c envoy.yaml
envoy.yml
admin:access_log_path: /tmp/admin_access.logaddress:socket_address: { address: 0.0.0.0, port_value: 9901 }static_resources:listeners:- name: listener_0address:socket_address: { address: 0.0.0.0, port_value: 19090 }filter_chains:- filters:- name: envoy.filters.network.http_connection_managertyped_config:"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManagercodec_type: autostat_prefix: ingress_httproute_config:name: local_routevirtual_hosts:- name: local_servicedomains: ["*"]routes:- match: { prefix: "/" }route:cluster: echo_servicetimeout: 0smax_stream_duration:grpc_timeout_header_max: 0scors:allow_origin_string_match:- prefix: "*"allow_methods: GET, PUT, DELETE, POST, OPTIONSallow_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-timeoutmax_age: "1728000"expose_headers: custom-header-1,grpc-status,grpc-messagehttp_filters:- name: envoy.filters.http.grpc_webtyped_config:"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb- name: envoy.filters.http.corstyped_config:"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors- name: envoy.filters.http.routertyped_config:"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Routerclusters:- name: echo_serviceconnect_timeout: 0.25stype: logical_dns# HTTP/2 supporttyped_extension_protocol_options:envoy.extensions.upstreams.http.v3.HttpProtocolOptions:"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptionsexplicit_http_config:http2_protocol_options: {}lb_policy: round_robinload_assignment:cluster_name: cluster_0endpoints:- lb_endpoints:- endpoint:address:socket_address:address: 192.168.3.100port_value: 19094