热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Linux服务器程序规范&高性能服务器程序框架

Linux服务器程序规范一般以后台进程的形式运行,又称守护进程。父进程为init通常有一套日志系统,至少能能输出到文件,大部分后台进程在varlog目录下有自己的日志目录一般使用一

Linux服务器程序规范

  1. 一般以后台进程的形式运行,又称守护进程。父进程为init

  2. 通常有一套日志系统,至少能能输出到文件,大部分后台进程在/var/log目录下有自己的日志目录

  3. 一般使用一个专门的非root账号运行

  4. 通常可配置,如果选项太多,可使用配置文件

  5. 通常会在启动时生成一个PID文件并存入/var/run目录中,以记录该进程后台PID。比如syslogd的PID文件为 /var/run/syslogd.pid

  6. 要考虑系统资源和限制,以预测自身能承受多大负荷,比如进程可用文件描述符总数和内存总量等


日志

linux使用一个守护进程处理系统日志——syslogd或升级版 rsyslogd

rsyslogd接收用户进程和内核的日志。用户进程使用syslog函数生成系统日志,该函数将日志输出到unix本地域docket类型的文件/dev/log中,rsyslogd监听该文件获取用户进程输出。内核日志在老系统上通过零一个rklogd管理,rsyslogd利用额外模块实现相同功能。内核日志通过printk打印至内核的环状内存(ring buffer),其直接映射到/proc/kmsg文件中。rsyslogd通过读取该文件获得内核日志。rsyslogd会将信息输出到特定日志文件:/var/log/debug /var/log/messages /var/log/kern.log。使用 /etc/rsyslog.conf 进行配置。

syslog函数指定日志级别打印日志

openlog设置日志格式

设置日志掩码,用于过滤日志

关闭日志功能:void closelog();


资源限制

extern int getrlimit (
__rlimit_resource_t __resource, // 资源类型
struct rlimit *__rlimits) __THROW;
struct rlimit
{
/* The current (soft) limit. */
rlim_t rlim_cur; // 软限制,建议不要超过
/* The hard limit. */
rlim_t rlim_max; // 硬限制,超过可能被系统终止
};
可以使用ulimit命令修改软硬限制,只有root用户可以增加


改变工作目录和根目录


服务程序后台化

bool daemonize() {
// 创建子进程,关闭父进程,这样使程序后台运行
pid_t pid = fork();
if (pid <0) {
return false;
} else if (pid > 0) {
exit 0;
}
// 设置文件掩码,当进程创建新文件时文件权限将为 mode & 0777
umask(0);
pid_t sid = setsid();
if (sid <0) {
return false;
}
if ((chdir("/")) <0) {
return false;
}
// 关闭 stdin stdout stderr
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 将标准流重定向到 /dev/null
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
return true;
}

linux提供了相同功能的库函数

extern int daemon (
int __nochdir, // 是否改变工作目录,传0时工作目录设置为 /
int __noclose // 为0时将stdio重定向到 /dev/null
) __THROW __wur;

高性能服务器程序框架

服务器程序通常要处理3类事件:io、信号、定时事件

两种高效事件处理模式 Reactor Proactor


Reactor 同步IO

主线程(IO处理单元)只负责监听fd是否有事件发生,有则通知工作线程(逻辑单元),主线程不做任何实质性工作。读写数据、接受新连接、处理客户请求都在工作线程内完成。

使用同步IO模型epoll实现:



  1. 主线程网epoll内核事件表种注册socket上读就绪事件

  2. 主线程调用epoll_wait 等等待 socket 上有数据可读

  3. 当 socket 上有数据可读,epoll_wait 通知主线程。主线程将 socket 可读事件放入请求队列

  4. 睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后向epoll事件表种注册该 socket 上的写事件

  5. 主线程调用 epoll_wait 等待 socket 可写

  6. 当socket可写,epoll_wait 通知主线程。主线程将socket可写事件放入请求队列

  7. 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果


Proactor 异步IO

所有io操作都交给主线程和内核处理,工作线程仅负责业务逻辑。

使用异步IO模型(以 aio_read 和 aio_write 为例):



  1. 主线程调用 aio_read 向内核注册 socket 的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序,比如信号

  2. 主线程继续处理其他逻辑

  3. 当socket上数据被读入用户缓冲区后,内核将向应用程序发送一个信号,通知应用程序可用

  4. 应用程序预定义好的信号处理函数选择一个工作线程处理客户请求。工作线程处理完客户请求后,调用aio_write向内核注册socket写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序以信号为例

  5. 主线程继续处理其他逻辑

  6. 当用户缓冲区数据被写入socket后,内核向应用发送一个信号,通知数据发送完毕

  7. 应用程序预先定义好的信号处理函数选择一个工作线程来善后处理,比如是否关闭socket

 


同步IO模拟Proactor模式

主线程执行数据读写操作,读写完成后,主线程向工作线程通知这一“完成事件”。那么从工作线程角度来看,他们直接获得数据读写操作结果,接下来只要对结果进行逻辑处理



  1. 向epoll内核注册socket读就绪事件

  2. 调用 epoll_wait 等待 socket 上有数据可读

  3. 当socket上有数据可读,epoll_wait通知主线程。主线程从socket循环读取数据,知道读完,然后将数据封装成一个对象出入请求队列

  4. 睡眠在请求队列上的某个工作线程被唤醒,获得请求对象并处理客户请求,然后向epoll内核事件表种注册socket上写就绪事件

  5. 主线程调用 epoll_wait 等待 socket 可写

  6. 当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求结果


并发模式

用于IO密集型程序,指IO处理单元和多个逻辑单元间协调完成任务的方法。服务器主要有两种并发编程模式:


1. 半同步/半异步模式

在io模型种“同步”“异步”区分的是内核向程序通知的是何种io事件(就绪事件还是完成事件),以及谁来完成io读写(应用还是内核)。并发模式中,“同步”指程序完全按照代码序列顺序执行,“异步”指程序的执行需要由系统事件驱动,如终端、信号等事件。

半同步半异步模型(half-sync/half-async)中,同步线程用于处理客户逻辑,异步线程用于处理io事件。异步线程监听到客户请求后将其封装成请求对象并插入请求队列。请求队列将通知某个工作在同步模式的工作线程读取并处理该请求对象。具体选哪个工作线程取决于请求队列设计。

半同步半反应堆模式(half-sync/half-reactive)是这种模式的一种变体,异步线程只有一个由主线程充当。它监听所有socket上事件。由可读事件时主线程得到新的连接socket并向epoll内核事件表中注册该socket读写事件;若连接socket上由读写事件主线程就将该连接socket插入请求队列。所有工作线程都睡眠在请求队列上,当有任务时,他们通过竞争(不如互斥锁)获得任务。

主线程将就绪的连接socket插入请求队列,这说明半同步/半反应堆模式采用Reactor模式:它要求工作线程自己从socket上读取客户请求和向socket写入应答,这就是“half-reactive”的含义。

缺点:



  1. 请求队列被共享。主线程和工作线程操作请求队列时都要加锁保护。

  2. 每个工作线程在同一时间只能处理一个客户请求。若客户数量多工作线程少,请求队列中将堆积多个任务对象,相应速度越来越慢。若通过增加工作线程,线程的切换也耗费大量cpu

在这个“高效的半同步/半异步模式”中,主线程只监听socket,连接socket由工作线程管理。当由新连接来时,主线程就接受并发给某个工作线程,此后该socket的所有io操作都有这个工作线程处理,知道关闭连接。派发socket最简单的方式就是向之间的管道写数据。工作线程从管道接收到新连接后把新socket的读写事件注册到自己的epoll内核事件表中。

主线程和所有工作线程都维持自己的事件循环,各自监听不同事件。所以这种模式中每个线程都工作在异步模式,并非严格的半同步/半异步模式。


2. 领导者/追随者模式

多个工作线程轮流获得事件源集合,轮流监听、分发处理事件的一种模式。任意时间点程序都只有一个leader,它监听io事件;其他为follower,休眠在线程池中等待成为新领导者。当leader检测到io事件,要先从线程池中选出新leader,然后处理io事件。新leader等待新io事件,原leader则处理io事件,二者并发。

包含组件:



  1. 句柄集 HandleSet

  2. 线程集 ThreadSet
    线程有3种状态 Leader Processing Follower
    leader推选新线程成领导者、追随者等待成为新领导者都会修改线程集,因此线程集提供一个成员 Synchronizert 同步这两个操作以避免竞态条件

  3. 事件处理器 EventHandler
    事件处理器包含一个或多个回调函数 handle_event,用于处理事件对应的业务逻辑。使用前要绑定到某个句柄上,当该句柄有事件时leader执行与之绑定的事件处理器中的回调函数。

  4. 具体的事件处理器 ConcreteEventHandler
    必须重写基类中的 handle_event 方法,以处理特定任务

领导者追随者模式中leader线程监听io事件并处理请求,因而无需传递任何数据或操作同步队列。但一个缺点是:仅支持一个事件源集合,因此无法让每个工作线程独立地管理多个客户连接。


提高服务器性能的方法




  1. 避免数据复制
    尽可能使用零拷贝,如 sendfile 共享内存

  2. 避免上下文切换
    线程切换会消耗大量cpu资源


 



推荐阅读
  • Eclipse 中 JSP 开发环境配置指南
    本文详细介绍了如何在 Eclipse 集成开发环境中配置 JSP 运行环境,包括必要的软件下载、Tomcat 服务器的配置以及常见问题的解决方法。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • 解决vCenter vSphere HA初始化失败的问题
    本文探讨了在集群中遇到的所有vSphere HA主机状态显示‘无法正确安装或配置vSphere HA代理’错误的情况,并详细介绍了排查与解决步骤,包括检查HA初始化错误及安装HA代理的常见故障排除方法。 ... [详细]
  • 本文详细介绍了在Linux环境下如何有效地管理任务,包括撤销操作、挂起与恢复任务、终止后台程序以及取消定时任务的方法。这些技巧对于提高日常工作效率和系统维护至关重要。 ... [详细]
  • 本文探讨了大型服务端开发过程中常见的几个误区,包括异步任务处理不当、日志同步模式使用、网络操作未设置超时、缓存命中率及响应时间未统计、单一缓存模式、分布式缓存加锁不当以及团队管理上的误区,旨在帮助开发者避免这些常见错误。 ... [详细]
  • 本文探讨了Web开发与游戏开发之间的主要区别,旨在帮助开发者更好地理解两种开发领域的特性和需求。文章基于作者的实际经验和网络资料整理而成。 ... [详细]
  • 在Linux系统上构建Web服务器的详细步骤
    本文详细介绍了如何在Linux系统上搭建Web服务器的过程,包括安装Apache、PHP和MySQL等关键组件,以及遇到的一些常见问题及其解决方案。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 应对.avast后缀勒索病毒:全面指南
    本文详细介绍了.avast后缀勒索病毒的特性、感染途径、恢复方法及预防措施,旨在帮助用户有效应对这一威胁。 ... [详细]
  • 本文详细介绍如何在 Windows 环境下安装 Ubuntu 12.04 版本的 Linux 操作系统,包括必要的软件下载、配置步骤以及注意事项。 ... [详细]
  • 随着技术社区的发展,越来越多的技术爱好者选择通过撰写博客来分享自己的学习经验和项目进展。本文将介绍一个具体案例,即将一套原本运行于Windows平台的代码成功移植到Linux(Redhat)环境下的过程与挑战。 ... [详细]
  • Ubuntu GamePack:专为游戏爱好者打造的Linux发行版
    随着Linux系统在游戏领域的应用越来越广泛,许多Linux用户开始寻求在自己的系统上畅玩游戏的方法。UALinux,一家致力于推广GNU/Linux使用的乌克兰公司,推出了基于Ubuntu 16.04的Ubuntu GamePack,旨在为Linux用户提供一个游戏友好型的操作环境。 ... [详细]
  • 使用LVS与ldirectord实现高可用负载均衡
    本文介绍了如何通过LVS(Linux Virtual Server)结合ldirectord工具来实现服务器的健康检查及负载均衡功能。环境设置包括一个LVS节点和两个真实服务器节点,通过配置ldirectord进行健康状态监测,确保系统的高可用性。 ... [详细]
  • 深入解析MySQL查询优化:特定类型查询的高级策略
    本文详细探讨了MySQL中特定类型查询的优化方法,包括COUNT()查询、关联查询、LIMIT分页及UNION查询的优化技巧,旨在提高数据库查询效率。 ... [详细]
  • Redis安全防护深入解析
    本文详细探讨了如何通过指令安全、端口管理和SSL代理等措施有效保护Redis服务的安全性。 ... [详细]
author-avatar
lantshirt
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有