使用grpc-server-spring-boot-starter及nacos搭建grpc服务

Spring Cloud + Nacos搭建Grpc服务实战指南
本文详细介绍了如何在Spring Cloud环境中利用Nacos配置管理和服务注册,结合protobuf-maven-plugin编译生成的protobuf库,实现provider和consumer之间的gRPC通信。通过创建cloud-grpc-provider和cloud-grpc-consumer模块,配置application.yml,以及使用GrpcService和@GrpcClient,展示了从服务注册到消费者调用的完整流程。

在上一篇文章《java使用protobuf-maven-plugin的插件编译proto文件》中,我们使用protobuf-maven-plugin已经生成了grpc的调用的库,这篇文章我们将讲解使用SpringCloud及nacos搭建grpc服务。

具体的实现步骤:

1. 先在github下载一个nacos的release版本应用,下载​​​​​​​地址。 解压缩之后,进到nacos目录,执行如下指令启动nacos,这里我们测试,所以启动的是单机服务:

sh ./bin/startup.sh -m standalone

运行成功后在网页访问:http://localhost:8848/nacos,打开后进到nacos登录页面,使用默认账号/密码:nacos/nacos进行登录。

登录成功后,我们可以看到nacos提供了ConfigManagement和ServiceManagement,我们要看的是服务注册中心的ServiceManagement。

2. 我们在上一章lib的工程中,创建三个module,分别为cloud-grpc-provider9090和cloud-grpc-provider9091和cloud-grpc-consumer10020。 前两个提供服务的grpc的provider的服务端, 后面一个consumer为grpc的消费端。项目结构如下:

3. 在父工程的POM文件中添加如下包依赖

 <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <jackson.version>2.11.1</jackson.version>
        <protobuf.plugin.version>0.6.1</protobuf.plugin.version>
        <spring.boot.version>2.3.9.RELEASE</spring.boot.version>
        <springcloud.version>Hoxton.SR9</springcloud.version>
        <springcloud.alibaba.version>2.1.0.RELEASE</springcloud.alibaba.version>
        <protobuf.version>3.14.0</protobuf.version>
        <grpc.java.version>1.35.0</grpc.java.version>
        <!--grpc.starter.version>2.11.0.RELEASE</grpc.starter.version-->
        <grpc.starter.version>2.13.1.RELEASE</grpc.starter.version>
    </properties>

    <dependencyManagement>
        <dependencies>
<!--            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${springcloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${springcloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- jackson-xml -->
            <dependency>
                <groupId>com.fasterxml.jackson.dataformat</groupId>
                <artifactId>jackson-dataformat-xml</artifactId>
                <version>${jackson.version}</version>
            </dependency>

            <!-- grpc -->
            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-client-spring-boot-starter</artifactId>
                <version>${grpc.starter.version}</version>
            </dependency>

            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-client-spring-boot-autoconfigure</artifactId>
                <version>${grpc.starter.version}</version>
            </dependency>

            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-server-spring-boot-starter</artifactId>
                <version>${grpc.starter.version}</version>
            </dependency>

            <dependency>
                <groupId>net.devh</groupId>
                <artifactId>grpc-server-spring-boot-autoconfigure</artifactId>
                <version>${grpc.starter.version}</version>
            </dependency>


            <!-- proto -->
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-services</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-api</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-context</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>

            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-core</artifactId>
                <version>${grpc.java.version}</version>
            </dependency>
        </dependencies>

    </dependencyManagement>

这里我们使用的spring-boot版本与grpc-client-spring-boot-starter和grpc-server-spring-boot-starter使用的版本不同,所以后面我们需要在grpc的starter排除掉默认的spring-boot版本。同时grpc的相关一些库都使用统一的版本。

这里可以看到grpc底层使用的是netty实现的消息通讯

 4. 配置application.yml文件

//provider的application.yml

server:
  port: 8081

grpc:
  server:
    port: 9091
    security:
      enabled: false

spring:
  application:
    name: cloud-grpc-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

# actuator management
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: '*'
//consumer的application.yml

server:
  port: 10020

spring:
  application:
    name: cloud-grpc-comsumber
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

grpc:
  client:
    cloud-grpc-provider:
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: PLAINTEXT
    GLOBAL:
      security:
        client-auth-enabled: false

以上是provider和consumer的application.yml配置文件。 这里我们使用的同一台机器进行测试,所以要注意不同的provider和consumer应用的server的端口和grpc的端口不要相同。
同时在provider和consumer,将grpc的security的enable开关关掉。consumer端要注意的是设置negotiationType的类型为PLAINTEXT

5. provider和consumer引入相应的包

provider的maven引入:

<dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>cn.ckeen</groupId>
            <artifactId>cloud-grpc-lib</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

consumer的maven引入:

 <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>cn.ckeen</groupId>
            <artifactId>cloud-grpc-lib</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

两个包都引入了lib包,作为客户端和服务端通讯协议。使用nacos作为注册中心,实现负载均衡。

6. provider的service编写

@GrpcService
public class GreeterService extends SimpleGrpc.SimpleImplBase {

    private static Logger log = LoggerFactory.getLogger(GreeterService.class);

    // 引入该参数是为了做负载均衡验证
    @Value("${grpc.server.port}")
    private String serverPort;

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
        String message = "server port:" + serverPort + ",Hello " + request.getName();
        final HelloReply.Builder replyBuilder = HelloReply.newBuilder().setMessage(message);
        responseObserver.onNext(replyBuilder.build());
        responseObserver.onCompleted();
        log.info("Returning " +message);

        responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("测试出错").asException());
    }
}

首先使用GrpcService注解来标识服务,该服务继承自proto文件生成的grpc的SimpleGrpc.SimpleImplBase,同时实现了sayHello的方法,这里我们通过responseObserver将结果返回。

7. consumer的消费

consumer端比较简单,直接使用@GrpcClient的注解生成一个SimpleGrpc的Stub,然后进行调用即可

@Service
public class GreeterService {

    @GrpcClient("cloud-grpc-provider")
    private SimpleGrpc.SimpleBlockingStub simpleBlockingStub;

    public String greet(String name) {

        try {
            HelloReply response = simpleBlockingStub.sayHello(HelloRequest.newBuilder().setName(name).build());
            return response.getMessage();
        } catch (final StatusRuntimeException e) {
            return "FAILED with " + e.getStatus().getCode();
        }
    }

}

最终我们可以写一个controller来调用该服务进行测试,实现代码如下:

    @Resource
    private GreeterService greeterService;

    @GetMapping("greeter")
    public String greet(@RequestParam("name")String name){
        String result = greeterService.greet(name);
        return result;
    }

8. 测试验证

先启动两个provider,我们可以在nacos的service list看到两个provider的注册信息:

 启动consumer访问http://localhost:10020/greeter?name=keen可以看到有返回,刷新后端口还有变化

到此我们简单的一个grpc的服务就已经实现了

demo地址: https://github.com/keenw/spring-cloud-grpc-ck

<think> 我们面对的问题是:在Nacos 2.4.1中修改配置发布后,Java程序自动刷新,但无法使用spring-cloud-starter-alibaba-nacos-config。 根据引用[3]和[5]的信息,可能涉及到版本兼容性问题。特别是引用[3]提到,新版本(2023.0.3.2)的spring-cloud-starter-alibaba-nacos-config需要与Spring Boot 3.x兼容,而旧版本可能不支持Nacos 2.4.1。 解决方案: 1. 确认Spring Boot版本: - 如果使用Spring Boot 2.x,应选择Spring Cloud Alibaba 2021.x或2.2.x版本。 - 如果使用Spring Boot 3.x,则必须使用Spring Cloud Alibaba 2022.x及以上版本(如2022.0.0.0-RC2或2023.0.3.2)。 2. 添加正确的依赖: - 对于Spring Boot 3.x,需要添加以下依赖(参考引用[3]): ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2023.0.3.2</version> </dependency> ``` - 同时,由于Spring Boot 3.x中默认不启用bootstrap配置,需要额外添加bootstrap依赖(参考引用[3]): ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> <version>3.1.1</version> </dependency> ``` 3. 配置文件: - 使用`bootstrap.yml`(或`bootstrap.properties`)来配置Nacos,而不是`application.yml`(参考引用[2])。 - 示例配置(参考引用[1][2]): ```yaml spring: application: name: your-service-name cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yaml refresh-enabled: true # 确保自动刷新开启 ``` 4. 代码中使用配置: - 使用`@RefreshScope`和`@Value`组合,或者使用`@ConfigurationProperties`(参考引用[1])。 5. 如果仍然不能自动刷新,检查Nacos配置的Data ID是否与`spring.application.name`匹配,以及Group、Namespace等是否一致。 6. 日志检查: - 检查应用启动日志,确认是否成功从Nacos获取配置。 - 当修改配置后,查看日志中是否有刷新日志,如`Refresh keys changed: [...]`。 7. 特别注意:Nacos 2.4.1可能需要客户端使用gRPC端口(9848)进行通信,确保防火墙开放此端口。 综上所述,主要问题可能是版本不匹配或配置不正确。根据Spring Boot版本选择正确的Spring Cloud Alibaba版本,并正确配置bootstrap文件即可解决。 如果问题仍然存在,请提供以下信息: - Spring Boot版本 - Spring Cloud Alibaba版本 - 配置文件内容(脱敏) - 相关日志 </think>### Nacos 2.4.1 配置自动刷新问题的解决方案 在 Nacos 2.4.1 中遇到配置修改后 Java 程序无法自动刷新的问题,通常与 **Spring Cloud Alibaba 版本兼容性**和**配置方式**有关。以下是完整解决方案: #### 一、版本兼容性问题(核心原因) Nacos 2.4.1 需要特定版本的 Spring Cloud Alibaba 支持: ```xml <!-- Spring Boot 2.x 使用 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2021.0.5.0</version> <!-- 兼容 Nacos 2.4.x --> </dependency> <!-- Spring Boot 3.x 使用 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2023.0.3.2</version> <!-- 必须添加 bootstrap 依赖 --> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> <version>3.1.1</version> <!-- 关键!Spring Boot 3.x 需显式引入 --> </dependency> ``` #### 二、配置文件修复 在 `bootstrap.yml` 中**必须配置**以下参数: ```yaml spring: application: name: your-service-name # 需与 Nacos Data ID 匹配 cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yaml # 配置文件后缀 refresh-enabled: true # 显式启用刷新 # 2.4.1 需要显式声明命名空间(即使使用 public) namespace: public # 添加长轮询参数(解决 2.4.1 刷新延迟) config-long-poll-timeout: 30000 config-retry-time: 3000 # 新版本需要 discovery 配置 discovery: server-addr: 127.0.0.1:8848 ``` #### 三、代码层修复 1. **添加刷新作用域注解**: ```java @RestController @RefreshScope // 必须添加此注解 public class ConfigController { @Value("${config.key}") private String configValue; // 自动刷新字段 } ``` 2. **使用配置类绑定**: ```java @Component @ConfigurationProperties(prefix = "app") public class AppConfig { private String dynamicParam; // 自动刷新字段 // 必须有 setter 方法 public void setDynamicParam(String v) { this.dynamicParam = v; } } ``` #### 四、排查步骤 1. **检查客户端日志**: ```log [Nacos Config] Listening config: dataId=your-service-name.yaml ``` 若未出现此日志,说明配置未加载成功 2. **验证配置获取**: ```bash curl http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=your-service-name.yaml ``` 3. **开启调试日志**: ```yaml logging: level: com.alibaba.nacos: DEBUG ``` #### 五、常见问题解决 1. **配置未生效**: - 确保使用 `bootstrap.yml` 而非 `application.yml`[^2] - Data ID 格式必须为 `${spring.application.name}.${file-extension}` 2. **长轮询失败**: ```yaml # 增加网络超时配置 spring: cloud: nacos: config: max-retry-timeout: 5000 config-long-poll-timeout: 30000 ``` 3. **权限问题**: - Nacos 2.4.1 默认开启鉴权,需在配置中添加: ```yaml spring: cloud: nacos: config: username: nacos password: nacos ``` > **重要提示**:Nacos 2.4.1 需要开放 **9848/9849** 端口(gRPC 通信端口),防火墙阻塞会导致刷新失败[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿CKeen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值