记C++集群服务器项目消息中间件编程大家遇到的两个Bug

bug 1. 向消息中间件PUBLISH发布消息不成功,导致无法跨服务器通信
bug 2. 多次向中间件SUBSCRIBE订阅消息,出现无响应,客户端也无响应

问题背景

以redis的发布-订阅机制作为集群服务器的消息中间件,解耦服务器之间的通信。用hiredis做客户端编程。

Bug1

两个客户端C1和C2分别登录在两台服务器S1和S2上,服务器通过redis作为消息中间件进行消息通信。当客户端C1和C2通信时,C1先发送消息到S1上,S1向redis上名字为C2的channel通道发布消息,S2服务器从C2的channel上接收消息后,再把消息转发给C2客户端,注意hiredis提供的发送redis命令行接口函数redisCommand,在发送发布-订阅命令时,需要在不同的上下文Context环境中进行,不能够在同一Context下进行。

// 下面redisCommand函数的第一个参数this->_publishContext需要使用一个单独的Context,不能
// 和subscribe使用一样的Context
redisReply *reply = (redisReply*)redisCommand(this->_publishContext, "PUBLISH %d %s", channel, message.c_str());

Bug2

客户端偶尔出现登录以后没有响应,造成业务不能继续进行,此时直接调试出现问题的服务器Server.out

查看出现问题的服务器的pid

tony@tony-virtual-machine:~$ ps -ef | grep Server
tony       5904   1529  0 11:08 pts/1    00:00:00 ./Server.out 127.0.0.1 9000
tony       5909   1442  0 11:08 pts/0    00:00:00 ./Server.out 127.0.0.1 9002
tony       6004   5991  0 11:13 pts/4    00:00:00 grep --color=auto Server

从上面看到,出现问题的Server的PID是5909

gdb调试5909号进程

在这里插入图片描述

打印当前进程所有的线程信息

在这里插入图片描述
用info threads可以输出当前进程所有线程的信息,可以看到Server.out是主线程,也就是muduo库的I/O线程,现在处理epoll_wait状态,等待新用户的连接;而EventLoop事件循环有三个线程,分别是ChatServer0、ChatServer1、ChatServer2,其中ChatServer1和ChatServer2处在epoll_wait状态,等待已连接用户的读写事件,但是ChatServer0却阻塞在__libc_recv函数处,不能继续处理逻辑业务,不能给客户端回复响应,导致客户端无应答。

调试线程ChatServer0

线程ChatServer0的thread id是2,如下:
在这里插入图片描述
从上面的ChatServer0线程的调用堆栈打印信息(bt命令)可以看到,这个线程在处理客户端的登录请求时,需要向redis中间件消息队列订阅消息,是通过hiredis的redisCommand发送的subscribe订阅命令,但是通过调用堆栈信息查看,redisCommand不仅可以发送subscribe订阅命令到redis server,还会以阻塞的方式等待redis server的响应。

但实际上,项目代码上已经开启了一个独立的线程,专门做redisGetReply,如下:
在这里插入图片描述
在这里插入图片描述
由于线程池里面的redisGetReply抢了上面订阅subscribe的redisCommand底层调用的redisGetReply的响应消息,导致ChatServer0线程阻塞在这个接口调用上,无法再次回到epoll_wait处了,这个线程就废掉了,如果工作线程全部发生这种情况,最终服务器所有的工作线程就全部停止工作了!

解决方案

从hiredis的redisCommand源码上可以看出,它实际上相当于调用了这三个函数:

  1. redisAppendCommand 把命令写入本地发送缓冲区
  2. redisBufferWrite 把本地缓冲区的命令通过网络发送出去
  3. redisGetReply 阻塞等待redis server响应消息

既然在muduo库的ThreadPool中单独开辟了一个线程池,接收this->_context上下文的响应消息,因此subcribe订阅消息只做消息发送,不做消息接收就可以了,如下:

// 订阅通道
void subscribe(int channel)
{
	// 只负责发送命令,不阻塞接收redis server响应消息,否则和notifyMsg线程抢占响应资源
	if (REDIS_ERR == redisAppendCommand(this->_context, "SUBSCRIBE %d", channel))
	{
		LOG_ERROR << "subscribe [" << channel << "] error!";
		return;
	}
	// redisBufferWrite可以循环发送缓冲区,直到缓冲区数据发送完毕(done被置为1)
	int done = 0;
	while (!done) 
	{
		if (REDIS_ERR == redisBufferWrite(this->_context, &done))
		{
			LOG_ERROR << "subscribe [" << channel << "] error!";
			return;
		}
	}
	LOG_INFO << "subscribe [" << channel << "] success!";
}

欢迎微信扫码关注

  • 15
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据 C++基于控制台的集群聊天服务器:配置nginx负载均衡模块分发客户端连接、基于发布-订阅的redis作为服务器消息队列,mysql数据库存储用户数据
C集群聊天服务器视频下载是指使用C语言进行开发的集群聊天服务器程序,该程序具备视频下载功能。集群聊天服务器是一种用于群组通信的服务器程序,可以实现多个用户之间的文本、语音、视频等多种形式的交流。 在C语言中开发集群聊天服务器视频下载功能需要使用网络编程相关的API,如Socket编程。通过Socket编程,可以在服务器端建立一个监听端口,等待客户端的连接请求。一旦客户端连接成功,服务器可以和客户端进行双向的数据传输。 为了支持视频下载功能,服务器端需要提供相应的视频资源,并通过Socket将视频内容传输给客户端。客户端在接收到视频数据后,可以将数据保存成视频文件。视频文件可以通过常见的视频播放器进行播放。 在集群聊天服务器中,可以通过添加一些额外的功能来实现视频下载功能的完善。例如,可以提供视频分类和搜索功能,让用户可以更方便地找到自己需要的视频资源。还可以加入断点续传的功能,当下载过程中出现网络中断或其他异常情况时,可以在恢复连接后继续下载,避免重新下载整个视频文件。 总结起来,C集群聊天服务器视频下载是一种基于C语言开发的服务器程序,它可以实现多人聊天、视频传输和视频下载等功能。通过Socket编程服务器和客户端之间可以进行双向的数据传输,从而实现视频数据的传输和保存。同时,可以通过添加一些额外的功能来提高视频下载的体验,如视频分类、搜索和断点续传等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值