百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

只需要90分钟,醍醐灌顶,读透Nginx源码

off999 2025-01-24 13:25 27 浏览 0 评论

前言

总结有福利

如何快速的把Nginx读明白,更加深入的了解Nginx,

有很多朋友就想我能用Nginx就可以了,搞那么明白干嘛,

学Nginx我们到底是先用,还是了解源码,

在这里我觉的当然还是先用起来,在去阅读源码,这是成正常的一种想法,

1、我肯定是先把Nginx跑起来明先会用,在深入。《用到什么是才算会用 到什么程度算深入才可以了解源码》如果在你不了解Nginx的情况下直接去用,我很负责人的告诉你,你阅读的会很痛苦真的,

什么时候算会用Nginx

(1)会编译安装

(2)能根据Nginx一些需求去配置使用《推荐大家一本书深入了解Nginx架构:模块开发》

Nginx该了解那些

(1)内存池,线程池,,共享内存,原子操作,日志处理,epoII,多进程模型,红黑树,《了解这些在去读Nginx源码就容易很多了》

先和大家讲一下线程池吧

线程池如何设计一下高性能服务器

a、cpu

b、内存

c、网卡

d、磁盘

线程池的优势:

(1)、创建的进程或者线程是有限的,服务器的系统代价比较小,一般不会到达系统限制的值。
(2)、服务器不需要频繁的创建、销毁进程或者线程,只在服务器启动时创建,结束时销毁。
(3)、创建的进程或者线程不是为一个客户端服务,可以串行为多个客户端服务。
(4)、客户端连接上以后,不需要再去创建进程或者线程,只需要分配进程池或者线程池中的进程或线程,减少了客户端等待的时间。

线程池的逻辑(伪代码)

int listenfd = socket();
int res = bind();
listen();


//创建线程池中所有的线程
while(1)
{
int c = accept();
//在线程池分配一个线程为C服务
}

  1. 在代码实现时需要注意的问题:
    (1)、线程如何阻塞在线程池中 ;
    信号量P操作
    (2)、如何分配一个线程为客户端服务;
    主线程接受一个客户端连接执行V操作
    (3)、主线程如何传递客户端连接的文件描述符;
    全局维护一个数组或者链表
    (4)、
    插入数据和获取数据时,每个线程都是互斥的,必须加锁。
  2. 具体代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <semaphore.h>

#define THREADNUM  3
#define CLIENTNUM  10
sem_t sem;
pthread_mutex_t  mutex;

int ClientFds[CLIENTNUM];

void Init_ClientFds()
{
	int i = 0;
	for(; i < CLIENTNUM; ++i)
	{
		ClientFds[i] = -1;
	}
}

void Insert_Client(int fd)
{
	pthread_mutex_lock(&mutex);

	int i = 0;
	for(; i < CLIENTNUM; ++i)
	{
		if(ClientFds[i] == -1)
		{
			ClientFds[i] = fd;
			break;
		}
	}

	pthread_mutex_unlock(&mutex);
}

int Get_Client()
{
	pthread_mutex_lock(&mutex);

	int i = 0, c = -1;
	for(; i < CLIENTNUM; ++i)
	{
		if(ClientFds[i] != -1)
		{
			c = ClientFds[i];
			ClientFds[i] = -1;
			break;
		}
	}

	pthread_mutex_unlock(&mutex);

	return c;
}

void * DealClient(void * arg)
{
	while(1)
	{
		sem_wait(&sem);
		int  c = Get_Client();
		while(1)
		{
			char data[128] = {0};
			int n = recv(c, data, 127, 0);
			if(n <= 0)
			{
				close(c);
				break;
			}

			printf("%s\n", data);

			send(c, "OK", 2, 0);
		}
	}
}

int main()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(listenfd != -1);

	struct sockaddr_in ser;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	listen(listenfd, 5);

	sem_init(&sem, 0, 0);
	pthread_mutex_init(&mutex, NULL);
	Init_ClientFds();

	//  创建线程池
	int i = 0;
	for(; i < THREADNUM; ++i)
	{
		pthread_t id;
		int res = pthread_create(&id, NULL, DealClient, NULL);
		assert(res  == 0);
	}

	while(1)
	{
		struct sockaddr_in cli;
		int len = sizeof(cli);

		int c = accept(listenfd, (struct sockaddr*)&cli, &len);
		assert(c != -1);

		Insert_Client(c);  //  将c传递给函数线程
		sem_post(&sem);   //  V操作
	}
}

代码优化:
我们为了保存文件描述符维护了一个数组,在代码中插入获取时都是从[0]遍历获取,加入56789,再取出56,此时又插入了10,那么下一次就会对10先进行操作,使后面的fd等待太久,因此对于代码中的GetClientFds()做了优化。


int GetClientFds() //只需将每次获取时整体向前挪一个单元格 { pthread_mutex_lock(&mutex); int i = 0; int c = ClientFds[0]; for(; i < CLIENTNUM-1 || ClientFds[i] != -1 ; i++) { ClientFds[i] = ClientFds[i +1]; } ClientFds[i] = -1; pthread_mutex_unlock(&mutex); return c; }
  1. 维护文件描述符数组的方式:

第①种方式:

优点:只要数组中有fd就能立即处理
缺点:当fd插入不是很频繁的时候,会出现一直只调用第一个函数线程的情况,其他的函数线程一直未使用,造成资源的浪费。

第②种方式:

优点:只要分配合理,每一条线程都能得到利用
缺点:当其中的一个子线程fd处理比较麻烦时,后面分配给他的fd会滞留在数组中得不到及时的处理
相对比①②来说,②的处理方法更合理一些,这种方法可以做到负载均衡

  1. 总结:
    进程池和线程池相比于多进程多线程有所改善,但当一个线程为一个客户端服务时,只能等客户端退出,才能服务下一个客户端,有可能出现客户端占着资源不使用,阻塞在recv函数的情况,对线程也是一种浪费,进程池同理,这种情况是之前提到服务器的通病,在select,poll,epoll,这三种方法建立的服务器会将这个问题解决。

一起学习的可以后台私信“资料”送学习视频需要C/C++ Linux服务器架构师学习资料后台私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等等。。。),免费分享

相关推荐

photoshop6序列号(photoshop8.01序列号)
  • photoshop6序列号(photoshop8.01序列号)
  • photoshop6序列号(photoshop8.01序列号)
  • photoshop6序列号(photoshop8.01序列号)
  • photoshop6序列号(photoshop8.01序列号)
win10下载应用商店(win10应用商店打不开)

1、点击Win10系统的开始菜单,然后在点击应用商店;2、打开Win10应用商店后,在搜索框里输入想要搜索的应用软件,然后点击检索;3、点击搜索到的应用,点击安装;4、点击安装后,系统会提示要切换到这...

dell电脑重装系统win10(dell 重装win10系统)

戴尔笔记本重装系统win10的步骤如下:制作好wepe启动盘之后,将win10系统iso镜像直接复制到U盘。在需要重装系统的戴尔电脑上插入pe启动盘,重启后不停按F12启动快捷键,调出启动菜单对话框,...

android升级包下载安装(android 升级包)

打开手机系统更新升级,前提是官方有新系统推送才能更新  哪个大不一定,但一般规律如下:  1、小版本的更新,通常越更新越大。比如3.1更新到3.2,通常是修复bug,代码量通常会增大,体积就会增大。 ...

hdd硬盘和ssd(ssd硬盘和hdd硬盘是什么意思)

HDD硬盘和SSD硬盘是两种不同类型的电脑存储设备,它们有着以下区别:1.工作原理:HDD硬盘使用机械旋转的磁盘和读写磁头来存储和读取数据,而SSD硬盘则使用闪存存储数据,类似于USB闪存盘。2....

电脑免费软件下载大全(电脑上免费的下载软件)

正常情况下,如果我们想要在自己的电脑上面下载一个不要钱的单机游戏,那么我们是可以直接在我们的软件管理中心进行一个下载的,这个时候我们只需要通过一个权限就能够正常的下载,当然我们也是可以在一些小游戏的软...

mpp文件转换excel(mpp转换成pdf)

要将Excel表格转换为MPP格式,您可以按照以下步骤操作:1.打开Excel表格并确保数据按照项目的不同阶段或任务进行组织。2.将Excel表格中的数据复制到一个新的MicrosoftProj...

win7旗舰版开机密码忘记按f2

方法如下:开始-控制面板-用户帐户;在打开的更改用户帐户界面点击要更改的帐户;然后点击帐户左面的更改密码按钮;在打开的页面上,输入一次当前使用的密码,输入2次要更改的新密码然后保存退出就可以了...

笔记本无音频输出设备(笔记本无音频输出设备)

1、没有声卡驱动,解决方法就是找到笔记本的官网,下载电脑声卡的驱动安装即可。2、没有外界的音频播放设备,解决方法就是买一个外界的音频播放设备插到电脑主机的音频接口上即可。笔记本电脑显示未安装任何音频输...

iso文件能用手机打开吗(iso文件能用手机打开吗安全吗)

一般的压缩软件就可以打开的,比如,好压软件,这个打开只是解压形式的,如果你说的是运行iso文件,这个没有,况且安卓系统也不支持iso运行ISO文件一般用于光盘镜像文件的存储,如果想要在手机上运行ISO...

win7系统卡顿怎么优化(win7很慢很卡怎么优化)

1、首先打开安全卫士,进入安全卫士首页,单击软件窗口右下角的“更多”图标,打开扩展应用程序。2、单击选择“我的工具”。3、在我的工具菜单里面找到“人工服务”单击打开人工服务。4、在人工服务对话框有很多...

如何查看c盘微信聊天记录(如何查看c盘微信聊天记录内存大小)

微信群中的消息只要没删除基本都能保存,想要找微信群中几个多月前的消息可以直接根据日期来查找聊天记录。操作如下:1、打开想要查找记录的微信群,点击右上角人形图标;2、点击查找聊天内容;3、选择按日...

office2016家庭版激活密钥(office家庭版激活码2019)

走淘宝吧,因为零售版的密钥只能用一次。大概几块钱就能激活2016。如果你不在乎钱的话可以向我一样,订阅一个office365.实在不行可以和几个人一起买一个家庭版的365.出现这个情况,找微软申诉是没...

移动硬盘驱动器下载安装(移动硬盘驱动器下载安装教程)

1、右键单击您的桌面,选择“新建文件夹”,并命名该文件夹(例如“usb驱动程序”);2、然后到本站下载驱动程序;3、将其解压缩至在您的桌面上刚刚创建的usb驱动程序文件夹;4、单击开始菜单,然后选择设...

电脑硬盘格式化工具(电脑 格式化硬盘)

硬盘格式化工具很多,PQMACGIG8.0(中文就叫硬盘分区魔法师)是比较好的一个,这个是在WINDOWS下比叫好用,(个人感觉)FDISK也是比较好的一个,这个一般用在DOS下分区格式化WIN...

取消回复欢迎 发表评论: