grpc实现一个较复杂的聊天室

本文介绍了如何使用gRPC、etcdv3和Keepalive机制来实现一个聊天室的负载均衡。通过服务注册、客户端Watch以及利用etcd的PUT+WATCH广播消息,解决了在高压力下聊天室的扩展性和服务发现问题。同时,将身份验证与聊天操作分离,以优化断线重连体验。完整实现可在GitHub找到。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

接上文 https://my.oschina.net/tuxpy/blog/1631953

之前通过grpc实现了一个双向流的方式来实现聊天室. 所有的stream都是存在一个sync.Map里。如果这时候聊天室的压力大了,如何做扩展? 说得简单点就是怎么样来做负载均衡。之前在https://segmentfault.com/a/1190000008672912 看到过一篇借助etcd来实现负载均衡的文章,就借来实现了哈.

问题

  1. grpc的服务发现如何做?
  2. 某一个client发送的聊天内容,如何让分布在不同的节点上的client都收到
  3. 当某些client连接的service节点down了,如何让client重新发现并连接健康的节点? 如果所有的rpc方法都是request <-> response模式 倒没什么问题,大不了有可能会丢次当前正在处理的请求. 但是像聊天室这类应用,使用双向流模式, 一旦一个节点down了,后续服务端的stream就会得到codes.Unavailable错误

解决

  1. 参考https://segmentfault.com/a/1190000008672912 。服务端注册key, 定期keepalive, 客户端watch
  2. 利用etcd的put + watch实现一个remote channel,所有服务节点监听某一个key的PUT行为,根据value内容调用相应的方法(如广播消息给连接到自己的所有客户端). 之前还想过,每一个service同时还是一个grpc client,每次自己有广播行为时, 远程调用其他节点广播操作。天呐,还要维护一份所有节点的清单,杀了我吧.
  3. 将身份验证操作和聊天操作分离出来,先进行身份验证,取出token, 后续聊天的rpc调用时带上. token验证借助etcd实现的session, 每次断线重连,只影响聊天的stream, 不会重复登录.

实现

service

/*
 *
 *     Author        : tuxpy
 *     Email         : q8886888@qq.com.com
 *     Create time   : 3/7/18 9:18 AM
 *     Filename      : service.go
 *     Description   :
 *
 *
 */

package main

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	grpclb "grpclb/etcdv3"
	pb "grpclb/helloword"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
	"utils"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"github.com/golang/protobuf/ptypes/timestamp"
	"github.com/pkg/errors"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
)

type Service struct{}

type ConnectPool struct {
	sync.Map
}

type RemoteCommand struct {
	Command string
	Args    map[string]string
}
type RemoteChannel struct {
	In  chan RemoteCommand
	Out chan RemoteCommand
	cli *clientv3.Client
}

type SessionManager struct {
	cli *clientv3.Client
}

type Session struct {
	Name  string
	Token string
}

var connect_pool *ConnectPool
var remote_channel *RemoteChannel

var session_manger *SessionManager

// 将消息广播给其他service. 因为做了负载均衡,一个client stream有可能落在不同节点,需要将行为广播给所有的节点
func ReadyBroadCast(from, message string) {
	remote_channel.Out &
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值