当前位置: 首页 > news >正文

网站图标ico 设置网上营销怎么做

网站图标ico 设置,网上营销怎么做,简洁的网站地图模板,国家知识产权商标网官方查询C10K和C10M 计算机领域的很多技术都是需求推动的#xff0c;上世纪90年代#xff0c;由于互联网的飞速发展#xff0c;网络服务器无法支撑快速增长的用户规模。1999年#xff0c;Dan Kegel提出了著名的C10问题#xff1a;一台服务器上同时处理10000个客户网络连接。10000…C10K和C10M 计算机领域的很多技术都是需求推动的上世纪90年代由于互联网的飞速发展网络服务器无法支撑快速增长的用户规模。1999年Dan Kegel提出了著名的C10问题一台服务器上同时处理10000个客户网络连接。10000个网络连接并不会发送请求到服务器有些连接并不活跃同一时刻只有极少的部分连接发送请求。不同的服务类型每个连接发送请求的频率也不相同游戏服务器的连接会频繁的发送请求而Web服务器的连接发送请求的频率就低很多。无论如何根据经验法则对于特定的服务类型连接越多同一时刻发送请求的连接也越多。 时至今日C10K问题当然早已解决不仅如此一台机器能支撑的连接越来越多后来提出了C10M问题在一台机器上支撑1000万的连接2015年MigratoryData在单机承载12M的连接解决了C10M问题。 本文先回顾C10问题的解决方案再探讨如何构建支撑C10M的应用程序聊聊其中涉及的各种技术。 C10K问题的解决 时间退回到1999年当时要实现一个网络服务器大概有这样几种模式 简单进程/线程模型 这是一种非常简单的模式服务器启动后监听端口阻塞在accept上当新网络连接建立后accept返回新连接服务器启动一个新的进程/线程专门负责这个连接。从性能和伸缩性来说这种模式是非常糟糕的原因在于 进程/线程创建和销毁的时间操作系统创建一个进程/线程显然需要时间在一个繁忙的服务器上如果每秒都有大量的连接建立和断开采用每个进程/线程处理一个客户连接的模式每个新连接都要创建创建一个进程/线程当连接断开时销毁对应的线程/进程。创建和销毁进程/线程的操作消耗了大量的CPU资源。使用进程池和线程池可以缓解这个问题。内存占用。主要包含两方面一个是内核数据结构所占用的内存空间另外一个是Stack所占用的内存。有些应用的调用栈很深比如Java应用经常能看到几十上百层的调用栈。上下文切换的开销。上下文切换时操作系统的调度器中断当前线程选择另外一个可运行的线程在CPU上继续运行。调度器需要保存当前线程的现场信息然后选择一个可运行的线程再将新线程的状态恢复到寄存器中。保存和恢复现场所需要的时间和CPU型号有关选择一个可运行的线程则完全是软件操作Linux 2.6才开始使用常量时间的调度算法。 以上是上下文切换的直接开销。除此之外还有一些间接开销上下文切换导致相关的缓存失效比如L1/L2 CacheTLB等这些也会影响程序的性能但是间接开销很难衡量。 有意思的是这种模式虽然性能极差但却依然是我们今天最常见到的模式很多Web程序都是这样的方式在运行。 select/poll 另外一种方式是使用select/poll在一个线程内处理多个客户连接。select和poll能够监控多个socket文件描述符当某个文件描述符就绪select/soll从阻塞状态返回通知应用程序可以处理用户连接了。使用这种方式我们只需要一个线程就可以处理大量的连接避免了多进程/线程的开销。之所以把select和poll放在一起说原因在于两者非常相似性能上基本没有区别唯一的区别在于poll突破了select 1024个文件描述符的限制然而当文件描述符数量增加时poll性能急剧下降因此所谓突破1024个文件描述符实际上毫无意义。select/poll并不完美依然存在很多问题 每次调用select/poll都要把文件描述符的集合从用户地址空间复制到内核地址空间select/poll返回后调用方必须遍历所有的文件描述符逐一判断文件描述符是否可读/可写。 这两个限制让select/poll完全失去了伸缩性。连接数越多文件描述符就越多文件描述符越多每次调用select/poll所带来的用户空间到内核空间的复制开销越大。最严重的是当报文达到select/poll返回之后必须遍历所有的文件描述符。假设现在有1万个连接其中只一个连接发送了请求但是select/poll就要把1万个连接全部检查一遍。 epoll FreeBSD 4.1引入了kqueue此时是2000年7月而在Linux上还要等待2年后的2002年才开始引入kqueue的类似实现: epoll。epoll最初于 2.5.44进入Linux kernel mainline此时已经是2002年距离C10K问题提出已经过了3年。 epoll是如何提供一个高性能可伸缩的IO多路复用机制呢首先epoll引入了epoll instance这个概念epoll instance在内核中关联了一组要监听的文件描述符配置interest list这样的好处在于每次要增加一个要监听的文件描述符不需要把所有的文件描述符都配置一次然后从用户地址空间复制到内核地址空间只需要把单个文件描述符复制到内核地址空间复制开销从O(n)降到了O(1)。 注册完文件描述符后调用epoll_wait开始等待文件描述符事件。epoll_wait可以只返回已经ready的文件描述符因此在epoll_wait返回之后程序只需要处理真正需要处理的文件描述符而不用把所有的文件描述符全部遍历一遍。假设在全部N个文件描述符中只有一个文件描述符Readyselect/poll要执行N次循环epoll只需要一次。 epoll出现之后Linux上才真正有了一个可伸缩的IO多路复用机制。基于epoll能够支撑的网络连接数取决于硬件资源的配置而不再受限于内核的实现机制。CPU越强内存越大能支撑的连接数越多。 编程模型 Reactor和proactor 不同的操作系统上提供了不同的IO多路复用实现Linux上有epollFreeBSD有kqueueWindows有IOCP。对于需要跨平台的程序必然需要一个抽象层提供一个统一的IO多路复用接口屏蔽各个系统接口的差异性。 Reactor是实现这个目标的一次尝试最早出现在Douglas C. Schmidt的论文The Reactor An Object-Oriented Wrapper for Event-Driven Port Monitoring and Service Demultiplexing中。从论文的名字可以看出Reactor是poll这种编程模式的一个面向对象包装。考虑到论文的时间当时正是面向对象概念正火热的时候什么东西都要蹭蹭面向对象的热度。论文中DC Schmidt描述了为什么要做这样的一个Wrapper给出了下面几个原因 操作系统提供的接口太复杂容易出错。select和poll都是通用接口因为通用增加了学习和正确使用的复杂度。接口抽象层次太低涉及太多底层的细节。不能跨平台移植。难以扩展。 实际上除了第三条跨平台其他几个理由实在难以站得住脚。select/poll这类接口复杂吗使用起来容易出错吗写出来的程序难以扩展吗不过不这么说怎么体现Reactor的价值呢。正如论文名称所说的Reactor本质是对操作系统IO多路复用机制的一个面向对象包装为了证明Reactor的价值DC Schmidt还用C面向对象的特性实现了一个编程框架ACE实际上使用ACE比直接使用poll或者epoll复杂多了。 后来DC Schmidt写了一本书《面向模式的软件架构》再次提到了Reactor并重新命名为Reactor Pattern现在网络上能找到的Reactor资料基本上都是基于Reactor Pattern而不是早期的面向Object-Orientend Wrapper。 《面向模式的软件》架构中还提到了另外一种叫做Proactor的模式和Reactor非常类似Reactor针对同步IOProactor则针对异步IO。 CallbackFuture和纤程 Reactor看上去并不复杂但是想编写一个完整的应用程序时候就会发现其实没那么简单。为了避免Reactor主逻辑阻塞所有可能会导致阻塞的操作必须注册到epoll上带来的问题就是处理逻辑的支离破碎大量使用callback产生的代码复杂难懂。如果应用程序中还有非网络IO的阻塞操作问题更严重比如在程序中读写文件。Linux中文件系统操作都是阻塞的虽然也有Linux AIO但是一直不够成熟难堪大用。很多软件采用线程池来解决这个问题不能通过epoll解决的阻塞操作扔到一个线程池执行。这又产生了多线程内存开销和上下文切换的问题。 Future机制是对Callback的简单优化本质上还是Callback但是提供了一致的接口代码相对来说简单一些不过在实际使用中还是比较复杂的。Seastar是一个非常彻底的future风格的框架从它的代码可以看到这种编程风格真的非常复杂阻塞式编程中一个函数几行代码就能搞定的事情在Seastar里需要上百行代码几十个labmda (在Seastar里叫做continuation)。 纤程是一种用户态调度的线程比如Go语言中的goroutine有些人可能会把这种机制成为coroutine不过我认为coroutine和纤程还是有很大区别的coroutine是泛化的子进程具有多个进入和退出点用来一些一些相互协作的程序典型的例子就是Python中的generator。纤程则是一种运行和调度机制。 纤程真正做到了高性能和易用在Go语言中使用goroutine实现的高性能服务器是一件轻松愉快的事情完全不用考虑线程数、epoll、回调之类的复杂操作和编写阻塞式程序完全一样。 网络优化 Kernel bypass 网络子系统是Linux内核中一个非常庞大的组件提供了各种通用的网络能力。通用通常意味在在某些场景下并不是最佳选择。实际上业界的共识是Linux内核网络不支持超大并发的网络能力。根据我过去的经验Linux最大只能处理1MPPS而现在的10Gbps网卡通常可以处理10MPPS。随着更高性能的25Gbps40Gbps网卡出现Linux内核网络能力越发捉襟见肘。 为什么Linux不能充分发挥网卡的处理能力原因在于 大多数网卡收发使用中断方式每次中断处理时间大约100us另外要考虑cache miss带来的开销。部分网卡使用NAPI轮询中断结合的方式处理报文当报文放进队列之后依然要触发软中断。数据从内核地址空间复制到用户地址空间。收发包都有系统调用。网卡到应用进程的链路太长包含了很多不必要的操作。 Linux高性能网络一个方向就是绕过内核的网络栈(kernel bypass)业界有不少尝试 PF_RING 高效的数据包捕获技术比libpcap性能更好。需要自己安装内核模块启用ZC Driver设置transparent_mode2的情况下报文直接投递到客户端程序绕过内核网络栈。Snabbswitch 一个Lua写的网络框架。完全接管网卡使用UIO(Userspace IO)技术在用户态实现了网卡驱动。Intel DPDK直接在用户态处理报文。非常成熟性能强大限制是只能用在Intel的网卡上。根据DPDK的数据3GHz的CPU Core上平均每个报文的处理时间只要60ns一次内存的访问时间。Netmap 一个高性能收发原始数据包的框架包含了内核模块以及用户态库函数需要网卡驱动程序配合因此目前只支持特定的几种网卡类型用户也可以自己修改网卡驱动。XDP使用Linux eBPF机制将报文处理逻辑下放到网卡驱动程序中。一般用于报文过滤、转发的场景。 kernel bypass技术最大的问题在于不支持POSIX接口用户没办法不修改代码直接移植到一种kernel bypass技术上。对于大多数程序来说还要要运行在标准的内核网络栈上通过调整内核参数提升网络性能。 网卡多队列 报文到达网卡之后在一个CPU上触发中断CPU执行网卡驱动程序从网卡硬件缓冲区读取报文内容解析后放到CPU接收队列上。这里所有的操作都在一个特定的CPU上完成高性能场景下单个CPU处理不了所有的报文。对于支持多队列的网卡报文可以分散到多个队列上每个队列对应一个CPU处理解决了单个CPU处理瓶颈。 为了充分发挥多队列网卡的价值我们还得做一些额外的设置把每个队列的中断号绑定到特定CPU上。这样做的目的一方面确保网卡中断的负载能分配到不同的CPU上另外一方面可以将负责网卡中断的CPU和负责应用程序的CPU区分开避免相互干扰。 在Linux中/sys/class/net/${interface}/device/msi_irqs下保存了每个队列的中断号有了中断号之后我们就可以设置中断和CPU的对应关系了。网上有很多文章可以参考。 网卡Offloading 回忆下TCP数据的发送过程应用程序将数据写到套接字缓冲区内核将缓冲区数据切分成不大于MSS的片段附加上TCP Header和IP Header计算Checksum然后将数据推到网卡发送队列。这个过程中需要CPU全程参与 随着网卡的速度越来越快CPU逐渐成为瓶颈CPU处理数据的速度已经赶不上网卡发送数据的速度。经验法则发送或者接收1bit/s TCP数据需要1Hz的CPU1Gbps需要1GHz的CPU10Gbps需要10GHz的CPU已经远超单核CPU的能力即使能完全使用多核假设单个CPU Core是2.5GHz依然需要4个CPU Core。 为了优化性能现代网卡都在硬件层面集成了TCP分段、添加IP Header、计算Checksum等功能这些操作不再需要CPU参与。这个功能叫做tcp segment offloading简称tso。使用ethtool -k 可以检查网卡是否开启了tso 除了tso还有其他几种offloading比如支持udp分片的ufo不依赖驱动的gso优化接收链路的lro 充分利用多核 随着摩尔定律失效CPU已经从追求高主频转向追求更多的核数现在的服务器大都是96核甚至更高。构建一个支撑C10M的应用程序必须充分利用所有的CPU最重要的是程序要具备水平伸缩的能力随着CPU数量的增多程序能够支撑更多的连接。 很多人都有一个误解认为程序里使用了多线程就能利用多核考虑下CPython程序你可以创建多个线程但是由于GIL的存在程序最多只能使用单个CPU。实际上多线程和并行本身就是不同的概念多线程表示程序内部多个任务并发执行每个线程内的任务可以完全不一样线程数和CPU核数没有直接关系单核机器上可以跑几百个线程。并行则是为了充分利用计算资源将一个大的任务拆解成小规模的任务分配到每个CPU上运行。并行可以 通过多线程实现系统上有几个CPU就启动几个线程每个线程完成一部分任务。 并行编程的难点在于如何正确处理共享资源。并发访问共享资源最简单的方式就加锁然而使用锁又带来性能问题获取锁和释放锁本身有性能开销锁保护的临界区代码不能只能顺序执行就像CPython的GIL没能充分利用CPU。 Thread Local和Per-CPU变量 这两种方式的思路是一样的都是创建变量的多个副本使用变量时只访问本地副本因此不需要任何同步。现代编程语言基本上都支持Thread Local使用起来也很简单C/C里也可以使用__thread标记声明ThreadLocal变量。 Per-CPU则依赖操作系统当我们提到Per-CPU的时候通常是指Linux的Per-CPU机制。Linux内核代码中大量使用Per-CPU变量但应用代码中并不常见如果应用程序中工作线程数等于CPU数量且每个线程Pin到一个CPU上此时才可以使用。 原子变量 如果共享资源是int之类的简单类型访问模式也比较简单此时可以使用原子变量。相比使用锁原子变量性能更好。在竞争不激烈的情况下原子变量的操作性能基本上和加锁的性能一致但是在并发比较激烈的时候等待锁的线程要进入等待队列等待重新调度这里的挂起和重新调度过程需要上下文切换浪费了更多的时间。 大部分编程语言都提供了基本变量对应的原子类型一般提供set, get, compareAndSet等操作。 lock-free lock-free这个概念来自 An algorithm is called non‐blocking if failure or suspension of any thread cannot cause failure or suspension of another thread; an algorithm is called lock‐free if, at each step, some thread can make progress. non-blocking算法任何线程失败或者挂起不会导致其他线程失败或者挂起lock-free则进一步保证线程间无依赖。这个表述比较抽象具体来说non-blocking要求不存在互斥存在互斥的情况下线程必须先获取锁再进入临界区如果当前持有锁的线程被挂起等待锁的线程必然需要一直等待下去。对于活锁或者饥饿的场景线程失败或者挂起的时候其他线程完全不仅能正常运行说不定还解决了活锁和饥饿的问题因此活锁和饥饿符合non-blocking但是不符合lock-free。 实现一个lock-free数据结构并不容易好在已经有了几种常见数据结构的的lock-free实现buffer, list, stack, queue, map, deque我们直接拿来使用就行了。 优化对锁的使用 有时候没有条件使用lock-free还是得用锁对于这种情况还是有一些优化手段的。首先使用尽量减少临界区的大小使用细粒度的锁锁粒度越细并行执行的效果越好。其次选择适合的锁比如考虑选择读写锁。 CPU affinity 使用CPU affinity机制合理规划线程和CPU的绑定关系。前面提到使用CPU affinity机制将多队列网卡的中断处理分散到多个CPU上。不仅是中断处理线程也可以绑定绑定之后线程只会运行在绑定的CPU上。为什么要将线程绑定到CPU上呢绑定CPU有这样几个好处 为线程保留CPU确保线程有足够的资源运行提高CPU cache的命中率某些对cache敏感的线程必须绑定到CPU上才行。更精细的资源控制。可以预先需要静态划分各个工作线程的资源例如为每个请求处理线程分配一个CPU其他后台线程共享一个CPU工作线程和中断处理程序工作在不同的CPU上。NUMA架构中每个CPU有自己的内存控制器和内存插槽CPU访问本地内存别访问远程内存快3倍左右。使用affinity将线程绑定在CPU上相关的数据也分配到CPU对应的本地内存上。 Linux上设置CPU affinity很简单可以使用命令行工具taskset也可以在程序内直接调用API sched_getaffinity和sched_setaffinity 其他优化技术 使用Hugepage Linux中程序内使用的内存地址是虚拟地址并不是内存的物理地址。为了简化虚拟地址到物理地址的映射虚拟地址到物理地址的映射最小单位是“Page”默认情况下每个页大小为4KB。CPU指令中出现的虚拟地址为了读取内存中的数据指令执行前要把虚拟地址转换成内存物理地址。Linux为每个进程维护了一张虚拟地址到物理地址的映射表CPU先查表找到虚拟地址对应的物理地址再执行指令。由于映射表维护在内存中CPU查表就要访问内存。相对CPU的速度来说内存其实是相当慢的一般来说CPU L1 Cache的访问速度在1ns左右而一次内存访问需要60-100ns比CPU执行一条指令要慢得多。如果每个指令都要访问内存比如严重拖慢CPU速度为了解决这个问题CPU引入了TLB(translation lookaside buffer)一个高性能缓存缓存映射表中一部分条目。转换地址时先从TLB查找没找到再读内存。 显然最理想的情况是映射表能够完全缓存到TLB中地址转换完全不需要访问内存。为了减少映射表大小我们可以使用“HugePages”大于4KB的内存页。默认HugePages是2MB最大可以到1GB。 避免动态分配内存 内存分配是个复杂且耗时的操作涉及空闲内存管理、分配策略的权衡分配效率碎片尤其是在并发环境中还要保证内存分配的线程安全。如果内存分配成为了应用瓶颈可以尝试一些优化策略。比如内存复用i不要重复分配内存而是复用已经分配过的内存在C/Java里则考虑复用已有对象这个技巧在Java里尤其重要不仅能降低对象创建的开销还避免了大量创建对象导致的GC开销。另外一个技巧是预先分配内存实际上相当于在应用内实现了一套简单的内存管理比如Memcached的Slab。 Zero Copy 对于一个Web服务器来说响应一个静态文件请求需要先将文件从磁盘读取到内存中再发送到客户端。如果自信分析这个过程会发现数据首先从磁盘读取到内核的页缓冲区再从页缓冲区复制到Web服务器缓冲区接着从Web服务器缓冲区发送到TCP发送缓冲区最后经网卡发送出去。这个过程中数据先从内核复制到进程内再从进程内回到内核这两次复制完全是多余的。Zero Copy就是类似情况的优化方案数据直接在内核中完成处理不需要额外的复制。 Linux中提供了几种ZeroCopy相关的技术包括sendfile,splice,copy_file_range,Web服务器中经常使用sendfile优化性能。 最后 千万牢记不要过早优化。 优化之前先考虑两个问题 现在的性能是否已经满足需求了如果真的要优化是不是已经定位了瓶颈 在回答清楚这两个问题之前不要盲目动手。 原文链接 本文为云栖社区原创内容未经允许不得转载。
http://www.huolong8.cn/news/20422/

相关文章:

  • wordpress用外部图片浙江企业seo推广
  • c2c网站有哪些网站建设与开发学什么内容呢
  • 佛山网站建设网络推广做关于植物的网站
  • 网站对于企业的作用公司网站建设多少费用哪儿济南兴田德润联系电话
  • 吉林新农村建设网站附近装修公司联系方式
  • 网站建设学习网公司有哪些住房和城乡建设部网站证书查询
  • 上海网站营销网络设计课程培训
  • 高端大气的网站制作申请域名建立网站
  • 建设与管理局网站设置网站的默认页面
  • 色一把做最好网站定制高端网站建设公司
  • 中英文网站如何建设济南做网络安全的公司
  • C 网站开发招聘邵阳棋牌软件开发
  • 富阳设计网站网页在线制作图片
  • 制作公司网站 优帮云上海网站建设怎么弄
  • asp网站生成泰安网站制作公司电话
  • 用网站做平台电商网站构建预算方案
  • 育才网站建设关于未备案网站
  • 网站被抄袭WordPress博客主题免费
  • 网站建设包括沈阳网络科技公司有哪些
  • 学网站开发的软件怎么免费弄网站
  • 江苏省建设厅网站公示重庆专业网站推广时间
  • 平顶山哪里做网站刚刚封城最新消息2021
  • 惠城区城乡规划建设局网站域名查询 阿里云
  • 门户网站的推广方案中国风网站建设
  • 浙江五联建设有限公司网站wordpress最新主题下载地址
  • 宁波网站建设开发门户网站建设参考文献
  • 免费公司网站制作如何成立网站
  • 北京市住房和城乡建设厅网站wordpress5.2.2怎么改中文
  • 仿58网站怎么做设备报价单模板
  • 哈尔滨网站建站模板简单学校网站模板免费下载