前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >gRPC完结:实战gRPC整合SpringBoot实现简单聊天室

gRPC完结:实战gRPC整合SpringBoot实现简单聊天室

作者头像
AI码师
发布2023-12-14 16:50:26
发布2023-12-14 16:50:26
71300
代码可运行
举报
运行总次数:0
代码可运行

这节课通过整合SpringBoot实现一个简单聊天室

聊天室服务端搭建

创建一个SpringBoot项目

配置gradle

添加插件
代码语言:javascript
代码运行次数:0
运行
复制
plugins {
    id 'java'
    id 'com.google.protobuf' version '0.9.4'
    id 'org.springframework.boot' version '2.7.14'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
设置版本信息
代码语言:javascript
代码运行次数:0
运行
复制
def grpcVersion = '1.57.2' // CURRENT_GRPC_VERSION
def protobufVersion = '3.24.0'
def protocVersion = protobufVersion
必备依赖
代码语言:javascript
代码运行次数:0
运行
复制
dependencies {
    implementation "net.devh:grpc-spring-boot-starter:2.14.0.RELEASE"
    implementation "io.grpc:grpc-protobuf:${grpcVersion}"
    implementation "io.grpc:grpc-services:${grpcVersion}"
    implementation "io.grpc:grpc-stub:${grpcVersion}"
    compileOnly "org.apache.tomcat:annotations-api:6.0.53"
    implementation "com.google.protobuf:protobuf-java:${protobufVersion}"
    // examples/advanced need this for JsonFormat
    implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
    runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
配置protobuf
代码语言:javascript
代码运行次数:0
运行
复制
protobuf {
    protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
    plugins {
        grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
    }
    generateProtoTasks {
        all()*.plugins { grpc {} }
    }
}
sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

定义chatproto

代码语言:javascript
代码运行次数:0
运行
复制
syntax = "proto3";

package com.lglbc.chatroom;

service ChatService {
  rpc JoinRoom (stream JoinRequest) returns (stream ChatMessage);
  rpc SendMessage (ChatMessage) returns (Empty);
}

message JoinRequest {
  string username = 1;
}

message ChatMessage {
  string username = 1;
  string message = 2;
}

message Empty {}

生成java文件
代码语言:javascript
代码运行次数:0
运行
复制
 ./gradlew generateProto

实现核心逻辑

代码语言:javascript
代码运行次数:0
运行
复制
package com.lglbc.grpcbootchat;

import com.lglbc.chatroom.Chat;
import com.lglbc.chatroom.ChatServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

import java.util.concurrent.ConcurrentHashMap;

@GrpcService
public class ChatServiceImpl extends ChatServiceGrpc.ChatServiceImplBase {

    private final ConcurrentHashMap<String, StreamObserver<Chat.ChatMessage>> userObservers = new ConcurrentHashMap<>();

    @Override
    public StreamObserver<Chat.JoinRequest> joinRoom(StreamObserver<Chat.ChatMessage> responseObserver) {
        return new StreamObserver<Chat.JoinRequest>() {
            private String username;

            @Override
            public void onNext(Chat.JoinRequest joinRequest) {
                username = joinRequest.getUsername();
                userObservers.put(username, responseObserver);

                broadcastMessage(username, "joined the room.");
            }

            @Override
            public void onError(Throwable t) {
                userObservers.remove(username);
                broadcastMessage(username, "left the room.");
            }

            @Override
            public void onCompleted() {
                userObservers.remove(username);
                broadcastMessage(username, "left the room.");
                responseObserver.onCompleted();
            }
        };
    }

    @Override
    public void sendMessage(Chat.ChatMessage request, StreamObserver<Chat.Empty> responseObserver) {
        String message = "[" + request.getUsername() + "]: " + request.getMessage();
        broadcastMessage(request.getUsername(), message);
        responseObserver.onNext(Chat.Empty.getDefaultInstance());
        responseObserver.onCompleted();
    }

    private void broadcastMessage(String username, String message) {
        Chat.ChatMessage chatMessage = Chat.ChatMessage.newBuilder()
                .setUsername(username)
                .setMessage(message)
                .build();

        for (StreamObserver<Chat.ChatMessage> observer : userObservers.values()) {
            observer.onNext(chatMessage);
        }
    }
}

代码解释

  • userObservers:维护客户端的channel
  • onNext: 维护userObservers
  • broadcastMessage:广播消息给其他客户端

搭建客户端

客户端比较简单,拷贝之前的实现就可以

拷贝服务端生成的java文件

实现客户端逻辑

代码语言:javascript
代码运行次数:0
运行
复制
package com.lglbc;


import com.lglbc.chatroom.Chat;
import com.lglbc.chatroom.ChatServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

import java.util.Scanner;

public class ChatroomClient {

    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090)
                .usePlaintext()
                .build();

        ChatServiceGrpc.ChatServiceStub stub = ChatServiceGrpc.newStub(channel);

        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter your username: ");
        String username = scanner.nextLine();

        StreamObserver<Chat.ChatMessage> chatStreamObserver = new StreamObserver<Chat.ChatMessage>() {
            @Override
            public void onNext(Chat.ChatMessage chatMessage) {
                System.out.println(chatMessage.getUsername() + ": " + chatMessage.getMessage());
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println("Error: " + throwable.getMessage());
            }

            @Override
            public void onCompleted() {
                System.out.println("Chat stream completed.");
            }
        };

        StreamObserver<Chat.JoinRequest> joinStreamObserver = stub.joinRoom(chatStreamObserver);

        joinStreamObserver.onNext(Chat.JoinRequest.newBuilder().setUsername(username).build());

        while (true) {
            String message = scanner.nextLine();
            if ("exit".equalsIgnoreCase(message)) {
                joinStreamObserver.onCompleted();
                break;
            }

            Chat.ChatMessage chatMessage = Chat.ChatMessage.newBuilder()
                    .setUsername(username)
                    .setMessage(message)
                    .build();
            stub.sendMessage(chatMessage, new StreamObserver<Chat.Empty>() {
                @Override
                public void onNext(Chat.Empty value) {}

                @Override
                public void onError(Throwable t) {
                    System.out.println("Error sending message: " + t.getMessage());
                }

                @Override
                public void onCompleted() {}
            });
        }

        channel.shutdown();
    }
}

演示

启动服务端

启动客户端

再启动一个客户端

发送一个消息

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 乐哥聊编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 聊天室服务端搭建
    • 创建一个SpringBoot项目
    • 配置gradle
      • 添加插件
      • 设置版本信息
      • 必备依赖
      • 配置protobuf
    • 定义chatproto
      • 生成java文件
    • 实现核心逻辑
  • 搭建客户端
    • 拷贝服务端生成的java文件
    • 实现客户端逻辑
  • 演示
    • 启动服务端
    • 启动客户端
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档