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/> |
| </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> |
| |
| |
| <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> |
| |
| <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> |
| |
| <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 { |
| @Override |
| public 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(); |
| } |
| |
| @Override |
| public 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调用接口

附带一些常用的格式
| @Override |
| public 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 { |
| |
| listen 19090; |
| http2 on; |
| server_name localhost; |
| |
| location / { |
| |
| |
| grpc_set_header Content-Type application/grpc; |
| |
| |
| |
| grpc_pass grpc://192.168.3.100:19094; |
| |
| |
| |
| 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.log |
| address: |
| socket_address: { address: 0.0.0.0, port_value: 9901 } |
| |
| static_resources: |
| listeners: |
| - name: listener_0 |
| address: |
| socket_address: { address: 0.0.0.0, port_value: 19090 } |
| 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: echo_service |
| timeout: 0s |
| 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: echo_service |
| connect_timeout: 0.25s |
| type: logical_dns |
| |
| typed_extension_protocol_options: |
| envoy.extensions.upstreams.http.v3.HttpProtocolOptions: |
| "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions |
| explicit_http_config: |
| http2_protocol_options: {} |
| lb_policy: round_robin |
| load_assignment: |
| cluster_name: cluster_0 |
| endpoints: |
| - lb_endpoints: |
| - endpoint: |
| address: |
| socket_address: |
| address: 192.168.3.100 |
| port_value: 19094 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构