??xml version="1.0" encoding="utf-8" standalone="yes"?>伊人久久综在合线亚洲91,久久久久亚洲AV无码专区网站 ,337P日本欧洲亚洲大胆精品http://www.tkk7.com/yongboy/category/54837.html记录工作/学习(fn)的点Ҏ(gu)滴?/description>zh-cnMon, 01 Jun 2015 03:42:48 GMTMon, 01 Jun 2015 03:42:48 GMT60SO_REUSEPORT学习(fn)W记补遗http://www.tkk7.com/yongboy/archive/2015/02/25/423037.htmlnieyongnieyongWed, 25 Feb 2015 14:23:00 GMThttp://www.tkk7.com/yongboy/archive/2015/02/25/423037.htmlhttp://www.tkk7.com/yongboy/comments/423037.htmlhttp://www.tkk7.com/yongboy/archive/2015/02/25/423037.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/423037.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/423037.html

前言

因ؓ(f)能力有限Q还是有很多东西QSO_REUSEADDR和SO_REUSEPORT的区别等Q没有能够在一文字中表达清楚Q作遗,也方便以后自己回q头来复?fn)?/p>

SO_REUSADDR VS SO_REUSEPORT

两者不是一码事Q没有可比性。有时也?x)被其搞晕,自己ȝ的不好,推荐StackOverflow?a >Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ?资料Qȝ的很全面?/p>

单来_(d)(x)

  • 讄?jin)SO_REUSADDR的应用可以避免TCP ?TIME_WAIT 状?旉q长无法复用端口Q尤其表现在应用E序关闭-重启交替的瞬?
  • SO_REUSEPORT更强大,隶属于同一个用P防止端口劫持Q的多个q程/U程׃n一个端口,同时在内核层面替上层应用做数据包q程/U程的处理均?

若有困惑Q推荐两者都讄Q不?x)有冲突?/p>

Netty多线E用SO_REUSEPORT

上一讲到SO_REUSEPORTQ多个程l定同一个端口,可以Ҏ(gu)需要控制进E的数量。这里讲讲基?code>Netty 4.0.25+Epoll navtie transport在单个进E内多个U程l定同一个端口的情况Q也是比较实用的?/p>

TCP服务器,同一个进E多U程l定同一个端?/h4>

q是一个PING-PONGC应用Q?/p>

     public void run() throws Exception {
            final EventLoopGroup bossGroup = new EpollEventLoopGroup();
            final EventLoopGroup workerGroup = new EpollEventLoopGroup();
            ServerBootstrap b = new ServerBootstrap();

           b.group(bossGroup, workerGroup)
                     .channel(EpollServerSocketChannel. class)
                     .childHandler( new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(
                                            new StringDecoder(CharsetUtil.UTF_8 ),
                                            new StringEncoder(CharsetUtil.UTF_8 ),
                                            new PingPongServerHandler());
                           }
                     }).option(ChannelOption. SO_REUSEADDR, true)
                     .option(EpollChannelOption. SO_REUSEPORT, true)
                     .childOption(ChannelOption. SO_KEEPALIVE, true);

            int workerThreads = Runtime.getRuntime().availableProcessors();
           ChannelFuture future;
            for ( int i = 0; i < workerThreads; ++i) {
                future = b.bind( port).await();
                 if (!future.isSuccess())
                      throw new Exception(String. format("fail to bind on port = %d.",
                                 port), future.cause());
           }
           Runtime. getRuntime().addShutdownHook (new Thread(){
                 @Override
                 public void run(){
                     workerGroup.shutdownGracefully();
                     bossGroup.shutdownGracefully();
                }
           });
     }

打成jar包,在CentOS 7下面q行Q检查同一个端口所打开的文件句柄?/p>

# lsof -i:8000
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    3515 root   42u  IPv6  29040      0t0  TCP *:irdmi (LISTEN)
java    3515 root   43u  IPv6  29087      0t0  TCP *:irdmi (LISTEN)
java    3515 root   44u  IPv6  29088      0t0  TCP *:irdmi (LISTEN)
java    3515 root   45u  IPv6  29089      0t0  TCP *:irdmi (LISTEN)

同一q程Q但打开的文件句柄是不一L(fng)?/p>

UDP服务器,多个U程l同一个端?/h4>
/**
 * UDP谚语服务器,单进E多U程l定同一端口C
 */
public final class QuoteOfTheMomentServer {

       private static final int PORT = Integer.parseInt(System. getProperty("port" ,
                   "9000" ));

       public static void main(String[] args) throws Exception {
             final EventLoopGroup group = new EpollEventLoopGroup();

            Bootstrap b = new Bootstrap();
            b.group(group).channel(EpollDatagramChannel. class)
                        .option(EpollChannelOption. SO_REUSEPORT, true )
                        .handler( new QuoteOfTheMomentServerHandler());

             int workerThreads = Runtime.getRuntime().availableProcessors();
             for (int i = 0; i < workerThreads; ++i) {
                  ChannelFuture future = b.bind( PORT).await();
                   if (!future.isSuccess())
                         throw new Exception(String.format ("Fail to bind on port = %d.",
                                     PORT), future.cause());
            }

            Runtime. getRuntime().addShutdownHook(new Thread() {
                   @Override
                   public void run() {
                        group.shutdownGracefully();
                  }
            });
      }
}
}

@Sharable
class QuoteOfTheMomentServerHandler extends
            SimpleChannelInboundHandler<DatagramPacket> {

       private static final String[] quotes = {
                   "Where there is love there is life." ,
                   "First they ignore you, then they laugh at you, then they fight you, then you win.",
                   "Be the change you want to see in the world." ,
                   "The weak can never forgive. Forgiveness is the attribute of the strong.", };

       private static String nextQuote() {
             int quoteId = ThreadLocalRandom.current().nextInt( quotes .length );
             return quotes [quoteId];
      }

       @Override
       public void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet)
                   throws Exception {
             if ("QOTM?" .equals(packet.content().toString(CharsetUtil. UTF_8))) {
                  ctx.write( new DatagramPacket(Unpooled.copiedBuffer( "QOTM: "
                              + nextQuote(), CharsetUtil. UTF_8), packet.sender()));
            }
      }

       @Override
       public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
      }

       @Override
       public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
      }
}

同样也要(g)一下端口文件句柄打开情况Q?/p>

# lsof -i:9000
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    3181 root   26u  IPv6  27188      0t0  UDP *:cslistener
java    3181 root   27u  IPv6  27217      0t0  UDP *:cslistener
java    3181 root   28u  IPv6  27218      0t0  UDP *:cslistener
java    3181 root   29u  IPv6  27219      0t0  UDP *:cslistener

以上为Netty+SO_REUSEPORT多线E绑定同一端口的一些情况,是ؓ(f)记蝲?/p>

nieyong 2015-02-25 22:23 发表评论
]]>
SO_REUSEPORT学习(fn)W记http://www.tkk7.com/yongboy/archive/2015/02/12/422893.htmlnieyongnieyongThu, 12 Feb 2015 08:50:00 GMThttp://www.tkk7.com/yongboy/archive/2015/02/12/422893.htmlhttp://www.tkk7.com/yongboy/comments/422893.htmlhttp://www.tkk7.com/yongboy/archive/2015/02/12/422893.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/422893.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422893.html

前言

本篇用于记录学习(fn)SO_REUSEPORT的笔记和?j)得Q末还?sh)(x)提供一个bindp工具也能ؓ(f)已有的程序n受这个新的特性?/p>

当前Linux|络应用E序问题

q行在Linuxpȝ上网l应用程序,Z(jin)利用多核的优势,一般用以下比较典型的多进E?多线E服务器模型Q?/p>

  1. 单线Elisten/acceptQ多个工作线E接收Q务分发,虽CPU的工作负载不再是问题Q但?x)存在?x)
    • 单线ElistenerQ在处理高速率量q接Ӟ一样会(x)成ؓ(f)瓉
    • CPU~存行丢失套接字l构(socket structure)现象严重
  2. 所有工作线E都accept()在同一个服务器套接字上呢,一样存在问题:(x)
    • 多线E访问server socket锁竞争严?/li>
    • 高负载下Q线E之间处理不均衡Q有旉?:1不均衡比?/li>
    • DCPU~存行蟩?cache line bouncing)
    • 在繁忙C(j)PU上存在较大gq?/li>

上面模型虽然可以做到U程和CPU核绑定,但都?x)存在?x)

  • 单一listener工作U程在高速的q接接入处理时会(x)成ؓ(f)瓉
  • ~存行蟩?/li>
  • 很难做到CPU之间的负载均?/li>
  • 随着核数的扩展,性能q没有随着提升

比如HTTP CPS(Connection Per Second)吞吐量ƈ没有随着CPU核数增加呈现U性增长:(x) 
Image(2)

Linux kernel 3.9带来?jin)SO_REUSEPORTҎ(gu),可以解决以上大部分问题?/p>

SO_REUSEPORT解决?jin)什么问?/h3>

linux man文档中一D|字描q其作用Q?/p>

The new socket option allows multiple sockets on the same host to bind to the same port, and is intended to improve the performance of multithreaded network server applications running on top of multicore systems.

SO_REUSEPORT支持多个q程或者线E绑定到同一端口Q提高服务器E序的性能Q解决的问题Q?/p>

  • 允许多个套接?bind()/listen() 同一个TCP/UDP端口
    • 每一个线E拥有自q服务器套接字
    • 在服务器套接字上没有?jin)锁的竞?/li>
  • 内核层面实现负蝲均衡
  • 安全层面Q监听同一个端口的套接字只能位于同一个用户下?/li>

其核?j)的实现主要有三点?x)

  • 扩展 socket optionQ增?SO_REUSEPORT 选项Q用来设|?reuseport?/li>
  • 修改 bind pȝ调用实现Q以便支持可以绑定到相同?IP 和端?/li>
  • 修改处理新徏q接的实玎ͼ查找 listener 的时候,能够支持在监听相?IP 和端口的多个 sock 之间均衡选择?/li>

代码分析Q可以参考引用资?[多个q程l定相同端口的实现分析[Google Patch]]?/p>

CPU之间q处理Q水qx(chng)?/h4>

以前通过fork形式创徏多个子进E,现在有了(jin)SO_REUSEPORTQ可以不用通过fork的Ş式,让多q程监听同一个端口,各个q程?code>accept socket fd不一P有新q接建立Ӟ内核只会(x)唤醒一个进E来acceptQƈ且保证唤醒的均衡性?/p>

模型单,l护方便?jin),q程的管理和应用逻辑解耦,q程的管理水qx(chng)展权限下攄E序?理员,可以Ҏ(gu)实际q行控制q程启动/关闭Q增加了(jin)灉|性?/p>

q带来了(jin)一个较为微观的水^扩展思\Q线E多是否合适,状态是否存在共享,降低单个q程的资源依赖,针对无状态的服务器架构最为适合?jin)?/p>

新特性测试或多个版本共存

可以很方便的试新特性,同一个程序,不同版本同时q行中,Ҏ(gu)q行l果军_新老版本更q与否?/p>

针对对客L(fng)而言Q表面上感受不到其变动,因ؓ(f)q些工作完全在服务器端进行?/p>

服务器无~重?切换

x(chng)是,我们q代?jin)一版本Q需要部|到U上Qؓ(f)之启动一个新的进E后Q稍后关闭旧版本q程E序Q服务一直在q行中不间断Q需要^衡过度。这像Erlang语言层面所提供的热更新一栗?/p>

x(chng)不错Q但是实际操作v来,׃是那么^滑了(jin)Q还好有一?a >hubtime开源工P原理?code>SIGHUP信号处理?SO_REUSEPORT+LD_RELOADQ可以帮助我们轻村ց刎ͼ有需要的同学可以(g)?gu)用一下?/p>

SO_REUSEPORT已知问题

SO_REUSEPORTҎ(gu)数据包的四元l{src ip, src port, dst ip, dst port}和当前绑定同一个端口的服务器套接字数量q行数据包分发。若服务器套接字数量产生变化Q内怼(x)把本该上一个服务器套接字所处理的客L(fng)q接所发送的数据包(比如三次握手期间的半q接Q以?qing)已l完成握手但在队列中排队的连接)(j)分发到其它的服务器套接字上面Q可能会(x)D客户端请求失败,一般可以用:(x)

  • 使用固定的服务器套接字数量,不要在负载繁忙期间轻易变?/li>
  • 允许多个服务器套接字׃nTCPh?Tcp request table)
  • 不用四元组作ؓ(f)HashD行选择本地套接字处理,?xi)选隶属于同一个CPU的套接字

与RFS/RPS/XPS-mq协作Q可以获得进一步的性能Q?/p>

  • 服务器线E绑定到CPUs
  • RPS分发TCP SYN包到对应CPU怸
  • TCPq接被已l定到CPU上的U程accept()
  • XPS-mq(Transmit Packet Steering for multiqueue)Q传输队列和CPUl定Q发送数?/li>
  • RFS/RPS保证同一个连接后l数据包都会(x)被分发到同一个CPU?/li>
  • |卡接收队列已经l定到CPUQ则RFS/RPS则无设|?/li>
  • 需要注意硬件支持与?/li>

目的嘛,数据包的软硬中断、接收、处理等在一个CPU怸Qƈ行化处理Q尽可能做到资源利用最大化?/p>

SO_REUSEPORT不是一贴万能膏?/h4>

虽然SO_REUSEPORT解决?jin)多个进E共同绑?监听同一端口的问题,但根据新林晓峰同学试l果来看Q在多核扩展层面也未能够做到理想的线性扩展:(x)

可以参考Fastsocket在其基础之上的改q,链接地址?/p>

支持SO_REUSEPORT的Tengine

淘宝的Tengine已经支持?jin)SO_REUSEPORTҎ(gu),在其试报告中,有一个简单测试,可以看出来相Ҏ(gu)SO_REUSEPORT所带来的性能提升Q?/p>

使用SO_REUSEPORT以后Q最明显的效果是在压力下不容易出Ch的情况,CPU均衡性^E?/p>

Java支持否?

JDK 1.6语言层面不支持,至于以后的版本,׃暂时没有使用刎ͼ不多说?/p>

Netty 3/4版本默认都不支持SO_REUSEPORTҎ(gu),但Netty 4.0.19以及(qing)之后版本才真正提供了(jin)JNI方式单独包装的epoll native transport版本Q在Linuxpȝ下运行)(j)Q可以配|类gSO_REUSEPORT{(JAVA NIIO没有提供Q选项Q这部分是在io.netty.channel.epoll.EpollChannelOption中定义(在线代码部分Q?/p>

在linux环境下用epoll native transportQ可以获得内核层面网l堆栈增强的U利Q如何用可参?a >Native transports文档?/p>

使用epoll native transport倒也单,cdE作替换Q?/p>

NioEventLoopGroup → EpollEventLoopGroup
NioEventLoop → EpollEventLoop
NioServerSocketChannel → EpollServerSocketChannel
NioSocketChannel → EpollSocketChannel

比如写一个PING-PONG应用服务器程序,cM代码Q?/p>

public void run() throws Exception {
    EventLoopGroup bossGroup = new EpollEventLoopGroup();
    EventLoopGroup workerGroup = new EpollEventLoopGroup();
    try {
        ServerBootstrap b = new ServerBootstrap();
        ChannelFuture f = b
                .group(bossGroup, workerGroup)
                .channel(EpollServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch)
                            throws Exception {
                        ch.pipeline().addLast(
                                new StringDecoder(CharsetUtil.UTF_8),
                                new StringEncoder(CharsetUtil.UTF_8),
                                new PingPongServerHandler());
                    }
                }).option(ChannelOption.SO_REUSEADDR, true)
                .option(EpollChannelOption.SO_REUSEPORT, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true).bind(port)
                .sync();
        f.channel().closeFuture().sync();
    } finally {
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }
}

若不要这么折腾,q想让以往Java/Netty应用E序在不做Q何改动的前提下顺利在Linux kernel >= 3.9下同样n受到SO_REUSEPORT带来的好处,不妨试一?a >bindpQ更为经,q一部分下面?x)讲到?/p>

bindpQؓ(f)已有应用dSO_REUSEPORTҎ(gu)?/h3>

以前所?a >bindp程序,可以为已有程序绑定指定的IP地址和端口,一斚w可以省去编码,另一斚w也ؓ(f)试提供?jin)一些方ѝ?/p>

另外Qؓ(f)?jin)让以前没有编?code>SO_REUSEPORT的应用程序可以在Linux内核3.9以及(qing)之后Linuxpȝ上也能够得到内核增强支持Q稍做修改,d支持?/p>

但要求如下:(x)

  1. Linux内核(>= 3.9)支持SO_REUSEPORTҎ(gu)?/li>
  2. 需要配|?code>REUSE_PORT=1

不满以上条Ӟ此特性将无法生效?/p>

使用CQ?/p>

REUSE_PORT=1 BIND_PORT=9999 LD_PRELOAD=./libbindp.so java -server -jar pingpongserver.jar &

当然Q你可以Ҏ(gu)需要运行命令多ơ,多个q程监听同一个端口,单机q程水^扩展?/p>

使用C

使用python脚本快速构Z个小的示范原型,两个q程Q都监听同一个端?0000Q客L(fng)hq回不同内容Q仅供娱乐?/p>

server_v1.pyQ简单PING-PONGQ?/p>

# -*- coding:UTF-8 -*-

import socket
import os

PORT = 10000
BUFSIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', PORT))
s.listen(1)

while True:
    conn, addr = s.accept()
    data = conn.recv(PORT)
    conn.send('Connected to server[%s] from client[%s]\n' % (os.getpid(), addr))
    conn.close()

s.close()

server_v2.pyQ输出当前时_(d)(x)

# -*- coding:UTF-8 -*-

import socket
import time
import os

PORT = 10000
BUFSIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', PORT))
s.listen(1)

while True:
    conn, addr = s.accept()
    data = conn.recv(PORT)
    conn.send('server[%s] time %s\n' % (os.getpid(), time.ctime()))
    conn.close()

s.close()

借助于bindpq行两个版本的程序:(x)

REUSE_PORT=1 LD_PRELOAD=/opt/bindp/libindp.so python server_v1.py &
REUSE_PORT=1 LD_PRELOAD=/opt/bindp/libindp.so python server_v2.py &

模拟客户端请?0ơ:(x)

for i in {1..10};do echo "hello" | nc 127.0.0.1 10000;done

看看l果吧:(x)

Connected to server[3139] from client[('127.0.0.1', 48858)]
server[3140] time Thu Feb 12 16:39:12 2015
server[3140] time Thu Feb 12 16:39:12 2015
server[3140] time Thu Feb 12 16:39:12 2015
Connected to server[3139] from client[('127.0.0.1', 48862)]
server[3140] time Thu Feb 12 16:39:12 2015
Connected to server[3139] from client[('127.0.0.1', 48864)]
server[3140] time Thu Feb 12 16:39:12 2015
Connected to server[3139] from client[('127.0.0.1', 48866)]
Connected to server[3139] from client[('127.0.0.1', 48867)]

可以看出来,CPU分配很均衡,各自分配50%的请求量?/p>

嗯,虽是玩P有些意?:))

bindp的用方?/h4>

更多使用说明Q请参?a >README?/p>

参考资?/h3>

nieyong 2015-02-12 16:50 发表评论
]]>Fastsocket学习(fn)W记之小l篇http://www.tkk7.com/yongboy/archive/2015/02/05/422760.htmlnieyongnieyongThu, 05 Feb 2015 07:21:00 GMThttp://www.tkk7.com/yongboy/archive/2015/02/05/422760.htmlhttp://www.tkk7.com/yongboy/comments/422760.htmlhttp://www.tkk7.com/yongboy/archive/2015/02/05/422760.html#Feedback3http://www.tkk7.com/yongboy/comments/commentRss/422760.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422760.html

前言

前面啰啰嗦嗦的几文字,各个斚w介绍?jin)FastsocketQ盲人摸象一般,能力有限Q还得l深入学?fn)不是。这不,C(jin)该小l收时候了(jin)?/p>

~vQ内核已l成为瓶?/h3>

使用Linux作ؓ(f)服务器,在请求量很小的时候,是不用担?j)其性能。但在v量的数据h下,Linux内核在TCP/IP|络处理斚wQ已l成为瓶颈。比如新在某台HAProxy服务器上取样Q?0%的CPU旉被内核占用,应用E序只能够分配到较少的CPU旉周期的资源?/p>

l过Haproxypȝ详尽分析后,发现大部分CPU资源消耗在kernel里,q且在多核^CQkernel在网l协议栈处理q程中存在着大量同步开销?/p>

同时在多怸q行试QHTTP CPS(Connection Per Second)吞吐量ƈ没有随着CPU核数增加呈现U性增长:(x)

Image(2)

内核3.9之前的Linux TCP调用

Image(13)

  • kernel 3.9之前的tcp socket实现
  • bindpȝ调用?x)将socket和portq行l定Qƈ加入全局tcp_hashinfo的bhash链表?
  • 所有bind调用都会(x)查询q个bhash链表Q如果port被占用,内核?x)导致bindp|
  • listen则是Ҏ(gu)用户讄的队列大预先ؓ(f)tcpq接分配内存I间
  • 一个应用在同一个port上只能listen一ơ,那么也就只有一个队列来保存已经建立的连?
  • nginx在listen之后?x)fork处多个workerQ每个worker?x)承listen的socketQ每个worker?x)创Z个epoll fdQƈlisten fd和accept的新q接的fd加入epoll fd
  • 但是一旦新的连接到来,多个nginx worker只能排队acceptq接q行处理
  • 对于大量的短q接Qaccept昄成ؓ(f)?jin)一个瓶?

Linux|络堆栈所存在问题

  • TCP处理&多核

    • 一个完整的TCPq接Q中断发生在一个CPU怸Q但应用数据处理可能?x)在另外一个核?
    • 不同CPU核心(j)处理Q带来了(jin)锁竞争和CPU Cache MissQL动不qQ?
    • 多个q程监听一个TCP套接字,׃n一个listen queue队列
    • 用于q接理全局哈希表格Q存在资源竞?
    • epoll IO模型多进E对accept{待Q惊现?br />
  • Linux VFS的同步损耗严?/p>

    • Socket被VFS理
    • VFSҎ(gu)件节点Inode和目录Dentry有同步需?
    • SOCKET只需要在内存?sh)存在即可,非严格意义上文gpȝQ不需要Inode和Dentry
    • 代码层面略过不必ȝ常规锁,但又保持?jin)够的兼容?

Fastsocket所作改q?/h3>
  1. TCP单个q接完整处理做到?jin)CPU本地化,避免?jin)资源竞?
  2. 保持完整BSD socket API

CPU之间不共享数据,q行化各自独立处理TCPq接Q也是其高效的主要原因。其架构囑֏以看出其改进Q?/p>

20150205215656_12

Fastsocket架构囑֏以很清晰说明其大致结构,内核态和用户态通过ioctl函数传输。记得netmap在重写网卡驱动里面通过ioctl函数直接透传到用h中Q其更ؓ(f)高效Q但没有完整的TCP/IP|络堆栈支持嘛?/p>

Fastsocket的TCP调用?/h4>

Image

  • 多个q程可以同时listen在同一个port?
  • 动态链接库libfsocket.so拦截socket、bind、listen{系l调用ƈq入q个链接库进行处?
  • 对于listenpȝ调用Qfastsocket?x)记录下q个fdQ当应用通过epoll这个fd加入到epoll fdset中时Qlibfsocket.so?x)通过ioctlq程clone listen fd兌的socket、sock、file的系l资?
  • 内核模块clone的socket再次调用bind和listen
  • bindpȝ调用(g)到另外一个进E绑定到已经被绑定的portӞ?x)进行相x(chng)?
  • 通过(g)查sock会(x)被记录到port相关联的一个链表中Q通过该链表可以知道所有bind同一个port的sock
  • 而sock是关联到fd的,q程则持有fdQ那么所有的资源已l关联到一?
  • 新的q程再次调用listenpȝ调用的时候,fastsocket内核?x)再ơ?f)其关联的sock分配accept队列
  • l果是多个进E也拥有了(jin)多个accept队列Q可避免cpu cache miss
  • fastsocket提供每个listen和accept的进E绑定到用户指定的CPU?
  • 如果用户未指定,fastsocket会(x)q程默认l定一个空闲的CPU?

Fastsocket短连接性能

在新测试中Q在24核的安装有Centos 6.5的服务器上,借助于FastsocketQNginx和HAProxy每秒处理q接数指标(connection/secondQ性能很惊人,分别增加290%?20%。这也证明了(jin)QFastsocket带来?jin)TCPq接快速处理的能力?除此之外Q借助于硬件特性:(x)

  • 借助于IntelU程Q可以获得另?0%的性能增长
  • HAProxy代理服务器借助于网卡Flow-DirectorҎ(gu)支持,吞吐量可增加15%

Fastsocket V1.0正式版从2014q?月䆾开始已l在新浪生环境中用,用作代理服务器,因此大家可以考虑是否可以采用。针?.0版本Q以下环境较为收益:(x)

  • 服务器至不于8个CPU核心(j)
  • 短连接被大量使用
  • CPU周期大部分消耗在|络软中断和套接字系l调用上
  • 应用E序使用Zepoll的非dIO
  • 应用E序使用多个q程单独接受q接

多线E嘛Q就得需要参考示范应用所提供实践?jin)?/p>

Nginx试服务器配|?/h4>
  • nginx工作q程数量讄成CPU核数?
  • http keep-aliveҎ(gu)被用
  • 试端http_load从nginx获取64字节?rn)态文Ӟq发量ؓ(f)500*CPU核数
  • 启用内存~存?rn)态文件访问,用于排除盘影响
  • 务必用accept_mutexQ多核访问a(chn)ccept产生锁竞争,另fastsocket内核模块为其去除?jin)锁竞争Q?

从下表测试图片中Q可以看刎ͼ(x)

  1. Fastsocket?4核服务器辑ֈ?75K Connection/SecondQ获得了(jin)21倍的提升
  2. Centos 6.5在CPU核数增长?2核时q没有呈现线性增长势_(d)反而在24核时下降?59k CPS
  3. Linux kernel 3.13?4核时获得?jin)近乎两倍于Centos 6.5的吞吐量Q?83K CPSQ但?2核后呈现出扩展性瓶?

HAProxy重要配置

  • 工作q程数量{同于CPU核数?
  • 需要启用RFD(Receive Flow Deliver)
  • http keep-alive需要禁?
  • 试端http_loadq发量ؓ(f)500*CPU核数
  • 后端服务器响应外?4个字节的消息

试l果中:(x)

  • fastsocket呈现Z(jin)惊h的扩展性能
  • 24核,Linux kernel 3.13成W?39K CPS
  • 24核,Centos 6.5借助FastsocketQ获得了(jin)370K CPS的吞吐量

Fastsocket Throughput

实际部v环境的成l?/h4>

Fastsocket Online

8核服务器U上环境q行?4时的成l,图a展示?jin)部|fastsocket之前CPU利用率,图b为部|了(jin)fastsocekt之后的CPU利用率?Fastsocket带来的收益:(x)

  • 每个CPU核心(j)负蝲均衡
  • q_CPU利用率降?0%
  • HAProxy处理能力增长85%

20150205215658_398

其实吧,q一块期待新公布更多的数据?/p>

长连接的支持正在开发中

长连接支持,q是需要等一{的。但是要支持什么类型长q接Q百万别应用服务器cdQ还是redisQ可能是后者。虽然目前正做,但目前没有时间表Q但目前所做特性ȝ如下Q?/p>

  1. |络堆栈的定?
    • SKB-PoolQ每一CPU核对应一个预分配skb poolQ替换内核缓冲区kernel slab
      • Percore skb pool
      • 合ƈskb头部和数?
      • 本地Pool和重复@环用的PoolQF(tun)low-DirectorQ?
    • Fast-Epoll
      • 多进E之间TCPq接׃n变得E?
      • 在filel构体中保存Epoll entryQ用以节省调用epoll_ctl时红黑树(wi)查询的开销
  2. 跨层的设?
    • Direct-TCPQ数据包隶属于已建立套接字会(x)直接跌路由q程
      • 记录TCP套接字的输入路由信息QRecord input route information in TCP socketQ?
      • 直接查找|络套接字在q入|络堆栈之前QLookup socket directly before network stackQ?
      • 从套接字d输入路由信息QRead input route information from socketQ?
      • 标记数据包被路有q(Mark the packet as routedQ?
    • Receive-CPU-Selection cM于RFSQ但更轻巧、精准与快?
      • 把当前CPU核id~码到套接字中(Application marks current CPU id in the socketQ?
      • 直接查询套接字在q入|络堆栈之前QLookup socket directly before network stackQ?
      • d套接字中包含的CPU核,然后发送给它(Read CPU id from socket and deliver accordinglyQ?
    • RPS-Framework 数据包在q入|络堆栈之前Q让开发者在内核模块之外定制数据包投递规则,扩充RPS功能

Redis试l果

试环境:

  • CPU: Intel E5 2640 v2 (6 core) * 2
  • NIC: Intel X520

Redis配置选项:

  • TCP持久q接
  • 8个Redis实例Q绑定不同端?
  • 使用?个CPU核心(j)Qƈ且绑定CPU?

试l果Q?/p>

  • 仅开启RSSQ?0%的吞吐量增加
  • 启用|卡Flow-DirectorҎ(gu):(x)45%吞吐量增?

但需要注意:(x)

  • 仅ؓ(f)实验试阶段
  • 为V1.0补充QNginx和HAProxy同样?x)收?

Fastsocket v1.1

V1.1版本要增加长q接的支持,那么cM于Redis的服务器应用E序很受益?jin),因?f)没有具体的时间表Q只能够慢慢{待?jin)?/p>

以后一些优化措?/h3>
  1. 在上下文切换Ӟ避免拯操作QZero-Copy
  2. 中断机制完善Q减中?
  3. 支持扚w提交Q降低系l函数调?
  4. 提交到Linux kerneld支上?
  5. HugeTLB/HugePage{?

Fastsocket和mTCP{简单对?/h3>

说是Ҏ(gu)Q其实是我从mTCP论文中摘取出来,增加?jin)Fastsocket一栏,可以看出Z一直努力的脚步?/p>
Types Accept queue Conn. Locality Socket API Event Handling Packet I/O Application Mod- ification Kernel Modification
PSIO ,
DPDK ,
PF RING ,
netmap
No TCP stack Batched No interface for transport layer No
(NIC driver)
Linux-2.6 Shared None BSD socket Syscalls Per packet Transparent No
Linux-3.9 Per-core None BSD socket Syscalls Per packet Add option SO REUSEPORT No
Affinity-Accept Per-core Yes BSD socket Syscalls Per packet Transparent Yes
MegaPipe Per-core Yes lwsocket Batched syscalls Per packet Event model to completion I/O Yes
FlexSC,VOS Shared None BSD socket Batched syscalls Per packet Change to use new API Yes
mTCP Per-core Yes User-level socket Batched function calls Batched Socket API to mTCP API No
(NIC driver)
Fastsocket Per-core Yes BSD socket Ioctl + kernel calls Per packet Transparent No

有一个大致的印象Q也方便Ҏ(gu)Q但q只能是一个暂时的摘要而已Qhcd性能的(f)求L朝着更好的方向发展着?/p>

部v试

怎么说呢QFastsocket是ؓ(f)大家耳熟能详服务器程序NginxQHAProxy{而开发的。但若应用环境ؓ(f)大量的短q接Qƈ且是文件类型请求,不需要强制支持Keep-aliveҎ(gu)(短连接要的是快速请?相应Q然后关闭)(j)Q那么管理员可以试一下FastsocketQ至于部|策略,选择性部|几C为实验看看结果?/p>

本系列到此算是告一D落啦。以后呢Q自然是希望Fastsocket快发布寚wq接的支持,q有更高性能的提升咯 :))

资源引用



nieyong 2015-02-05 15:21 发表评论
]]>
Fastsocket学习(fn)W记之内核篇http://www.tkk7.com/yongboy/archive/2015/02/04/422732.htmlnieyongnieyongWed, 04 Feb 2015 06:22:00 GMThttp://www.tkk7.com/yongboy/archive/2015/02/04/422732.htmlhttp://www.tkk7.com/yongboy/comments/422732.htmlhttp://www.tkk7.com/yongboy/archive/2015/02/04/422732.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/422732.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422732.html

前言

前面分析Fastsocket慢慢凑成?jin)几烂文字Q要把一件事情坚持做下来Q有时味同爵蜡,但既焉择?jin),也得着头皮做下厅R闲话少_(d)文归正文。本文接自上内核模块篇Ql记录学?fn)Fastsocket内核的笔记内宏V?/p>

Fastsocket建立在SO_REUSEPORT支持基础?/h3>

Linux kernel 3.9包含TCP/UDP支持多进E、多U程l定同一个IP和端口的Ҏ(gu),?code>SO_REUSEPORTQ在内核层面同时也让U程/q程之间各自独nSOCKETQ避免CPU怹间以锁资源争?code>accept queue的调用。在fastsocket/kernel/net/sock.h定义sock_commonl构Ӟ可以看到其n影:(x)

unsigned char          skc_reuse:4;
unsigned char          skc_reuseport:4;

在多个socket.h文g中(比如fastsocket/kernel/include/asm/socket.hQ,定义?jin)SO_REUSESORT的变量|(x)

#define SO_REUSEPORT     15

在fastsocket/kernel/net/core/sock.c的sock_setsockopt和sock_getsockopt函数中,都有SO_REUSEPORT的n影:(x)

sock_setsockopt函数中:(x)

case SO_REUSEADDR:
  sk->sk_reuse = valbool;
  break;
case SO_REUSEPORT:
  sk->sk_reuseport = valbool;
  break;

sock_getsockopt函数体中Q?/p>

case SO_REUSEADDR:
  v.val = sk->sk_reuse;
  break;
case SO_REUSEPORT:
  v.val = sk->sk_reuseport;
  break;

?code>SO_REUSEPORTҎ(gu)支持之前的事g驱动驱动服务器资源竞争:(x)

之后呢,可以看做是ƈ行的?jin)?x)

Fastsocket没有重复发明轮子Q在SO_REUSEPORT基础上进行进一步的优化{?/p>

嗯,后面准备写一个动态链接库程序,打算让以前的没有编?code>SO_REUSEPORT的程序也能够在Linux kernel >= 3.9pȝ上n受真正的端口重用的新Ҏ(gu)的支持?/p>

Fastsocket架构?/h3>

Image

下面按照其架构图所C内核层面从上到下一一列出?/p>

虚拟文gpȝVFS的改q?/h3>

因ؓ(f)Linux Kernel VFS的同步损耗严?/p>

  • VFSҎ(gu)件节点Inode和目录Dentry有同步需?
  • 但SOCKET只需要在内存?sh)存在即可,非严格意义上文gpȝQ其不需要\径,不需要ؓ(f)Inode和Dentry加锁
  • 代码层面略过不必ȝ常规锁,但又保持?jin)够的兼容?

提交记录Q?/p>

a209dfc vfs: dont chain pipe/anon/socket on superblock s_inodes list
4b93688 fs: improve scalability of pseudo filesystems

对VFS的改q,在所提升的性能中占有超q?0%的比例,效果非常明显Q?/p>

Local Listen Table

对于多核多接攉列来_(d)linux原生的协议栈只能listen在一个socket上面Qƈ且所有完成三ơ握手还没来得及(qing)被应用accept的套接字都会(x)攑օ光带的accept队列中,acceptpȝ调用必须串行的从队列取出Q当q发量较大时多核竞争Q这成为性能瓉Q媄(jing)响徏立连接处理速度?/p>

Local Listen TableQfastsocket为每一个CPU核克隆监听套接字Qƈ保存到其本地表中QCPU怹间不?x)存在accept的竞争关pR下面ؓ(f)引用描述内容Q?/p>

  • 每个core有一个listen socket table。应用程序徏立连接的时候,执行q程?x)调用local_listen()函数Q有两个参数Q一个是socket FDQ一个是core number. new socket从原始的listen socket(global)拯到per-core local socket table. q些对于应用E序来说都是透明的,提供l应用程序的socketFD是抽象过的,隐藏?jin)底层的实现?
  • 当一个TCP SYN到达本机Qkernel首先去local listen table中找匚w的listen socketQ如果找刎ͼ通过|卡RSS传递这个socketC个coreQ否则就去global listen table中找?
  • 定w斚wQ当q程崩溃的话Qlocal listen socket?x)被关闭Q进入的q接会(x)被引导到global Listen socketQ?q样的话Q别的process可以处理q些q接。由于local listen socket和global listen socket׃nFDQ所以kernel会(x)把新的connet通知到相应的process?
  • 如果应用E序q程使用accept()pȝ调用Q那么处理过E是首先去global listen table中查扑֒操作Q因为是L作,没有使用锁)(j)Q如果没有找刎ͼ那么去core的local table中查找。如果找刎ͼp回给应用E序。由于listen的时候把socketl定C(jin)一个coreQ所以查扄时候也去这个core的local table中查找?
  • epoll兼容性,如果应用E序使用epoll_ctl()pȝ调用Q来把一个listen socketd到Epoll set中,那么local的listen socket和global的listen socket都被epoll监控。事件发生的时候,epoll_wait()pȝ调用?x)返回listen socketQaccept()pȝ调用׃(x)处理q个socket。这样就保证?jin)epoll实现的兼Ҏ(gu)?

使用程图概括上面所qͼ(x)

Image(9)

Local Established Table

Linux内核使用一个全局的hash表以?qing)锁操作来维护establised socketsQ被用来跟踪q接的socketsQ。Fastsocket x(chng)是把全局table分散到per-Core tableQ当一个core需要访问socket的时候,只在隶属于自qtable中搜索,因此不需要锁操纵Q也不存在资源竞争。由fastsocket建立的socket本地l(f)ocal established table中,其他的regular sockets保存在global的table中。core首先去自qlocal table中查找(不需要锁Q,然后去global中查找?/p>

Image(10)

Receive Flow Deliver

默认情况下,应用E序d发包的时候,发出ȝ包是通过正在执行本进E的那个CPU 核(pȝ分配的)(j)来完成的Q而接收数据包的时CPU 核是由前面提到的RSS或RPS来传递。这样一来,q接可能׃同的两个CPU核来完成。连接应该在本地化处理。RFS和Intel|卡的FlowDirector可以从Y件和g上缓解这U情况,但是不完备?/p>

RFDQReceive Flow DeliverQ主要的思想是CPU核数d发vq接的时候可以把CPU core的标识和q接的source port~码C赗CPU cores和ports的关pȝ一个关p集合来军_【coresQports】, 对于一个portQ有唯一的一个core与之对应。当一个core来徏立connection的时候,RFD随机选择一个跟当前core匚w的port。接收包的时候,RFD负责军_q个包应该让哪一个core来处理,如果当前core不是被选中的cpu coreQ那么就deliver到选中的cpu core?/p>

Image

一般来_(d)RFD对代理程序收益比较大Q单U的WEB服务器可以选择用?/p>

以上参考了(jin)大量的外部资料进行整理而成Q进而可以获得一个较为整体的Fastsocket内核架构印象?/p>

Fastsocket的努力,在单个TCPq接的管理从|卡触发的硬中断、Y中断、三ơ握手、数据传输、四ơ挥手等完整的过E在完整在一个CPU怸q行处理Q从而实C(jin)每一个CPU核心(j)TCP资源本地化,q样为多核水qx(chng)展打好了(jin)基础Q减全局资源竞争Q^行化处理q接Q同旉低文仉的副作用Q做C(jin)极ؓ(f)高效的短q接处理Ҏ(gu)Q不得不赞啊?/p>

引用资料Q?/h3>

nieyong 2015-02-04 14:22 发表评论
]]>Fastsocket学习(fn)W记之模块篇http://www.tkk7.com/yongboy/archive/2015/02/03/422694.htmlnieyongnieyongTue, 03 Feb 2015 05:26:00 GMThttp://www.tkk7.com/yongboy/archive/2015/02/03/422694.htmlhttp://www.tkk7.com/yongboy/comments/422694.htmlhttp://www.tkk7.com/yongboy/archive/2015/02/03/422694.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/422694.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422694.html

前言

本篇学习(fn)Fastsocket内核模块fastsocket.soQ作为用h?code>libfsocket.so的内核态的支持Q处?code>ioctl传递到/dev/fastsocket的数据,非常核心(j)和基。嗯Q还是先译Q随后挟带些点评q来?/p>

模块介绍

Fastsocket内核模块 (fastsocket.ko) 提供若干Ҏ(gu),q各自具有开启和关闭{丰富选项可配|?/p>

VFS 优化

CentOS 6.5带来的内栔R竞争处处可见Q导致无论如何优化TCP/IP|络堆栈都不能够带来很好的性能扩展。比较严重锁竞争例子Q?code>inode_lock?code>dcache_lockQ针对套接字文gpȝsockfs而言Qƈ不是必须。fastsocket通过在VFS初始化结构时提供fastpath快速\径用以解x(chng)w题,已经向代号ؓ(f)香草QvanillaQ的内核提交?jin)两处修改?x)

a209dfc vfs: dont chain pipe/anon/socket on superblock s_inodes list
4b93688 fs: improve scalability of pseudo filesystems

此项修改没有提供选项可供配置Q因此所有fastsocket创徏的套接字sockets都会(x)强制l由fastpath传输?/p>

内核模块参数

enable_listen_spawn

fastsocket为每个CPU创徏?jin)一个本地socket监听表(local listen tableQ,应用E序可以军_在一个特定CPU内核上处理某个新的连接,具体是通过拯原始监听套接字socketQ然后插入到本地套接字socket监听表中。当新徏q接在某CPU处理Ӟpȝ内核试匚w本地socket监听表,匚w成功?x)插入到本地accept队列中。稍后,CPU?x)从本地accept队列中获取进行处理?/p>

q种方式每一个网lY中断都会(x)有隶属于自己本地套接字队列当新的q接q来时可以压入,每一个进E从本地队列中弹?gu)接进行处理。当q程和CPUq行l定Q一旦有|卡接口军_投递到某个CPU内核上,那么包括中断、Y中断、系l调用以?qing)用戯E,都会(x)有这个CPU全程负责。好处就是客L(fng)hq接在没有锁的竞争环境下分散到各个CPU上被动处理本地连接?/p>

本特性更适合以下情况Q?/p>

  • 可能多的网卡Rx接收队列和CPU核数
  • 应用E序工作q程被静(rn)态绑定到每一个CPU?

W一U情况下QRPS可以在网卡接攉列小于CPU核数时被使用。第二种Ҏ(gu)可以满两个斚wQ?/p>

  • 应用E序在启动时自己l定工作q程和CPU亲和?
  • 允许fastsocket自动为工作进E绑定CPU亲和?

因此Q?code>enable_listen_spawnh三个值可供配|:(x)

  • enable_listen_spawn=0: d止
  • enable_listen_spawn=1: 启用Q但要求应用E序自己l定CPU
  • enable_listen_spawn=2 (默认?: 启用此特性,允许fastsocket为每一个工作进E绑定到CPU?

enable_fast_epoll

一旦开启,需要ؓ(f)文gl构额外d一字段用以保存文g与epitem的映关p,q样可省depoll_ctlҎ(gu)被调用时从epollU黑?wi)查找epitem的开销?/p>

虽然此项优化有所修改epoll语义Q但带来?jin)套接字性能提升。开启的前提是一个套接字只允许添加到一个epoll实例中,但不包括监听套接字。默认gؓ(f)true可以适用于绝大多数应用程序,若你的程序不满条g得需要禁用了(jin)?/p>

enable_fast_epoll 为布?yu)(dng)型boolean选项:

  • enable_fast_epoll=0: 用fast-epoll
  • enable_fast_epoll=1 (默认?: 开启fast-epoll

enable_receive_flow_deliver

RFDQReceive Flow DeliverQ会(x)把ؓ(f)新徏q接分配的CPU ID装到其q接的端口号中,而不是随机选择新创建的dq接的源端口q行分配到CPU上?/p>

当应用从zdq接收到数据包RFD解码Ӟ?x)从目的地端口上解析出对应的CPU内核IDQ而{发给对应的CPU内核。再加上listen_spawnQ保证了(jin)一个连接CPU处理的完全本地化?/p>

enable_receive_flow是一个布?yu)(dng)型选项:

  • enable_receive_flow=0 (默认?: 用RFD
  • enable_receive_flow=1: 启用RFD

注意事项Q?/p>

  • 当启用时Q在当前的实玎ͼRFD完全覆盖RPS{略Qƈ使得RPS无效。若使用RPSQ请用此特?
  • ׃RFD只会(x)对诸如代理应用程序有利,我们在Web服务器上用此特?

以上Q翻译完毕?/em>

源码单梳?/h3>

fastsocket的内核模块相对\径ؓ(f)fastsocket/module/Q除?jin)README.md外,是两个软连接文件了(jin)Q?/p>

  • fastsocket.c ../kernel/net/fastsocket/fastsocket.c 真实环境下不存在q个文gQ可能是E序BUG
  • fastsocket.h ../kernel/net/fastsocket/fastsocket.h 有对应头文g存在

换种说法Qfastsocket内核模块真正路径?code>fastsocket/kernel/net/fastsocketQ具体文件列表ؓ(f)Q?/p>

  • Kconfig
  • Makefile
  • fastsocket.h 定义内核模块所使用到变量和Ҏ(gu)
  • fastsocket_core.c 负责Ҏ(gu)实现Q供fastsocket_api.c调用
  • fastsocket_api.c 内核模块加蝲/卸蝲{操作,处理前端动态链接库l由ioctl传递的数据

fastsocket_api.c实现内核模块接口Q在源码里面注册?jin)好多文档暂时没有公开的可配置目Q?/p>

int enable_fastsocket_debug = 3;
/* Fastsocket feature switches */
int enable_listen_spawn = 2;
int enable_receive_flow_deliver;
int enable_fast_epoll = 1;
int enable_skb_pool;
int enable_rps_framework;
int enable_receive_cpu_selection = 0;
int enable_direct_tcp = 0;
int enable_socket_pool_size = 0;

module_param(enable_fastsocket_debug,int, 0);
module_param(enable_listen_spawn, int, 0);
module_param(enable_receive_flow_deliver, int, 0);
module_param(enable_fast_epoll, int, 0);
module_param(enable_direct_tcp, int, 0);
module_param(enable_skb_pool, int, 0);
module_param(enable_receive_cpu_selection, int, 0);
module_param(enable_socket_pool_size, int, 0);

MODULE_PARM_DESC(enable_fastsocket_debug, " Debug level [Default: 3]" );
MODULE_PARM_DESC(enable_listen_spawn, " Control Listen-Spawn: 0 = Disabled, 1 = Process affinity required, 2 = Autoset process affinity[Default]");
MODULE_PARM_DESC(enable_receive_flow_deliver, " Control Receive-Flow-Deliver: 0 = Disabled[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_fast_epoll, " Control Fast-Epoll: 0 = Disabled, 1 = Enabled[Default]");
MODULE_PARM_DESC(enable_direct_tcp, " Control Direct-TCP: 0 = Disbale[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_skb_pool, " Control Skb-Pool: 0 = Disbale[Default], 1 = Receive skb pool, 2 = Send skb pool,  3 = Both skb pool");
MODULE_PARM_DESC(enable_receive_cpu_selection, " Control RCS: 0 = Disabled[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_socket_pool_size, "Control socket pool size: 0 = Disabled[Default], other are the pool size");

接收用户态的libfsocket.so通过ioctl传递过来的数据Q根据命令进行数据分发:(x)

static long fastsocket_ioctl(struct file *filp, unsigned int cmd, unsigned long __user u_arg)
{
     struct fsocket_ioctl_arg k_arg;

     if (copy_from_user(&k_arg, (struct fsocket_ioctl_arg *)u_arg, sizeof(k_arg))) {
          EPRINTK_LIMIT(ERR, "copy ioctl parameter from user space to kernel failed\n");
          return -EFAULT;
     }

     switch (cmd) {
     case FSOCKET_IOC_SOCKET:
          return fastsocket_socket(&k_arg);
     case FSOCKET_IOC_LISTEN:
          return fastsocket_listen(&k_arg);
     case FSOCKET_IOC_SPAWN_LISTEN:
          return fastsocket_spawn_listen(&k_arg);
     case FSOCKET_IOC_ACCEPT:
          return fastsocket_accept(&k_arg);
     case FSOCKET_IOC_CLOSE:
          return fastsocket_close(&k_arg);
     case FSOCKET_IOC_SHUTDOWN_LISTEN:
          return fastsocket_shutdown_listen(&k_arg);
     //case FSOCKET_IOC_EPOLL_CTL:
     //     return fastsocket_epoll_ctl((struct fsocket_ioctl_arg *)arg);
     default:
          EPRINTK_LIMIT(ERR, "ioctl [%d] operation not support\n", cmd);
          break;
     }
     return -EINVAL;
}

fastsocket/library/libsocket.h头文件定义的FSOCKET_IOC_* 操作状态码p够一一对应的上?ioctl传输数据从用h?>内核态,需要经q一ơ拷贝过E(copy_from_userQ,然后Ҏ(gu)cmd命o(h)q行功能路由?/p>

libfsocket.so如何与fastsocket内核模块交互

通过指定的设备通道/dev/fastsocketq行交互Q?/p>

  1. fastsocket内核模块注册要监听的通道讑֤名称?code>/dev/fastsocket
  2. libfsocket打开/dev/fastsocket讑֤获得文g句柄Q开?code>ioctl数据传?

单梳理了(jin)fastsocket内核模块Q但一h很多的点没有涉及(qing)Q后面可能会(x)在Fastsocket内核中再次梳理一下?/p>

nieyong 2015-02-03 13:26 发表评论
]]>
Fastsocket学习(fn)W记之动态链接库?/title><link>http://www.tkk7.com/yongboy/archive/2015/02/02/422658.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Mon, 02 Feb 2015 06:16:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2015/02/02/422658.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/422658.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2015/02/02/422658.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/422658.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/422658.html</trackback:ping><description><![CDATA[<div id="ke42ssg" class="wrap"> <h3 id="-">前言</h3> <p>本篇为fastsocket的动态链接库学习(fn)W记Q对应源码目录ؓ(f) fastsocket/libraryQ先译README.md文g内容Q后面添加上个h学习(fn)?j)得?/p> <h3 id="-">介绍</h3> <p>动态链接库<code>libfsocket.so</code>Qؓ(f)已有应用E序提供加速服务,h可维护性和兼容性?/p> <ul> <li><strong>可维护?/strong>QFastsocket优化在于重新实现套接字的pȝ调用从而达到Linux内核|络堆栈效率的提高。而应用程序是不用修改q些pȝ调用Q借助于Fastsocket可以达到加速的目的。Fastsocket在内核模块提供了(jin)一个新的ioctl接口Q供上层应用E序调用? </li><li><strong>兼容?/strong>Q若让应用程序必M改其代码以适应新的pȝ调用接口Q在现实世界中这很麻?ch)也不可行。借助于libfsocket拦截pȝ调用q提供新的接口进行替换系l调用,同时Fastsocket提供?jin)与BSD socket完全兼容的调用接口,q得应用程序在无需更改M代码的情况下Q可直接使用FastsocketQ获得网l加速的效果?</li></ul> <h3 id="-">~译</h3> <p>很简单,q入目录之后Q执?code>make</code>命o(h)~译卛_Q?/p><pre><code><span id="eisy22a" class="hljs-keyword">cd</span> fastsocket/library <span id="o6awsyo" class="hljs-keyword">make</span> </code></pre> <p>最后在当前目录下生?code>libfsocket.so</code>文g?/p> <h3 id="-">用法</h3> <p>很简单的_(d)借助?code>LD_PRELOAD</code>加蝲<code>libfsocket.so</code>Q启动应用程序,以nginxZQ?/p><pre><code><span id="y4k2m24" class="hljs-constant">LD_PRELOAD</span>=<span id="gmiqwk4" class="hljs-regexp">/your_path/fastsocket</span><span id="ewekegy" class="hljs-regexp">/library/libfsocket</span>.so nginx </code></pre> <p>若回滚,q单了(jin)Q直接启动nginxpQ?/p><pre><code>nginx </code></pre> <p>注意事项Q?/p> <ul> <li>保<code>fastsocket.ko</code>内核模块已经加蝲成功 </li><li>只对启动时以预加?code>libfsocket.so</code>的上层应用程序有效果 </li></ul> <h3 id="-">内部构g</h3> <p>Fastsocket拦截|络套接字的常规pȝ调用Qƈ使用ioctl接口取代之?/p> <p>若不依赖?code>libfsocket.so</code>Q上层应用程序要想用Fastsocket Percore-Listen-Table的特点,应用E序需要在父流Eforking之后Q以?qing)提前做事g循环Qevent loopQ处理,应用工作q程需要手动调?code>listen_spawn</code>函数Q复制全局的监听套接字q插入到本地监听表中?/p> <p><code>libfsocket.so</code>Z层应用程序做?code>listien_spawn</code>的工作,用以保持应用E序的代码不变,Ҏ(gu)如下:</p> <ul> <li><code>libfsocket.so</code>跟踪所有需要监听的套接字文件句? </li><li><code>libfsocket.so</code>拦截?code>epoll_ctl</code>pȝ调用 </li><li>当监听到应用E序调用<code>epoll_ctl</code>d监听套接字文件句柄到epollӞ<code>libfsocket.so</code>?x)调?code>listen_spawn</code>Ҏ(gu) </li></ul> <p>不是所有应用程序都适合本方案,但nginx、haproxy、lighttpd与之配合工作得相当不错。因此当你在其他应用E序中想使用Percore-Listen-TableҎ(gu)时Q请务必心(j)试?jin),保是否合适?/p> <p><em>OKQ翻译完毕?/em></p> <h3 id="-">源码一?/h3> <p>fastsocket/library用于构徏<code>libfsocket.so</code>动态链接库Q主要组成:(x)</p> <ul> <li>Makefile ~译脚本 </li><li>libsocket.h 头文Ӟ定义变量、结构等 </li><li>libsocket.c 动态链接库实现 </li></ul> <h3 id="libsocket-h">libsocket.h</h3> <p>定义?code>ioctl</code>Qؓ(f)Input/Output ConTroL~写Q函数和伪设?<code>/dev/fastsocket</code>)交换数据所使用到的几个命o(h)Q?/p><pre><code><span id="qwowug2" class="hljs-hexcolor">#def</span>ine IOC_ID <span id="yeyem6w" class="hljs-number">0</span>xf5 <span id="2q22aa2" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_SOCKET _IO(IOC_ID, <span id="62gcs2w" class="hljs-number">0</span>x01) <span id="os6qyqg" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_LISTEN _IO(IOC_ID, <span id="geke4c2" class="hljs-number">0</span>x02) <span id="a4qi4iy" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_ACCEPT _IO(IOC_ID, <span id="u6222wy" class="hljs-number">0</span>x03) <span id="gmem4aa" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_CLOSE _IO(IOC_ID, <span id="sisysum" class="hljs-number">0</span>x04) <span id="q2qmewy" class="hljs-comment">//#define FSOCKET_IOC_EPOLL_CTL _IO(IOC_ID, 0x05)</span> <span id="eo40sii" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_SPAWN_LISTEN _IO(IOC_ID, <span id="42gowmo" class="hljs-number">0</span>x06) <span id="aciqyao" class="hljs-hexcolor">#def</span>ine FSOCKET_IOC_SHUTDOWN_LISTEN _IO(IOC_ID, <span id="2y2uk4o" class="hljs-number">0</span>x07) </code></pre> <p>紧接着定义?jin)需要在用户态和内核态通过<code>ioctl</code>q行交互的结构:(x)</p><pre><code><span id="wwcugi2" class="hljs-tag">struct</span> <span id="me6ckoq" class="hljs-tag">fsocket_ioctl_arg</span> { <span id="u2yg2uu" class="hljs-attribute">u32</span> fd; <span id="umg444w" class="hljs-attribute">u32</span> backlog; <span id="qmuauwy" class="hljs-tag">union</span> <span id="sue4ky4" class="hljs-tag">ops_arg</span> { <span id="44uyswy" class="hljs-tag">struct</span> <span id="6agmeg2" class="hljs-tag">socket_accept_op_t</span> { <span id="kowqiwg" class="hljs-attribute">void</span> *sockaddr; <span id="ggkekmc" class="hljs-attribute">int</span> *sockaddr_len; <span id="coy4yqe" class="hljs-attribute">int</span> flags; }<span id="mek42am" class="hljs-attribute">accept_op</span>; <span id="aci4oas" class="hljs-tag">struct</span> <span id="g2wsmog" class="hljs-tag">spawn_op_t</span> { <span id="mow4kmo" class="hljs-attribute">int</span> cpu; }<span id="w6qyqsi" class="hljs-attribute">spawn_op</span>; <span id="6keokw4" class="hljs-tag">struct</span> <span id="y460a4q" class="hljs-tag">io_op_t</span> { <span id="q4wcgww" class="hljs-attribute">char</span> *buf; <span id="cewc2iw" class="hljs-attribute">u32</span> buf_len; }<span id="6eackos" class="hljs-attribute">io_op</span>; <span id="62uciko" class="hljs-tag">struct</span> <span id="kemgwoq" class="hljs-tag">socket_op_t</span> { <span id="2g22q24" class="hljs-attribute">u32</span> family; <span id="q2ekees" class="hljs-attribute">u32</span> type; <span id="sciekmc" class="hljs-attribute">u32</span> protocol; }<span id="iw6kgmo" class="hljs-attribute">socket_op</span>; <span id="cm4swy2" class="hljs-tag">struct</span> <span id="o4oi4y2" class="hljs-tag">shutdown_op_t</span> { <span id="6smeycq" class="hljs-attribute">int</span> how; }<span id="ayew4g2" class="hljs-attribute">shutdown_op</span>; <span id="kgqiyao" class="hljs-tag">struct</span> <span id="c4i4ss2" class="hljs-tag">epoll_op_t</span> { <span id="qaqagkm" class="hljs-attribute">u32</span> epoll_fd; <span id="k6wowy4" class="hljs-attribute">u32</span> size; <span id="62i22im" class="hljs-attribute">u32</span> ep_ctl_cmd; <span id="y6kswkm" class="hljs-attribute">u32</span> time_out; <span id="wicieuy" class="hljs-attribute">struct</span> epoll_event *ev; }<span id="cygm2mm" class="hljs-attribute">epoll_op</span>; }<span id="maga2cq" class="hljs-attribute">op</span>; }; </code></pre> <p>q样看来Q?code>ioctl</code>函数原型调用为:(x)</p><pre><code> <span id="i6a2o2s" class="hljs-function"><span id="esy2g22" class="hljs-title">ioctl</span><span id="uwsysiw" class="hljs-params">(/dev/fastsocket讑֤文g句柄Q?FSOCKET_IOC_具体宏命令, fsocket_ioctl_argl构指针)</span></span> </code></pre> <p>现在大致能够弄清楚了(jin)内核态和用户态之间通过<code>ioctl</code>传递结构化的数据的方式?jin)?/p> <h3 id="libsocket-c-">libsocket.c 要分?/h3> <p>q接内核模块已经注册好的讑֤道<code>/dev/fastsocket</code>Q获取到文g描述W,同时做些CPUq程l定的工?/p><pre><code><span id="yuosae4" class="hljs-preprocessor">#<span id="6mw2moe" class="hljs-keyword">define</span> INIT_FDSET_NUM 65536</span> ...... __attribute__((constructor)) <span id="gsyge2y" class="hljs-keyword">void</span> fastsocket_init(<span id="eeawmc4" class="hljs-keyword">void</span>) { <span id="4u2kq2i" class="hljs-keyword">int</span> ret = <span id="eiagoem" class="hljs-number">0</span>; <span id="iga2suy" class="hljs-keyword">int</span> i; cpu_set_t cmask; ret = open(<span id="ugcwae2" class="hljs-string">"/dev/fastsocket"</span>, O_RDONLY); <span id="suou42k" class="hljs-comment">// 建立fastsocket通道</span> <span id="42m2acu" class="hljs-keyword">if</span> (ret < <span id="6eem2gk" class="hljs-number">0</span>) { FSOCKET_ERR(<span id="u24u2qq" class="hljs-string">"Open fastsocket channel failed, please CHECK\n"</span>); <span id="8ekew4g" class="hljs-comment">/* Just exit for safty*/</span> <span id="m64q4gk" class="hljs-built_in">exit</span>(-<span id="c42m2aa" class="hljs-number">1</span>); } fsocket_channel_fd = ret; fsocket_fd_set = <span id="6ksmqiw" class="hljs-built_in">calloc</span>(INIT_FDSET_NUM, <span id="wi6q24o" class="hljs-keyword">sizeof</span>(<span id="gsmgmcg" class="hljs-keyword">int</span>)); <span id="ia4s2sg" class="hljs-keyword">if</span> (!fsocket_fd_set) { FSOCKET_ERR(<span id="mg6qocq" class="hljs-string">"Allocate memory for listen fd set failed\n"</span>); <span id="aywqm42" class="hljs-built_in">exit</span>(-<span id="mwaimq4" class="hljs-number">1</span>); } fsocket_fd_num = INIT_FDSET_NUM; <span id="miow2qs" class="hljs-comment">// gؓ(f)65535</span> CPU_ZERO(&cmask); <span id="c2q4iyo" class="hljs-keyword">for</span> (i = <span id="eum2og2" class="hljs-number">0</span>; i < get_cpus(); i++) CPU_SET(i, &cmask); ret = sched_setaffinity(<span id="ayu4a4y" class="hljs-number">0</span>, get_cpus(), &cmask); <span id="csagmoc" class="hljs-keyword">if</span> (ret < <span id="cue2wwa" class="hljs-number">0</span>) { FSOCKET_ERR(<span id="wkcuoeu" class="hljs-string">"Clear process CPU affinity failed\n"</span>); <span id="wewqi44" class="hljs-built_in">exit</span>(-<span id="6cy2o22" class="hljs-number">1</span>); } <span id="scumsgw" class="hljs-keyword">return</span>; } </code></pre> <p>主观上,仅仅是ؓ(f)?jin)短q接而设|的Q定义的fastsocket文g句柄数组大小?5535Q针对类gWEB Server、HTTP API{环境够了(jin)Q针对百万别的长连接服务器环境?yu)׃适合?jin)?/p> <p>socket/listen/accept/close/shutdown/epoll_ctl{函敎ͼ通过<code>dlsym</code>方式替换已有套接字系l函数等Q具体的交互q程使用<code>ioctl</code>替代一些系l调用?/p> <p>除了(jin)重写socket/listen/accept/close/shutdown{套接字接口Q同时也?code>epoll_ctl</code>Ҏ(gu)动了(jin)手术Q江湖传aCPU多核多进E的epoll服务器存在惊现象)(j)Q更好利用多核:(x)</p><pre><code><span id="aayum4a" class="hljs-function"><span id="442gs2s" class="hljs-keyword">int</span> <span id="sq4yeei" class="hljs-title">epoll_ctl</span><span id="kuogaae" class="hljs-params">(<span id="4g2qm22" class="hljs-keyword">int</span> efd, <span id="m2ygaoq" class="hljs-keyword">int</span> cmd, <span id="2uowmeu" class="hljs-keyword">int</span> fd, <span id="wg4icce" class="hljs-keyword">struct</span> epoll_event *ev)</span> </span>{ <span id="em2yqei" class="hljs-function"><span id="sumsmoe" class="hljs-keyword">static</span> <span id="ykq4qeu" class="hljs-title">int</span> <span id="i42sam2" class="hljs-params">(*real_epoll_ctl)</span><span id="cewakky" class="hljs-params">(<span id="cm4agik" class="hljs-keyword">int</span>, <span id="qasm4gw" class="hljs-keyword">int</span>, <span id="ei4umcs" class="hljs-keyword">int</span>, <span id="ccy4i2k" class="hljs-keyword">struct</span> epoll_event *)</span> </span>= NULL; <span id="6cyuosy" class="hljs-keyword">int</span> ret; <span id="i8qiceu" class="hljs-keyword">struct</span> fsocket_ioctl_arg arg; <span id="om44kc2" class="hljs-keyword">if</span> (fsocket_channel_fd >= <span id="y4awoes" class="hljs-number">0</span>) { arg.fd = fd; arg.op.spawn_op.cpu = -<span id="ayic4gi" class="hljs-number">1</span>; <span id="mmou42c" class="hljs-comment">/* "Automatically" do the spawn */</span> <span id="22kqyog" class="hljs-keyword">if</span> (fsocket_fd_set[fd] && cmd == EPOLL_CTL_ADD) { ret = ioctl(fsocket_channel_fd, FSOCKET_IOC_SPAWN_LISTEN, &arg); <span id="ssycw6k" class="hljs-keyword">if</span> (ret < <span id="eeu442q" class="hljs-number">0</span>) { FSOCKET_ERR(<span id="s4ucwyo" class="hljs-string">"FSOCKET: spawn failed!\n"</span>); } } } <span id="sc42ska" class="hljs-keyword">if</span> (!real_epoll_ctl) real_epoll_ctl = dlsym(RTLD_NEXT, <span id="cau2wym" class="hljs-string">"epoll_ctl"</span>); ret = real_epoll_ctl(efd, cmd, fd, ev); <span id="km44amc" class="hljs-keyword">return</span> ret; } </code></pre> <p>因ؓ(f)定义?jin)作用于内部的?rn)态变?code>real_epoll_ctl</code>Q只有在W一ơ加载的时候才?x)被赋|<code>real_epoll_ctl = dlsym(RTLD_NEXT, "epoll_ctl")</code>Q后面调用时通过<code>ioctl</code>把fsocket_ioctl_arg传递到内核模块中去?/p> <p>其它socket/listen/accept/close/shutdown{套接字接口Q流E类伹{?/p> <h3 id="-">结</h3> <p>以上单翻译、粗略分析用hfastsocket动态链接库大致情况Q若要v作用Q需要和内核态Fastsocketq行交互、传递数据才能够作用的很好?/p></div><img src ="http://www.tkk7.com/yongboy/aggbug/422658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2015-02-02 14:16 <a href="http://www.tkk7.com/yongboy/archive/2015/02/02/422658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Fastsocket学习(fn)W记之网卡设|篇http://www.tkk7.com/yongboy/archive/2015/01/30/422592.htmlnieyongnieyongFri, 30 Jan 2015 08:49:00 GMThttp://www.tkk7.com/yongboy/archive/2015/01/30/422592.htmlhttp://www.tkk7.com/yongboy/comments/422592.htmlhttp://www.tkk7.com/yongboy/archive/2015/01/30/422592.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/422592.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422592.html

前言

前面~译安装好了(jin)包含有fastsocket的内核模块,以及(qing)fastsocket的动态链接库libfsocket.soQ下面其实就可以讄|卡?jin)?/p>

下面Z些名词解释,上下文中需要用到Q?/p>

  • RxQ接攉?
  • TxQ发送队?

本文|卡讄W记内容Q大部分来自于fastsocket源码相对路径fastsocket/scripts/Q老规矩,先翻译?/p>

|卡讄翻译原?/h3>

介绍

nic.sh脚本负责|卡配置以尽可能的最大化受益于fastsocket带来的问题。给定一个网卡接口, 它调整接口的各种Ҏ(gu)以?qing)一些系l配|?/p>

相关配置

中断和CPU的亲和?/h5>

每个|卡g队列?qing)其兌中断l定C同的CPU核心(j)。若g队列数大于CPU核数Q队列需要配|成循环round-robin方式Q?Irqbalance服务需要被用以防其更攚w|?/p>

中断阀速率

nic.sh脚本通过ethtool命o(h)讄每秒中断C限,防止中断风暴。两个Rx中断间隔讄成至?33usQ约3000个中断每U?/p>

RPS

为每个CPU核心(j)与不同的|卡g队列之间建立一一映射对应关系Q这样CPU核心(j)?yu)可以很均匀地处理网l数据包。当|卡g队列于CPU内核敎ͼnic.sh脚本利用RPS (Receive Packet Steering)软g方式qq入量负蝲Q这样CPU和硬仉列不存在对应关系。RPS机制可以让进入的数据包自由分发到MCPU怸?/p>

|卡接收产生的中断可以均衡分配到对应CPU上?/p>

XPS

XPS (Transmit Packet Steering) 建立CPU内核和Tx发送队列映对应关p,掌控出站数据包。系l有N个CPU核心(j)Q脚本会(x)讄XPS臛_存在N个Tx队列在网卡接口上Q这样就可以建立CPU内核和Tx队列1?的映关pR?/p>

|卡传送数据生的中断一样可以均很分配到CPU上,避免单个CPU核心(j)q于J忙?/p>

IPTABLES

压测Ӟ防火墙iptables的规则会(x)占用更多的CPU周期Q有所降低|络堆栈性能。因?code>nic.sh脚本若检到iptables后台q行中会(x)直接输出报警信息Q提C关闭之?/p>

nic.sh脚本脚本分析

l过验证好用的Intel和博通系列千兆和万兆|卡列表Q?/p>

# igb
"Intel Corporation 82576 Gigabit Network Connection (rev 01)"
"Intel Corporation I350 Gigabit Network Connection (rev 01)"
# ixgbe
"Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01)"
"Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)"
# tg3
"Broadcom Corporation NetXtreme BCM5720 Gigabit Ethernet PCIe"
"Broadcom Corporation NetXtreme BCM5761 Gigabit Ethernet PCIe (rev 10)"
# bnx2
"Broadcom Corporation NetXtreme II BCM5708 Gigabit Ethernet (rev 12)"
"Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)"

若当前服务器没有以上|卡Q会(x)警告一下,无碍?/p>

q里把一些常规性的CPU、网卡驱动、网l队列情冉|查单独抽取出来,重温好多已经遗忘的命令,有改变,q样写较单嘛Q便于以后用:(x)

  • 直接查看CPU核数Q?code>grep -c processor /proc/cpuinfo
  • 查看|卡软接攉列数Q?code>ls /sys/class/net/eth0/queues | grep -c rx
  • 查看|卡软发生队列数Q?code>ls /sys/class/net/eth0/queues | grep -c tx
  • 查看当前|卡g队列敎ͼ(x)egrep -c eth0 /proc/interrupts
  • 查看|卡名称和版本号Q?code>lspci | grep Ethernet | sed "s/Ethernet controller: //g"
  • 查看|卡驱动名称Q?code>ethtool -i eth0 | grep driver

脚本先是获取CPU、网卡等信息Q接着讄中断单位U内吞吐量:(x) ethtool -C eth0 rx-usecs 333 > /dev/null 2>&1

启用XPSQ充分借助|卡发送队列,提升|卡发送吞吐量Q是有条仉制的Q发送队列数要大于CPU核数Q?/p>

if [[ $TX_QUEUES -ge $CORES ]]; then
    for i in $(seq 0 $((CORES-1))); do
     cpuid_to_mask $((i%CORES)) | xargs -i echo {} > /sys/class/net/$IFACE/queues/tx-$i/xps_cpus
    done
    info_msg "    XPS enabled"
fi

接着判断是否可以启用PRSQ省L动设|的ȝ(ch)Q但启用RPS前提是CPU核数与网卡硬仉列不相等Q?/p>

if [[ ! $HW_QUEUES == $CORES ]]; then
    for i in /sys/class/net/$IFACE/queues/rx-*; do
     printf "%x\n" $((2**CORES-1)) | xargs -i echo {} > $i/rps_cpus;
    done
    info_msg "    RPS enabled"
else
    for i in /sys/class/net/$IFACE/queues/rx-*; do
     echo 0 > $i/rps_cpus;
    done
    info_msg "    RPS disabled"
fi

若没有用fastsocketQ单U借助于RPSQ会(x)带来处理中断的CPU和处理当前数据包的CPU不是同一个,自然?x)造成CPU Cache MissQCPU~存?sh)失Q,造成许的性能影响Qؓ(f)?jin)避免这U情况,Z?x)依赖于RFSQReceive Flow SteeringQ?/p>

使用?jin)fastsocket后,׃用这么麻?ch)?jin)?/p>

irqbalance和fastsocket有冲H,?x)强制禁用?x)

if ps aux | grep irqbalance | grep -v grep; then
    info_msg "Disable irqbalance..."
    # XXX Do we have a more moderate way to do this?
    killall irqbalance > /dev/null 2>&1
fi

脚本也包含了(jin)讄中断和CPU的亲和性:(x)

i=0
intr_list $IFACE $DRIVER | while read irq; do
    cpuid_to_mask $((i%CORES)) | xargs -i echo {} > /proc/irq/$irq/smp_affinity
    i=$((i+1))
done

若iptables服务存在Q会(x)友善用?x)好一些,毕竟?x)带来性能损耗。文件打开句柄不大?024Q脚本同样会(x)提醒Q怎么讄文g打开句柄Q可以参考以前博文?/p>

Linuxpȝ|络堆栈的常规扩展优化措?/h3>

针对不用fastsocket的服务器Q当前比较流行的针对|卡的网l堆栈性能扩展、优化措施,一般会(x)使用到RSS、RPS、RFS、XFS{方式,以便充分利用CPU多核和硬件网卡等自n性能Q达到ƈ?q发处理的目的。下面ȝ一个表|可以凑合看一下?/p>

RSS
(Receive Side Scaling)
RPS
(Receive Packet Steering)
RFS
(Receive Flow Steering)
Accelerated RFS
(Accelerated Receive Flow Steering)
XPS
(Transmit Packet Steering)
解决问题 |卡和驱动支?/td> 软g方式实现RSS 数据包生的中断和应用处理在同一个CPU?/td> ZRFSg加速的负蝲q机制 选择|卡多队列的队列快速发?/td>
内核支持 2.6.36开始引入,需要硬件支?/td> 2.6.35 2.6.35 2.6.35 2.6.38
|卡队列数和物理核数一?/td> x(chng)多队列的|卡若RSS已经配置?jin),则不需要RPS?/td> 需要rps_sock_flow_entries和rps_flow_cnt属?/td> 需要网卡设备和驱动都支持加速。ƈ且要求ntupleqo(h)已经通过ethtool启用 单传输队列的|卡无效Q若队列比CPU,׃n指定队列的CPU最好是与处理传输硬中断的CPU׃n~存的CPU
fastsocket |卡Ҏ(gu)?/td> 改进版RPSQ性能提升 源码包含Q文档没有涉?/td> 文档没有涉及(qing) 要求发送队列数要大于CPU核数
传送方?/td> |卡接收 内核接收 CPU接收处理 加速ƈ接收 |卡发送数?/td>

更具体优化措施,可以参考文档:(x)Scaling in the Linux Networking Stack?/p>

另,若网卡支?code>Flow Director FiltersҎ(gu)(q里有一个非常有的动画介绍Q?a >Intel® Ethernet Flow DirectorQ值得一看)(j)Q那么可以结合F(tun)astsocket一起加速。比如,在其所作Redis长连接测试中Q启用Flow-DirectorҎ(gu)要比禁用可以带?5%的性能提升?/p>

自然软硬l合Q可以做的更好一些嘛?/p>

延阅读Q?a >多队列网卡简?/a>

以上记录?jin)学习(fn)fastsocket的网卡设|脚本方面笔记?/p>

不过呢,nic.sh脚本Q值得收藏Q无Z不用fastsocketQ对U上服务器网卡调优都是不错选择哦?/p>

nieyong 2015-01-30 16:49 发表评论
]]>
Fastsocket学习(fn)W记之安装篇http://www.tkk7.com/yongboy/archive/2015/01/30/422579.htmlnieyongnieyongFri, 30 Jan 2015 05:14:00 GMThttp://www.tkk7.com/yongboy/archive/2015/01/30/422579.htmlhttp://www.tkk7.com/yongboy/comments/422579.htmlhttp://www.tkk7.com/yongboy/archive/2015/01/30/422579.html#Feedback2http://www.tkk7.com/yongboy/comments/commentRss/422579.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422579.html

前言

q行环境为Centos 6.5pȝQ默认内ؓ(f)2.6.32-431.el6.x86_64Q下面所有编译安装操作是?code>root用户权限q行操作?/p>

~译安装f(xi)astsocket内核

W一步需要下载代码,当然q是废话?jin),下蝲?opt目录下:(x)

 git clone https://github.com/fastos/fastsocket.git

~译安装

下蝲之后Q需要进入其目录中:(x)

 cd fastsocket/kernel

因ؓ(f)是涉?qing)到内核嘛,~译之前需要做一些参数选项配置Q?code>make config?x)篏Mh的,好几千个选项参数需要你一一配置Q大部分旉Q默认配|就挺好的:(x)

 make defconfig

然后嘛,~译内核的节奏:(x)

 make

内核~译相当耗费旉Q至?0分钟旉。之后紧接着是编译所需的内核模块,fastsocket模块Q?/p>

 make modules_install

~译完成之后Q最后一条输出,?x)看刎ͼ?x)

DEPMOD 2.6.32-431.17.1.el6.FASTSOCKET

fastsocket内核模块~译好之后,需要安装内核:(x)

 make install

上面命o(h)其实执行shell脚本q行安装Q?/p>

sh /opt/fastsocket/kernel/arch/x86/boot/install.sh 2.6.32-431.17.1.el6.FASTSOCKET arch/x86/boot/bzImage \ System.map "/boot"

基本上,fastsocket内核模块已经构徏安装完毕?jin),但需要告知Linuxpȝ在下ơ启动的时候切换到新编译的、包含有fastsocket模块的内核?/p>

配置启动旉要切换的内核

q部分需要在/etc/grup.conf中配|,现在看一下其文g内容Q?/p>

default=1
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-431.17.1.el6.FASTSOCKET)
        root (hd0,0)
        kernel /vmlinuz-2.6.32-431.17.1.el6.FASTSOCKET ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS rd_NO_MD rd_LVM_LV=vg_centos6/lv_swap crashkernel=auto LANG=zh_CN.UTF-8 rd_LVM_LV=vg_centos6/lv_root  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
        initrd /initramfs-2.6.32-431.17.1.el6.FASTSOCKET.img
title CentOS (2.6.32-431.el6.x86_64)
        root (hd0,0)
        kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=/dev/mapper/vg_centos6-lv_root rd_NO_LUKS rd_NO_MD rd_LVM_LV=vg_centos6/lv_swap crashkernel=auto LANG=zh_CN.UTF-8 rd_LVM_LV=vg_centos6/lv_root  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
        initrd /initramfs-2.6.32-431.el6.x86_64.img

defautl=1Q表C目前系l选择的以原先内核作作为启动项Q原先位于第二个root (hd0,0)后面Q需要切换到新的内核下面Q需要修?code>default=0Q保存后Qreboot重启pȝQ之生效?/p>

(g)生?/h4>

pȝ重启后,需要加载fastsocket模块到系l运行中去,下面以默认选项参数方式加蝲Q?/p>

modprobe fastsocket

加蝲之后Q列出当前系l所加蝲模块列表Q检查是否成?/p>

lsmod | grep fastsocket

若能看到cM输出信息Q表COKQ?/p>

fastsocket 39766 0

开始构建libfastsocket.so链接库文?/h3>

上面内核模块安装好之后,可以构徏fastsocket的动态链接库文g?jin)?x)

cd /opt/fastsocket/library/
make

可能?x)收C些警告信息,无碍Q?/p>

gcc -g -shared -ldl -fPIC libsocket.c -o libfsocket.so -Wall
libsocket.c: 在函?#8216;fastsocket_init’?
libsocket.c:59: 警告Q隐式声明函?#8216;open’
libsocket.c: 在函?#8216;fastsocket_expand_fdset’?
libsocket.c:109: 警告Q隐式声明函?#8216;ioctl’
libsocket.c: 在函?#8216;accept’?
libsocket.c:186: 警告Q对指针赋值时目标与指针符号不一?
libsocket.c: 在函?#8216;accept4’?
libsocket.c:214: 警告Q对指针赋值时目标与指针符号不一?

最后,可以看到gcc~译之后生成?code>libfsocket.so库文Ӟ说明~译成功?/p>

OKQ编译安装到此结束,后面是如何使用fastsocket的示范程序进行测试了(jin)?/p>

nieyong 2015-01-30 13:14 发表评论
]]>
Fastsocket学习(fn)W记之示范应用篇http://www.tkk7.com/yongboy/archive/2015/01/29/422550.htmlnieyongnieyongThu, 29 Jan 2015 09:16:00 GMThttp://www.tkk7.com/yongboy/archive/2015/01/29/422550.htmlhttp://www.tkk7.com/yongboy/comments/422550.htmlhttp://www.tkk7.com/yongboy/archive/2015/01/29/422550.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/422550.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/422550.html

前言

上篇介绍?jin)如何构建安装f(xi)astsocket内核模块Q下面将Zfastsocket/demo/README.md文g译整理而成?/p>

嗯,下面q入译?/p>

介绍

CZ个简单TCP Server服务器程序,用于基准试和剖析Liunx内核|络堆栈性能表现Q当然也是ؓ(f)?jin)演CFastsocket可扩展和其性能改进?/p>

C应用Zepoll模型和非d性IOQ处理网l连接,但只有在多核的模式下才能够工作得很好Q程序的每一个进E被l定到CPU的不同核Qv始于CPU core 0Q各自独立处理客L(fng)q接h?/p>

CE序h两种工作模式Q?/p>

  • 服务器模?/strong>QQ何请求都?x)直接返回HTTP 200 OK
  • 代理模式Q服务器接收到客L(fng)hQ{发给后端服务器,同时转发后端响应l客L(fng)?

q是一个简单傻瓜Ş式的Tcp ServerQ仅仅用于测试用,使用时要求客L(fng)和服务器端只能够携带一个packet包大的数据Q否则程序会(x)处理不了(jin)?/p>

构徏

以下面方式进行构建:(x)

cd demo && make

用法

最单方式以默认配置无参数Ş式运行:(x)

./server

参数如下:

  • -w worker_num: 定义q程?
    • 默认gؓ(f)当前可用CPU核心(j)Cq程.
  • -c start_core: 指定q程l定CPU核的开始烦(ch)引?
    • 默认gؓ(f) 0.
  • -o log_file: 定义日志文g名称
    • 默认gؓ(f) ./demo.log
  • -a listen_address: 指定监听地址Q[ip:port]字符串组合Ş式,支持d多个地址
    • 默认gؓ(f) 0.0.0.0:80
  • -x backend_address: 启动代理模式Q需要填写[ip:port]l合形式地址Q支持多个代理地址
    • 默认不开?
  • -v: 启用详细l计数据输出
    • 默认为禁?
  • -d: 启动Debug调试模式Q调试信息被写入日志文g?
    • 默认用
  • -k: 启用HTTP keepalive机制Q当前只能够工作在服务器模式?
    • 默认被禁?

实例

在运行之前,需要注意两点:(x)

  • Z(jin)跑满CPUQ需要确保客L(fng)和后端服务器都不应该成ؓ(f)瓉Q两U可行方案:(x)
    • 提供_多机器用以充当客L(fng)和后端服务器角色
    • 或在一台机器上充当客户端和后端服务器,使用fastsocketQ推荐方案,较ؓ(f)节省服务器)(j)
  • 正确配置|卡Q若不知道如何做Q可以参考源码中script目录

服务器模式示?/h4>

服务器模式至需要两C机:(x)

  • LA作ؓ(f)客户端生HTTPh
  • LB为Web服务?

讑֮每台LCPU 12核,|络大概讄如下Q?/p>

 +--------------------+     +--------------------+
 |       Host A       |     |      Host B        |
 |                    |     |                    |
 |    10.0.0.1/24     |-----|    10.0.0.2/24     |
 |                    |     |                    |
 +--------------------+     +--------------------+

下面是运行两C机的步骤Q?/p>

LBQ?/p>

  • Web服务器模式单独运行,开?2个工作进E,和CPU核心(j)C_(d)(x)

    ./server -w 12 -a 10.0.0.2:80

  • 或者测试借助于Fastsocket所带来的性能

    LD_PRELOAD=../library/libfsocket.so ./server -w 12 -a 10.0.0.2:80

LAQ?/p>

  • q行Apache abE序作ؓ(f)h?br />ab -n 1000000 -c 100 http://10.0.0.2:80/
  • 单个Apache abE序不能够体现服务器负蝲能力Q多个ab实例同时q发q行可能?x)好很多Q开12个实例和CPU核心(j)C_(d)(x) N=12; for i in $(seq 1 $N); do ab -n 1000000 -c 100 http://10.0.0.2:80/ > /dev/null 2>&1; done

代理模式C

代理模式下,需要三台机器:(x)

  • LA作ؓ(f)客户端生HTTPh
  • LB作ؓ(f)代理角色
  • LC则需要后端服务器

讑֮每台机器CPU内核?2Q网l结构如下:(x)

 +--------------------+     +--------------------+     +--------------------+
 |       Host A       |     |       Host B       |     |       Host C       |
 |                    |     |                    |     |                    |
 |    10.0.0.1/24     |     |    10.0.0.2/24     |     |     10.0.0.3/24    |
 +---------+----------+     +---------+----------+     +----------+---------+
           |                          |                           |
 +---------+--------------------------+---------------------------+---------+
 |                                 switch                                   |
 +--------------------------------------------------------------------------+

下面为具体的q行步骤Q?/p>

LBQ?/p>

  • Z理服务器启动12个进E?br />./server -w 12 -a 10.0.0.2:80 -x 10.0.0.3:80
  • 或者以Fastsocket方式启动 LD_PRELOAD=../library/libsocket.so ./server -w 12 -a 10.0.0.2:80 -x 10.0.0.3:80

LCQ?/p>

  • 理论上Q何WEB服务器都可以充当后端服务器,q里充分利用CE序好了(jin)Q?br />./server -w 12 -a 10.0.0.3:80

LAQ?/p>

  • 作ؓ(f)客户端请求生成器Q同样启?2个Apache ab实例Q?br />N=12; for i in $(seq 1 $N); do ab -n 1000000 -c 100 http://10.0.0.2:80/ > /dev/null 2>&1; done

动手实践

以上译完毕Q下面将是根据上面内容进行动手测试描q吧?/p>

安装Apache ab命o(h)

(g)查一下包含Apache ab命o(h)的Y件包Q?/p>

yum provides /usr/bin/ab

可以看到cM于如下字P(x)

httpd-tools-2.2.15-39.el6.centos.x86_64 : Tools for use with the Apache HTTP Server

安装它就可以?/p>

yum install httpd-tools

虚拟机测?/h4>

Windows 7专业版跑VMware Workstation 10.04虚拟机,两个Centos 6.5pȝQ配|一_(d)2G内存Q?个CPU逻辑处理器核?j)?/p>

客户端安装Apache ab命o(h)试Q跑8个实例:(x) for i in $(seq 1 8); do ab -n 10000 -c 100 http://192.168.192.16:80/ > /dev/null 2>&1; done

服务器端Q分别记录:(x)
/opt/fast/server -w 8 LD_PRELOAD=../library/libfsocket.so ./server -w 8

服务器模式对?/h4>

两组数据Ҏ(gu)Q?/p>
q行方式 处理消耗时?U? 处理L q_每秒处理?/th> 最大?/th>
单独q行 34s 80270 2361 2674
加蝲fasocket 28s 80399 2871 2964

代理模式数据

试方式如上Q三台服务器Q测试端+代理?服务器端Q配|一栗第一ơ代理单独启动,W二ơ代理预加蝲fastsocket方式?/p>
q行方式 处理消耗时?U? 处理L q_每秒处理?/th> 最大?/th>
W一ơ测试后?/td> 44s 80189 1822 2150
W一ơ测试代?/td> 44s 80189 1822 2152
W二ơ测试后?/td> 42s 80051 1906 2188
W二ơ测试代?/td> 42s 80051 1906 2167

备注Q虚拟机上数据,不代表真实服务器上数据,仅供参考?/p>

虽然Z虚拟机,试环境受限Q但一样可以看到基于fastsocket服务器模型,处理性能有所提升QM处理旉Q每U^均处理数Q以?qing)处理上限等?/p>

关于LD_PRELOAD注意事项

动态链接预先加载LD_PRELOAD虽是利器Q但不是万能药,LD_PRELOAD遇到下面情况?x)失效?x)

  • ?rn)态链接用gcc -static参数把libc.so.6?rn)态链入执行程序中
  • 讄执行文g的SUID权限Q可能也?x)导致LD_PRELOAD失效Q如Qchmod 4755 daemonQ?

情况很复杂,心(j)Z?/p>

学习(fn)q测试了(jin)fastsocket的源码示范部分,前后Ҏ(gu)可以看到fastsocket带来?jin)处理性能的提升?/p>

nieyong 2015-01-29 17:16 发表评论
]]>
Fastsocket学习(fn)W记之开?/title><link>http://www.tkk7.com/yongboy/archive/2015/01/29/422536.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Thu, 29 Jan 2015 06:11:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2015/01/29/422536.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/422536.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2015/01/29/422536.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/422536.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/422536.html</trackback:ping><description><![CDATA[<div id="gewg42u" class="wrap"> <h3 id="-">前言</h3> <p>以前在infoq上看到fastsocket的宣?a >《两周内在Github上收?800+个星Q内核层|络栈优化项目Fastsocket背后的故事?/a>Q明白了(jin)fastsocket是什么:(x)</p> <ul> <li>高度可扩展的socket </li><li>是Linux内核层面的底层网l实? </li><li>在多核机器上可实现极x(chng)能Q?4总内的性能增长呈线性,q超q默认内核在12总上的机器׃(x)出现性能下降的情? </li><li>非常Ҏ(gu)使用和维护,应用代码无需变更 </li><li>针对kernel-2.6.32-431.17.1.el6/CentOS-6.5的实? </li><li>已经在新的生环境部v </li><li>由新的操作pȝ团队发v </li><li>清华大学操作pȝ实验室、Intel、哲思自pY件社区(ZeuuxQ对该项目均有支? </li><li> <p>开源协议ؓ(f)GPLv2</p> <p>M很吸引hQ从内核层面q行优化TCP/IP|络堆栈Q上层网l应用程序不用做修改Q就可以得到处理性能的提升,很赞Q?/p></li></ul> <h3 id="fastsocket-">Fastsocket学习(fn)W记目录</h3> <p>q期有点空Ԍ开始对Fastsocketq行x(chng)Q虽然资料不多,但也记录?jin)几连l的学习(fn)W记。大部分W记Q思\主要是优先翻译官Ҏ(gu)档,紧接着?x)夹带些个h一些学?fn)笔记?/p> <p>fastsocket目地址是:(x)<a >https://github.com/fastos/fastsocket</a>Q其wiki和代码是本系列笔C要来源。一开始想q一步全面认知fastsocketQ发现无从下手,只能从侧面开始一一旁敲侧击Q逐渐加深。本pdW记Ҏ(gu)其源码目录结构划分特性,分开记录学习(fn):</p> <ul> <li><a href="http://www.tkk7.com/yongboy/archive/2015/01/30/422579.html">~译安装?/a> </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/01/29/422550.html">C应用?/a>Q对应demo目录 </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/01/30/422592.html">|卡讄?/a>Q对应scripts目录 </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/02/02/422658.html">动态链接库?/a>Q对应library目录 </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/02/03/422694.html">内核模块?/a>Q对应module目录Q实际上是kernel/net/fastsocket目录 </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/02/04/422732.html">内核?/a>Q对应kernel目录Q也是内核模块篇 </li><li><a href="http://www.tkk7.com/yongboy/archive/2015/02/05/422760.html">结?/a></li></ul> <p>怎么说呢Q能力有限,若发现问?U漏Q请帮忙?qing)时指正Q不胜感Ȁ?/p> <h3 id="-">其它</h3> <p>代码贡献者,除了(jin)<a >林晓?/a>之外Q目前提交最为频J的?a >greewind</a>同学Q其博客地址?a >http://blog.chinaunix.net/uid/23629988.html</a>Q也是一位牛人?/p> <p>优秀的开源项目,L可以吸引到最优秀的开发者?/p></div><img src ="http://www.tkk7.com/yongboy/aggbug/422536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2015-01-29 14:11 <a href="http://www.tkk7.com/yongboy/archive/2015/01/29/422536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从网l游戏中学习(fn)如何处理延迟http://www.tkk7.com/yongboy/archive/2014/12/23/421672.htmlnieyongnieyongTue, 23 Dec 2014 02:02:00 GMThttp://www.tkk7.com/yongboy/archive/2014/12/23/421672.htmlhttp://www.tkk7.com/yongboy/comments/421672.htmlhttp://www.tkk7.com/yongboy/archive/2014/12/23/421672.html#Feedback1http://www.tkk7.com/yongboy/comments/commentRss/421672.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/421672.html前言

|络延迟是客观存在的Q但|络游戏行业已经U篏?jin)大量优质经验,使用一些策略、技术手D在客户端消?隐藏掉gq带来的不便Q以可能的掩盖实际存在的gӞ同时实现实时渲染Q将用户带入快速的交互式实时游戏中Q体验完的互动׃中?/p>

q样处理l果Q稍高gq的玩家也不?x)因为网l不是那么好Q也能够很和谐的与其它网l参差不?qing)玩家一h戏中?/p>

虽然延时军_?jin)实时游戏的最低反应时_(d)但最重要的是客户端看h要流畅。第一人称设计游戏QF(tun)PSQ可巧妙的化解与规避Q最l在适合普遍用户|络环境?200ms)Q实现实时快速互动游戏?/p>

嗯,下面是q期脑补l果?/p>

|游P2P & CSl构

早先|游使用P2P|络拓扑在玩家之间进行交换数据通信。但P2P模型引v的高延迟在FPS游戏中无法被很好掩盖Q所有玩家的延迟取决于当前玩家中延迟最烂的那个。好比木桶理论,低gq网l好的玩家会(x)被高延迟坏网l的玩家拖篏。最l结果导_(d)所有玩安不太开?j)?jin)。但在局域网环境下,不会(x)感觉到gq带来的问题。另Q游戏逻辑大部分都集中在客L(fng)?jin),很难避免作弊行?f)?/p>

C/Sl构|游Q?/p>

  • C/Sl构在服务器端跑所有的游戏逻辑和输入响应,客户端只需要渲染以?qing)把自己需要一些状态同步下来,把用戯入发l服务器端,然后昄l果可以了(jin)
  • C/Sl构|游最大优点就是把延迟从玩家之间最卡玩家的延迟改变?sh)玩家和服务器连接的延迟Q结果就是客L(fng)在带宽上的要求也低了(jin)不少Q因为只需要把输入发给服务器端׃?qing)接收服务器响应够?
  • C/Sl构|游虽然转移?jin)网lgq矛盄Q但现实|络环境一样会(x)带来较高的网lgq。客L(fng)每执行一ơ操作,都需要等待服务器端命令,那会(x)用户操作?x)造成操纵卡顿现象。如何解军_Q客L(fng)一般采用预和插值等方式在渲染层隐藏|络延迟

客户端预和插?/h3>

服务器可以允许某些情况下客户端本地即时执行移动操作,q种Ҏ(gu)可以UCؓ(f)客户端预?/p>

比如游戏中键盘控制角色行赎ͼq个时候可以在很小的时间段Q时间很短,比如1-3U)(j)内预用戯动轨q(方向+加速度Q角色行走结果)(j)Q这部分的命令客L(fng)?x)全部发送到服务器端校验正确与否Q避免瞬间{Uȝ外挂Q。但客户端预有时也不是癑ֈ癑և,需要服务器q行U正Q所谓服务器是上帝QThe sever is the manQ)(j)。纠正结果可能就是游戏角色行走轨q和客户端预轨qҎ(gu)所偏差Q客L(fng)可以使用插值方式(_略来讲Q就是角色在两点之间Ud渲染的方式)(j)渲染游戏角色在游戏世界中的位|{Ud^滑一些,避免游戏角色从一个位|瞬间拉回到另一个位|,让h有些莫名其妙?/p>

插|有h也称之ؓ(f)路径补偿Q都是一回事。插值的Ҏ(gu)?x)涉及(qing)到很多数学公式Q线性插倹{三ơ线性插值等Q比如这文章所讲到?a >插值那些事?/p>

结Q客L(fng)预测Q服务器端纠正,客户端采用插值方式微调?/p>

针对交互的一玩Ӟ|络好坏层次不齐Q游戏的一些操作效果可能需?#8221;延迟补偿“{略q行

延迟补偿

延迟补偿是游戏服务器端执行的一U策略,处理用户命o(h)回退到客L(fng)发送命令的准确旉Qgq导_(d)(j)Q根据客L(fng)的具体情况进行修正,以牺牲游戏在伤害判定斚w的真实感来I补攻击行为等斚w真实感,本质上是一U折?sh)选择?/p>

主要注意Qgq补偿不是发生在客户端?/p>

关于延迟补偿的一个例子:(x)

  1. 在FPS游戏中,玩家A?0.5U时向目标对象玩家B击q且MQ射M息被打包发送(|络延迟100毫秒Q,服务器于10.6U收刎ͼ此时玩家B可能已跑到另外一个位|?
  2. 若服务器仅仅Z接收时刻Q?0.6U)(j)q行判断Q那么玩家B没有收到伤害Q或许可能会(x)M玩家B后面紧跟的玩家CQ?00ms后玩家C完全由可能已处于玩家A的射ȝ标位|)(j)
  3. Z(jin)弥补׃延迟造成的问题,服务器端需要引?#8220;延迟补偿”{略用于修正因gq造成错ؕ假象
  4. 服务器计执行设计命令时_(d)然后扑և当前世界10.5U时ȝ家信息,Ҏ(gu)击法模拟得出是否命中判断Q以辑ֈ可能精?

若游戏gq补偿被用Q那么就?x)有许多玩家抱怨自己明明打中了(jin)Ҏ(gu)却没有造成M伤害。?/p>

有所得,有所失:(x)但这对低延时玩家貌似有些不公qIUd速度快,可能已经跑到角落里ƈ且已y在一个箱子后面隐藏v来时被对手击中的错觉Q子Ҏ(gu)视掩体,玩家隔着墙被击Q,实有些不乐意?/p>

延迟补偿Q网l高延迟的玩家有利,低gq的玩家优势可能?x)被降低Q低延迟玩家利益受损Q,但对l护游戏世界的^衡还是有利的?/p>

Ҏ(gu)&阀?/h3>

客户端和服务器需要对Ӟ互相知道彼此延迟情况Q比如云风定义的某个步骤Q?/p>

客户端发送一个本地时间量l服务器Q服务收到包后,夹带一个服务器旉q回l客L(fng)。当客户端收到这个包后,可以估算出包在\E上l过的时间。同时把本地新时间夹带进去,再次发送给服务器。服务器也可以进一步的?jin)解响应旉?/p>

C/S两端通过cM步骤q行计算彼此延时/时差Q同时会(x)对实时同步设|一个阀|比如对gq低?0msQ?.01U)(j)的交互认为是x(chng)同步发生Q不?x)认为是延迟?/p>

UDP或TCP

不同cd的游戏会(x)钟爱不同的协议呢Q不一而Q?/p>

  • 客户端间歇性的发v无状态的查询Qƈ且偶?dng)发生gq是可以容忍Q那么用HTTP/HTTPS?
  • 客户端和服务器都可以独立发包Q偶?dng)发生gq可以容忍(比如Q在U的U牌游戏Q许多MMOcȝ游戏Q,那么使用TCP长连接吧
  • 客户端和服务器都可以独立发包Q而且无法忍受延迟Q比如:(x)大多数的多hFPS动作cL戏Quake、CS{,以及(qing)一些MMOcL戏)(j)Q那么用UDP?

TCP?x)认定丢包是因?f)本地带宽不DQ本地带宽不x(chng)丢包的一部分原因Q,但国内ISP可能?x)在自n机房|络拥挤时丢弃数据包Q这时候可能需要快速发包争抢通道Q而非TCPH口收羃QUDP没有TCPH口收羃的负担,可以很容易做到这一炏V?/p>

要求实时性放在第一位的FPS游戏QegQQuakeQCSQ,q域|一般采用UDPQ因可容许有丢失数据包存在(另客L(fng)若等待一D|间中间丢包,可以通过插值等手段忽略掉)(j)Q一旦检到可以快速发送,另不涉及(qing)到重发的时候UDP比TCP要快一点嘛。但?x)在UDP应用层面有所增加协议控制Q比如ACK{?/p>

很多时候协议؜用,比如MMO客户端也?dng)R先用HTTP去获取上一ơ的更新内容Q?重要信息如角色获得的物品和经验需要通过TCP传输Q而周围h物的动向、NPCUd、技能动L令等则可以用UDP传输Q虽然可能丢包,但媄(jing)响不大?/p>

|游通过客户端预、插值和服务器端延迟补脓(chung){,化解/消除用户端网lgq造成的停ѝ我们虽然可能没有机?x)接触游戏开发,学习(fn)跨界的优良经验和实践Q说不准?x)对当前工作某些业务点的处理有所启发呢?/p>

本集由韩国宇航局赞助播出Q我们要去远方看看,q有什么是我们的思密达?------ 《万万没惛_》王大锤



nieyong 2014-12-23 10:02 发表评论
]]>
随手CAndroid|络调试要记?/title><link>http://www.tkk7.com/yongboy/archive/2014/11/20/420371.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Thu, 20 Nov 2014 14:05:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2014/11/20/420371.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/420371.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2014/11/20/420371.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/420371.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/420371.html</trackback:ping><description><![CDATA[<p>最q一D|_(d)Ud2G/3G客户端连接成功率不高Q着实让人头疹{?/p> <p>说是Android|络调试Q其实也不过是在被ROOT后Androidpȝ操作Q用adb shell执行一些常规的l端命o(h)Q检?G/3G/4G/WIFI|络{,q而确定一些因|络{导致的问题而已。但adb shell默认没有几个支持的命令,比如 <code>cat</code>, <code>tcpdump</code>Q这些都是最基本的必备命令,也不支持。对于想要查看网l请求有几次跌{Q不借助些外力,实是g很不可能的事情?/p> <p>基本会(x)包含如下内容Q?/p> <blockquote> <ul> <li>如何安装需要的Linuxl端命o(h)tcpdump,mtr </li><li>调试2G/3G{网l连通,域名h跌{ </li><li>h丢包情况 </li></ul></blockquote> <h3>Androidl端扩展器<strong>opkg</strong></h3> <p>说它是神器,一炚w不夸张。HomepageQ?http://dan.drown.org/android/)Q上开明义:(x)</p> <blockquote> <p>Unix command-line programs ported to run on android. This project uses opkg, which handles downloading and installing packages and their dependencies (like yum or apt). Source for all packages are available.</p></blockquote> <p>作?strong>Dan</strong> (http://blog.dan.drown.org/)为我们移植到Androidq_Qƈ且还?sh)我们编译好相当多的常用E序Q具体支持列表,可从<code>Changelog</code>(http://dan.drown.org/android/)中找刎ͼq里不再累述?/p> <p>十分隑־Q由h谢?/p> <h3>下蝲opkg?/h3> <p>预先把依赖下载到本地:</p> <blockquote> <p>http://dan.drown.org/android/system/xbin/busybox <br />http://dan.drown.org/android/opkg.tar.gz</p></blockquote> <h3>安装opkg</h3> <p>讑֮装到Android手机?/data/local 目录Q那么首先需要确保这个目录具有可d权限?/p> <blockquote> <p>记得要用su命o(h)切换到root理员̎P操作、权限才不会(x)受阻?/p></blockquote><pre><code>adb shell chmod 777 /data/local </code></pre> <p>拯opkg?data/local目录</p><pre><code>adb push busybox /data/local adb push opkg.tar.gz /data/local </code></pre> <p>adb shellq去之后Q开始编译安装:(x)</p><pre><code>cd /data/local chmod 777 busybox ./busybox tar zxvf opkg.tar.gz </code></pre> <p>讄环境变量Q?/p><pre><code>export PATH=$PATH:/data/local/bin </code></pre> <p>执行更新、安装准?/p><pre><code>opkg update opkg install opkg opkg list # 可以查看可以支持安装的终端应用程?命o(h)) </code></pre> <p>话说Qopkg可以应用于各U嵌入式环境中,强的说?/p> <h4>安装linuxl端应用/命o(h)</h4> <p>可以一口气安装几个试试Q?/p><pre><code>opkg install mtr curl tcpdump cat </code></pre> <p>当然Q你也可以一个一个安装?/p> <p>安装好之后呢Q就是直接运行应?命o(h)?jin),试baidu.com域名解析、丢包情c(din)?/p> <blockquote> <p>mtr -r baidu.com HOST: localhost Loss% Snt Last<br />Avg Best Wrst StDev<br />1.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0<br />2.|-- 192.168.61.1 0.0% 10 504.3 635.0 339.3 1024. 238.7<br />3.|-- 192.168.63.138 0.0% 10 392.9 588.7 298.5 847.7 220.3<br />4.|-- 221.130.39.106 0.0% 10 340.9 557.3 257.4 823.5 211.7<br />5.|-- 221.179.159.45 10.0% 10 649.6 631.4 332.6 821.4 165.0<br />6.|-- 111.13.14.6 10.0% 10 561.9 551.3 268.2 777.0 170.0<br />7.|-- 111.13.0.162 10.0% 10 510.6 570.6 385.5 767.6 116.6<br />8.|-- 111.13.1.14 10.0% 10 775.4 565.2 377.7 775.4 130.9<br />9.|-- 111.13.2.130 10.0% 10 707.2 564.6 381.1 887.3 173.4</p></blockquote> <p>嗯,通过mtr实很容易就看出Q网l蟩敎ͼ每一个节点丢包率。这样就能很Ҏ(gu)扑ֈ在移?G/3G|络q接时比较严重的问题所在。下面就是希望运l的同学快处理好,避免再次出现p通机房再ơ蟩转到Ud机房问题?/p> <blockquote> <p>非常感谢陈杰同学推荐的比ping+tracerouteq要好用命o(h)mtr。一旦拥有,不会(x)放手Q?/p></blockquote> <h4>Ud2G/3G下网l抓?/h4> <p>要想抓取2G/3G|络下数据包Q必d装一个tcpdump命o(h)Q?/p><pre><code>opkg install tcpdump </code></pre> <p>opkg很脓(chung)?j)的会(x)把所依赖的libpcap也都一q安装上Q完全不用担?j)版本问题?/p><pre><code>tcpdump -i any -p -vv -s 0 -w /sdcard/capture.pcap </code></pre> <p>下面是一气呵成的导出Q用wiresharkq行分析?jin)?/p><pre><code>adb pull /sdcard/tmp1.pcap c:/tmp </code></pre> <h3>其它有利于诊断网l的APP</h3> <p>不习(fn)惯用终端诊断网l,可以直接使用现成的APP?/p> <ol> <li>W一?FingQ大名如雯耻I跨Android、IOSq_QDNS、PING{不在话下,居家生活之必? </li><li>W二名嘛Q暂时还没有发现? </li><li>shark for rootQ也不错QAndroidq_推荐 </li><li>|速测试,可以看到当前|络的gq等Q也不错 </li></ol> <p>有更好的APP推荐Q欢q推荐一二?/p> <h3>结</h3> <ol> <li>希望可以l遇到同样问题的同学一些帮? </li><li>记录下来便于以后索引 </li></ol><img src ="http://www.tkk7.com/yongboy/aggbug/420371.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2014-11-20 22:05 <a href="http://www.tkk7.com/yongboy/archive/2014/11/20/420371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Z么批量请求要可能的合ƈ操作http://www.tkk7.com/yongboy/archive/2014/11/09/419829.htmlnieyongnieyongSun, 09 Nov 2014 14:08:00 GMThttp://www.tkk7.com/yongboy/archive/2014/11/09/419829.htmlhttp://www.tkk7.com/yongboy/comments/419829.htmlhttp://www.tkk7.com/yongboy/archive/2014/11/09/419829.html#Feedback16http://www.tkk7.com/yongboy/comments/commentRss/419829.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/419829.html前言

U上情况Q?/p>

  1. U上Redis集群Q多个Twemproxy代理QnutcrackerQ,LVS DR路由均衡调度
  2. 客户端用Jedis操作Redis集群Q一个程序进E实例用原?024个工作线E处理请求,若干个进E实?
  3. 一天超q?2亿次hQ网l一般情况下Q一天超q上万个q接p|异常
  4. q维同学告知QLVS压力较大

改进工作Q?/p>

  1. 工作U程由原?024改用16?
  2. 每个U程每次最多操?000个Redis命o(h)扚w提交

实际效果Q?/p>

  1. 一天不C亿次的请求量
  2. LVS压力大减
  3. CPU压力降低到原?/3以下
  4. 单个h抽样调研q_减少1-90毫秒旉Q尤其是跨机房处理)(j)

Redis支持扚w提交

原生支持扚w操作方式

一般命令前~若添加上m字符Ԍ表示支持多个、批量命令提交了(jin)?/p>

昑ּ?..

MSET key value [key value ...]
MSETNX key value [key value ...]

HMGET key field [field ...]
HMSET key field value [field value ...]

一般方式的...

HDEL key field [field ...]
SREM key member [member ...]
RPUSH key value [value ...]
......

更多Q请参考:(x)http://redis.cn/commands.html

pipeline道方式

官方文档Q?a >http://redis.io/topics/pipelining

  1. Redis Client把所有命令一h包发送到Redis ServerQ然后阻塞等待处理结?
  2. Redis Server必须在处理完所有命令前先缓存v所有命令的处理l果
  3. 打包的命令越多,~存消耗内存(sh)多
  4. 不是打包的命令越多越?
  5. 实际环境需要根据命令执行时间等各种因素选择合ƈ命o(h)的个敎ͼ以及(qing)试效果{?

Java队列支持

一般业务、接入前端请求量q大Q生产者速度q快Q这时候用队列暂时缓存(sh)(x)比较好一些,消费者直接直接从队列获取dQ通过队列让生产者和消费者进行分这也是业界普通采用的方式?/p>

监控队列

有的时候,若可以监控一下队列消Ҏ(gu)况,可以监控一下,很直观。同事ؓ(f)队列d?jin)一个监控线E,清晰明了(jin)?jin)解队列消费情况?/p>

C

C使用?jin)Redis PipelineQ线E池Q准备数据,生?消费者队列,队列监控{,消费完毕Q程序关闭?/p>

/**
 * 以下试在Jedis 2.6下测试通过
 * 
 * @author nieyong
 * 
 */
public class TestJedisPipeline {
    private static final int NUM = 512;
    private static final int MAX = 1000000; // 100W

    private static JedisPool redisPool;
    private static final ExecutorService pool = Executors.newCachedThreadPool();
    protected static final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(
            MAX); // 100W
    private static boolean finished = false;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxActive(64);
        config.setMaxIdle(64);

        try {
            redisPool = new JedisPool(config, "192.168.192.8", 6379, 10000,
                    null, 0);
        } catch (Exception e) {
            System.err.println("Init msg redis factory error! " + e.toString());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("prepare test data 100W");
        prepareTestData();
        System.out.println("prepare test data done!");

        // 生者,模拟h100W?
        pool.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < MAX; i++) {
                    if (i % 3 == 0) {
                        queue.offer("del_key key_" + i);
                    } else {
                        queue.offer("get_key key_" + i);
                    }
                }
            }
        });

        // CPU核数*2 个工作者线E?
        int threadNum = 2 * Runtime.getRuntime().availableProcessors();

        for (int i = 0; i < threadNum; i++)
            pool.execute(new ConsumerTask());

        pool.execute(new MonitorTask());

        Thread.sleep(10 * 1000);// 10sec
        System.out.println("going to shutdown server ...");
        setFinished(true);
        pool.shutdown();

        pool.awaitTermination(1, TimeUnit.MILLISECONDS);

        System.out.println("colse!");
    }

    private static void prepareTestData() {
        Jedis redis = redisPool.getResource();
        Pipeline pipeline = redis.pipelined();

        for (int i = 0; i < MAX; i++) {
            pipeline.set("key_" + i, (i * 2 + 1) + "");

            if (i % (NUM * 2) == 0) {
                pipeline.sync();
            }
        }
        pipeline.sync();
        redisPool.returnResource(redis);
    }

    // queue monitorQ生产?消费队列监控
    private static class MonitorTask implements Runnable {

        @Override
        public void run() {
            while (!Thread.interrupted() && !isFinished()) {
                System.out.println("queue.size = " + queue.size());
                try {
                    Thread.sleep(500); // 0.5 second
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
    }

    // consumerQ消费?
    private static class ConsumerTask implements Runnable {
        @Override
        public void run() {
            while (!Thread.interrupted() && !isFinished()) {
                if (queue.isEmpty()) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                    }

                    continue;
                }

                List<String> tasks = new ArrayList<String>(NUM);
                queue.drainTo(tasks, NUM);
                if (tasks.isEmpty()) {
                    continue;
                }

                Jedis jedis = redisPool.getResource();
                Pipeline pipeline = jedis.pipelined();

                try {
                    List<Response<String>> resultList = new ArrayList<Response<String>>(
                            tasks.size());

                    List<String> waitDeleteList = new ArrayList<String>(
                            tasks.size());

                    for (String task : tasks) {
                        String key = task.split(" ")[1];
                        if (task.startsWith("get_key")) {
                            resultList.add(pipeline.get(key));
                            waitDeleteList.add(key);
                        } else if (task.startsWith("del_key")) {
                            pipeline.del(key);
                        }
                    }

                    pipeline.sync();

                    // 处理q回列表
                    for (int i = 0; i < resultList.size(); i++) {
                        resultList.get(i).get();
                        // handle value here ...
                        // System.out.println("get value " + value);
                    }

                    // d完毕Q直接删除之
                    for (String key : waitDeleteList) {
                        pipeline.del(key);
                    }

                    pipeline.sync();
                } catch (Exception e) {
                    redisPool.returnBrokenResource(jedis);
                } finally {
                    redisPool.returnResource(jedis);
                }
            }
        }
    }

    private static boolean isFinished(){
        return finished;
    }

    private static void setFinished(boolean bool){
        finished = bool;
    }
}

代码作ؓ(f)C。若U上则需要处理一些异常等?/p>

若能够批量请求进行合q操作,自然可以节省很多的网l带宽、CPU{资源。有cM问题的同学,不妨考虑一下?/p>

nieyong 2014-11-09 22:08 发表评论
]]>
随手CLinux 2.6.32内核SYN flooding警告信息http://www.tkk7.com/yongboy/archive/2014/08/20/417165.htmlnieyongnieyongWed, 20 Aug 2014 12:43:00 GMThttp://www.tkk7.com/yongboy/archive/2014/08/20/417165.htmlhttp://www.tkk7.com/yongboy/comments/417165.htmlhttp://www.tkk7.com/yongboy/archive/2014/08/20/417165.html#Feedback3http://www.tkk7.com/yongboy/comments/commentRss/417165.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/417165.html前言

新申L(fng)服务器内ؓ(f)2.6.32Q原先的TCP Server直接在新内核的Linxu服务器上q行Q运行dmesg命o(h)Q可以看到大量的SYN flooding警告Q?/p>

possible SYN flooding on port 8080. Sending cookies.

原先?.6.18内核的参数在2.6.32内核版本情况下,单调?net.ipv4.tcp_max_syn_backlog"已经没有作用?/p>

怎么办,只能再次阅读2.6.32源码Q以下即是?/p>

最后小l处有直接结论,?j)急的你可以直接阅Lȝ好了(jin)?/p>

linux内核2.6.32有关backlog值分?/h3>

net/Socket.c:

SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
    struct socket *sock;
    int err, fput_needed;
    int somaxconn;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
        if ((unsigned)backlog > somaxconn)
            backlog = somaxconn;

        err = security_socket_listen(sock, backlog);
        if (!err)
            err = sock->ops->listen(sock, backlog);

        fput_light(sock->file, fput_needed);
    }
    return err;
}

net/ipv4/Af_inet.c:

/*
 *  Move a socket into listening state.
 */
int inet_listen(struct socket *sock, int backlog)
{
    struct sock *sk = sock->sk;
    unsigned char old_state;
    int err;

    lock_sock(sk);

    err = -EINVAL;
    if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
        goto out;

    old_state = sk->sk_state;
    if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
        goto out;

    /* Really, if the socket is already in listen state
     * we can only allow the backlog to be adjusted.
     */
    if (old_state != TCP_LISTEN) {
        err = inet_csk_listen_start(sk, backlog);
        if (err)
            goto out;
    }
    sk->sk_max_ack_backlog = backlog;
    err = 0;

out:
    release_sock(sk);
    return err;
}

inet_listen调用inet_csk_listen_start函数Q所传入的backlog参数改头换面Q变成了(jin)不可修改的常量nr_table_entries?jin)?/p>

net/ipv4/Inet_connection_sock.c:

int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
{
    struct inet_sock *inet = inet_sk(sk);
    struct inet_connection_sock *icsk = inet_csk(sk);
    int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries);

    if (rc != 0)
        return rc;

    sk->sk_max_ack_backlog = 0;
    sk->sk_ack_backlog = 0;
    inet_csk_delack_init(sk);

    /* There is race window here: we announce ourselves listening,
     * but this transition is still not validated by get_port().
     * It is OK, because this socket enters to hash table only
     * after validation is complete.
     */
    sk->sk_state = TCP_LISTEN;
    if (!sk->sk_prot->get_port(sk, inet->num)) {
        inet->sport = htons(inet->num);

        sk_dst_reset(sk);
        sk->sk_prot->hash(sk);

        return 0;
    }

    sk->sk_state = TCP_CLOSE;
    __reqsk_queue_destroy(&icsk->icsk_accept_queue);
    return -EADDRINUSE;
}

下面处理的是TCP SYN_RECV状态的q接Q处于握手阶D,也可以说是半q接Ӟ{待着q接方第三次握手?/p>

/*
 * Maximum number of SYN_RECV sockets in queue per LISTEN socket.
 * One SYN_RECV socket costs about 80bytes on a 32bit machine.
 * It would be better to replace it with a global counter for all sockets
 * but then some measure against one socket starving all other sockets
 * would be needed.
 *
 * It was 128 by default. Experiments with real servers show, that
 * it is absolutely not enough even at 100conn/sec. 256 cures most
 * of problems. This value is adjusted to 128 for very small machines
 * (<=32Mb of memory) and to 1024 on normal or better ones (>=256Mb).
 * Note : Dont forget somaxconn that may limit backlog too.
 */
int reqsk_queue_alloc(struct request_sock_queue *queue,
              unsigned int nr_table_entries)
{
    size_t lopt_size = sizeof(struct listen_sock);
    struct listen_sock *lopt;
    nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
    nr_table_entries = max_t(u32, nr_table_entries, 8);
    nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
    lopt_size += nr_table_entries * sizeof(struct request_sock *); 
    if (lopt_size > PAGE_SIZE)
        lopt = __vmalloc(lopt_size,
            GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
            PAGE_KERNEL);
    else
        lopt = kzalloc(lopt_size, GFP_KERNEL);
    if (lopt == NULL)
        return -ENOMEM;

    for (lopt->max_qlen_log = 3;
         (1 << lopt->max_qlen_log) < nr_table_entries;
         lopt->max_qlen_log++);

    get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd));
    rwlock_init(&queue->syn_wait_lock);
    queue->rskq_accept_head = NULL;
    lopt->nr_table_entries = nr_table_entries;

    write_lock_bh(&queue->syn_wait_lock);
    queue->listen_opt = lopt;
    write_unlock_bh(&queue->syn_wait_lock);

    return 0;
}

关键要看nr_table_entries变量Q在reqsk_queue_alloc函数中nr_table_entries变成?jin)无W号变量Q可修改的,变化受限?/p>

比如实际内核参数gؓ(f)Q?/p>

net.ipv4.tcp_max_syn_backlog = 65535

所传入的backlogQ不大于net.core.somaxconn = 65535Qؓ(f)8102Q那?/p>

// 取listen函数的backlog和sysctl_max_syn_backlog最|l果?102
nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
// 取nr_table_entries?q行比较的最大|l果?102
nr_table_entries = max_t(u32, nr_table_entries, 8);
// 可看?nr_table_entries*2Q结果ؓ(f)8102*2=16204
nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);

计算l果Qmax_qlen_log = 14

2.6.18内核中max_qlen_log的计方?/h4>
for (lopt->max_qlen_log = 6;
     (1 << lopt->max_qlen_log) < sysctl_max_syn_backlog;
     lopt->max_qlen_log++);
  1. 很显?dng)sysctl_max_syn_backlog参与?jin)运,sysctl_max_syn_backlog值很大的话会(x)Dmax_qlen_log值相Ҏ(gu)也很?
  2. 若sysctl_max_syn_backlog=65535Q那么max_qlen_log=16
  3. 2.6.18内核中半q接长度?^16=65536

作ؓ(f)listen_sockl构定义?jin)需要处理的处理半连接的队列元素个数为nr_table_entriesQ此例中?6204长度?/p>

/** struct listen_sock - listen state
 *
 * @max_qlen_log - log_2 of maximal queued SYNs/REQUESTs
 */
struct listen_sock {
    u8          max_qlen_log;
    /* 3 bytes hole, try to use */
    int         qlen;
    int         qlen_young;
    int         clock_hand;
    u32         hash_rnd;
    u32         nr_table_entries;
    struct request_sock *syn_table[0];
};

l描q而知Q?^max_qlen_log = 半连接队列长度qlen倹{?/p>

再回头看看报告SYN flooding的函敎ͼ(x)

net/ipv4/Tcp_ipv4.c

#ifdef CONFIG_SYN_COOKIES
static void syn_flood_warning(struct sk_buff *skb)
{
    static unsigned long warntime;

    if (time_after(jiffies, (warntime + HZ * 60))) {
        warntime = jiffies;
        printk(KERN_INFO
               "possible SYN flooding on port %d. Sending cookies.\n",
               ntohs(tcp_hdr(skb)->dest));
    }
}
#endif

被调用的处,已精若干代码Q?/p>

int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
......
#ifdef CONFIG_SYN_COOKIES
    int want_cookie = 0;
#else
#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
#endif
    ......
    /* TW buckets are converted to open requests without
     * limitations, they conserve resources and peer is
     * evidently real one.
     */
     // 判断半连接队列是否已?&& !0
    if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
#ifdef CONFIG_SYN_COOKIES
        if (sysctl_tcp_syncookies) {
            want_cookie = 1;
        } else
#endif
        goto drop;
    }

    /* Accept backlog is full. If we have already queued enough
     * of warm entries in syn queue, drop request. It is better than
     * clogging syn queue with openreqs with exponentially increasing
     * timeout.
     */
    if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
        goto drop;

    req = inet_reqsk_alloc(&tcp_request_sock_ops);
    if (!req)
        goto drop;

    ......

    if (!want_cookie)
        TCP_ECN_create_request(req, tcp_hdr(skb));

    if (want_cookie) {
#ifdef CONFIG_SYN_COOKIES
        syn_flood_warning(skb);
        req->cookie_ts = tmp_opt.tstamp_ok;
#endif
        isn = cookie_v4_init_sequence(sk, skb, &req->mss);
    } else if (!isn) {
        ......
    }       
    ......
}

判断半连接队列已满的函数很关键,可以看看q算法则Q?/p>

include/net/Inet_connection_sock.h:

static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
{
    return reqsk_queue_is_full(&inet_csk(sk)->icsk_accept_queue);
}

include/net/Rquest_sock.h:

static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)
{
    // 向右UMmax_qlen_log个单?
    return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;
}

q回1Q自然表C半q接队列已满?/p>

以上仅仅是分析了(jin)半连接队列已满的判断条gQM应用E序所传入的backlog很关键,如值太,很容易得?.

?somaxconn = 128Qsysctl_max_syn_backlog = 4096Qbacklog = 511 则最l?nr_table_entries = 256Qmax_qlen_log = 8。那么超q?56个半q接的队列,257 >> 8 = 1Q队列已满?/p>

如何讄backlogQ还得需要结合具体应用程序,需要ؓ(f)其调用listenҎ(gu)赋倹{?/p>

Netty backlog处理

Tcp Server使用Netty 3.7 版本Q版本较低,在处理backlogQ若我们不手动指定backlog|JDK 1.6默认?0?/p>

有证如下Q?java.net.ServerSocket:

public void bind(SocketAddress endpoint, int backlog) throws IOException {
    if (isClosed())
        throw new SocketException("Socket is closed");
    if (!oldImpl && isBound())
        throw new SocketException("Already bound");
    if (endpoint == null)
        endpoint = new InetSocketAddress(0);
    if (!(endpoint instanceof InetSocketAddress))
        throw new IllegalArgumentException("Unsupported address type");
    InetSocketAddress epoint = (InetSocketAddress) endpoint;
    if (epoint.isUnresolved())
        throw new SocketException("Unresolved address");
    if (backlog < 1)
      backlog = 50;
    try {
        SecurityManager security = System.getSecurityManager();
        if (security != null)
        security.checkListen(epoint.getPort());
        getImpl().bind(epoint.getAddress(), epoint.getPort());
        getImpl().listen(backlog);
        bound = true;
    } catch(SecurityException e) {
        bound = false;
        throw e;
    } catch(IOException e) {
        bound = false;
        throw e;
    }
}

netty中,处理backlog的地方:(x)

org/jboss/netty/channel/socket/DefaultServerSocketChannelConfig.java:

@Override
public boolean setOption(String key, Object value) {
    if (super.setOption(key, value)) {
        return true;
    }

    if ("receiveBufferSize".equals(key)) {
        setReceiveBufferSize(ConversionUtil.toInt(value));
    } else if ("reuseAddress".equals(key)) {
        setReuseAddress(ConversionUtil.toBoolean(value));
    } else if ("backlog".equals(key)) {
        setBacklog(ConversionUtil.toInt(value));
    } else {
        return false;
    }
    return true;
}

既然需要我们手动指定backlog|那么可以q样做:(x)

bootstrap.setOption("backlog", 8102); // 讄大一些没有关p,pȝ内核?x)自动与net.core.somaxconn相比较,取最低?

相对比Netty 4.0Q有些不Q可参考:(x)http://www.tkk7.com/yongboy/archive/2014/07/30/416373.html

在linux内核2.6.32Q若在没有遭受到SYN floodingd的情况下Q可以适当调整Q?/p>

sysctl -w net.core.somaxconn=32768

sysctl -w net.ipv4.tcp_max_syn_backlog=65535

sysctl -p

另千万别忘记修改TCP Server的listen接口所传入的backlog|若不讄或者过,都会(x)有可能造成SYN flooding的警告信息。开始不妨设|成1024Q然后观察一D|间根据实际情况需要再慢慢往上调?/p>

无论你如何设|,最lbacklogD围ؓ(f)Q?/p>

backlog <= net.core.somaxconn

半连接队列长度约为:(x)

半连接队列长?≈ 2 * min(backlog, net.ipv4.tcpmax_syn_backlog)

另,若出现SYN floodingӞ此时TCP SYN_RECV数量表示半连接队列已l满Q可以查看一下:(x)

ss -ant | awk 'NR>1 {++s[$1]} END {for(k in s) print k,s[k]}'

感谢q维书坤伙提供的比较好用查看命令?/p>

nieyong 2014-08-20 20:43 发表评论
]]>
随手CLinux内核SYN flooding警告信息http://www.tkk7.com/yongboy/archive/2014/08/06/416647.htmlnieyongnieyongWed, 06 Aug 2014 13:57:00 GMThttp://www.tkk7.com/yongboy/archive/2014/08/06/416647.htmlhttp://www.tkk7.com/yongboy/comments/416647.htmlhttp://www.tkk7.com/yongboy/archive/2014/08/06/416647.html#Feedback5http://www.tkk7.com/yongboy/comments/commentRss/416647.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/416647.html前言

最q线上服务器Qdmesg?x)给Z些警告信息:(x)

possible SYN flooding on port 8080. Sending cookies.

初看以ؓ(f)是受到DOS拒绝性攻击,但仔l一分析Q一天量也就是在1000多条左右Q感觉上属于正常可接受范围?/p>

下面需要找出来源,以及(qing)原因Q以下内容基于Linux 2.6.18内核?/p>

警告输出源头

net/ipv4/Tcp_ipv4.c:

#ifdef CONFIG_SYN_COOKIES
static void syn_flood_warning(struct sk_buff *skb)
{
    static unsigned long warntime; // W一ơ加载初始化为零Q后lwarntime = jiffies

    if (time_after(jiffies, (warntime + HZ * 60))) {
        warntime = jiffies;
        printk(KERN_INFO
           "possible SYN flooding on port %d. Sending cookies.\n",
           ntohs(skb->h.th->dest));
    }
}
#endif

很显?dng)CONFIG_SYN_COOKIES在Linuxpȝ~译Ӟ已被讄true?/p>

time_after宏定义:(x)

#define time_after(a,b)     \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(b) - (long)(a) < 0))

两个无符L(fng)旉比较Q确定先后顺序?/p>

jiffies真nQ?/p>

# define jiffies    raid6_jiffies()

#define HZ 1000

......

static inline uint32_t raid6_jiffies(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec*1000 + tv.tv_usec/1000; // U?1000 + 微秒/1000
}

回过头来Q再看看syn_flood_warning函数Q?/p>

static void syn_flood_warning(struct sk_buff *skb)
{
    static unsigned long warntime; // W一ơ加载初始化为零Q后lwarntime = jiffies

    if (time_after(jiffies, (warntime + HZ * 60))) {
        warntime = jiffies;
        printk(KERN_INFO
           "possible SYN flooding on port %d. Sending cookies.\n",
           ntohs(skb->h.th->dest));
    }
}

warntime为staticcdQ第一ơ调用时被初始化为零Q下ơ调用就是上ơ的jiffiesg(jin)Q前后间隔DqHZ*60׃?x)输(gu)告信息?jin)?/p>

有关time_after和jiffiesQ分享几文章:(x)

http://wenku.baidu.com/view/c75658d480eb6294dd886c4e.html

http://www.360doc.com/content/11/1201/09/1317564_168810003.shtml

 

警告输出需要满的条g

注意观察want_cookie=1时的条g?/p>

net/ipv4/Tcp_ipv4.c:

int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
    struct inet_request_sock *ireq;
    struct tcp_options_received tmp_opt;
    struct request_sock *req;
    __u32 saddr = skb->nh.iph->saddr;
    __u32 daddr = skb->nh.iph->daddr;
    __u32 isn = TCP_SKB_CB(skb)->when; // when在tcp_v4_rcv()中会(x)被置?
    struct dst_entry *dst = NULL;
#ifdef CONFIG_SYN_COOKIES
    int want_cookie = 0;
#else
#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
#endif

    /* Never answer to SYNs send to broadcast or multicast */
    if (((struct rtable *)skb->dst)->rt_flags &
    (RTCF_BROADCAST | RTCF_MULTICAST))
        goto drop;

    /* TW buckets are converted to open requests without
     * limitations, they conserve resources and peer is
     * evidently real one.
     */
    // if(判断半连接队列已?&& !0)
    if (inet_csk_reqsk_queue_is_full(sk) && !isn) { 
#ifdef CONFIG_SYN_COOKIES
        if (sysctl_tcp_syncookies) { // net.ipv4.tcp_syncookies = 1
            want_cookie = 1;
        } else
#endif
        goto drop;
    }

    /* Accept backlog is full. If we have already queued enough
     * of warm entries in syn queue, drop request. It is better than
     * clogging syn queue with openreqs with exponentially increasing
     * timeout.
     */
    // if(q接队列是否已满 && 半连接队列中q有未重传ACK半连接数?> 1) 
    if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
        goto drop;

    ......

    tcp_openreq_init(req, &tmp_opt, skb);

    ireq = inet_rsk(req);
    ireq->loc_addr = daddr;
    ireq->rmt_addr = saddr;
    ireq->opt = tcp_v4_save_options(sk, skb);
    if (!want_cookie)
        TCP_ECN_create_request(req, skb->h.th);

    if (want_cookie) { // 半连接队列已满会(x)触发
#ifdef CONFIG_SYN_COOKIES
        syn_flood_warning(skb);
#endif
        isn = cookie_v4_init_sequence(sk, skb, &req->mss);
    } else if (!isn) {
        ......
    }
    /* Kill the following clause, if you dislike this way. */
    // net.ipv4.tcp_syncookies未设|情况下Qsysctl_max_syn_backlog发生的作?
    else if (!sysctl_tcp_syncookies &&
             (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
              (sysctl_max_syn_backlog >> 2)) &&
             (!peer || !peer->tcp_ts_stamp) &&
             (!dst || !dst_metric(dst, RTAX_RTT))) {
            /* Without syncookies last quarter of
             * backlog is filled with destinations,
             * proven to be alive.
             * It means that we continue to communicate
             * to destinations, already remembered
             * to the moment of synflood.
             */
            LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open "
                   "request from %u.%u.%u.%u/%u\n",
                   NIPQUAD(saddr),
                   ntohs(skb->h.th->source));
            dst_release(dst);
            goto drop_and_free;
        }

        isn = tcp_v4_init_sequence(sk, skb);
    }
    tcp_rsk(req)->snt_isn = isn;

    if (tcp_v4_send_synack(sk, req, dst))
        goto drop_and_free;

    if (want_cookie) {
        reqsk_free(req);
    } else {
        inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
    }
    return 0;

drop_and_free:
    reqsk_free(req);
drop:
    return 0;
}

MQ如pȝ出现Q?/p>

possible SYN flooding on port 8080. Sending cookies.

若量不大Q是在提醒你需要关?j)一下sysctl_max_syn_backlog其值是否过?

sysctl -a | grep 'max_syn_backlog'

不妨成倍增加一?/p>

sysctl -w net.ipv4.tcp_max_syn_backlog=8192

sysctl -p

若进E无法做到重新加载,那就需要重启应用,以适应新的内核参数。进而持l观察一D|间?/p>

貌似tcp_max_syn_backlog参数其完整作用域q没有理解完_(d)下次有时间再写吧?/p>

nieyong 2014-08-06 21:57 发表评论
]]>
随手CLinux内核BacklogW记http://www.tkk7.com/yongboy/archive/2014/07/30/416373.htmlnieyongnieyongWed, 30 Jul 2014 09:22:00 GMThttp://www.tkk7.com/yongboy/archive/2014/07/30/416373.htmlhttp://www.tkk7.com/yongboy/comments/416373.htmlhttp://www.tkk7.com/yongboy/archive/2014/07/30/416373.html#Feedback5http://www.tkk7.com/yongboy/comments/commentRss/416373.htmlhttp://www.tkk7.com/yongboy/services/trackbacks/416373.html零。前a

有些东西L很容易遗忘,一时记得了(jin)Q过两天q正还l周公了(jin)。零零碎的不如一q记下来Q以后可以直接拿q来查询卛_?/p>

以下内容ZLinux 2.6.18内核?/p>

一。listenҎ(gu)传入的backlog参数Qnet.core.somaxconn

q个参数具体意义Q先看看Linux Socket的listen解释

man listen

   #include <sys/socket.h>

   int listen(int sockfd, int backlog);

intcd的backlog参数QlistenҎ(gu)的backlog意义为,已经完成三次握手、已l成功徏立连接的套接字将要进入队列的长度?/p>

一般我们自己定义设定backlog|若我们设|的backlog值大于net.core.somaxconn|被|ؓ(f)net.core.somaxconn值大。若不想直接性指定,跟随pȝ讑֮Q则需要读?proc/sys/net/core/somaxconn?/p>

net\Socket.c :

/*
 *  Perform a listen. Basically, we allow the protocol to do anything
 *  necessary for a listen, and if that works, we mark the socket as
 *  ready for listening.
 */

int sysctl_somaxconn = SOMAXCONN;

asmlinkage long sys_listen(int fd, int backlog)
{
    struct socket *sock;
    int err, fput_needed;

    if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL) {
        if ((unsigned) backlog > sysctl_somaxconn)
            backlog = sysctl_somaxconn;

        err = security_socket_listen(sock, backlog);
        if (!err)
            err = sock->ops->listen(sock, backlog);

        fput_light(sock->file, fput_needed);
    }
    return err;
}

比如l常使用的netty(4.0)框架Q在Linux下启动时Q会(x)直接d/proc/sys/net/core/somaxconn值然后作为listen的backlog参数q行调用Linuxpȝ的listenq行初始化等?/p>

int somaxconn = 3072;
BufferedReader in = null;
try {
    in = new BufferedReader(new FileReader("/proc/sys/net/core/somaxconn"));
    somaxconn = Integer.parseInt(in.readLine());
    logger.debug("/proc/sys/net/core/somaxconn: {}", somaxconn);
} catch (Exception e) {
    // Failed to get SOMAXCONN
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (Exception e) {
            // Ignored.
        }
    }
}

SOMAXCONN = somaxconn;
......
private volatile int backlog = NetUtil.SOMAXCONN;

一般稍微增大net.core.somaxconn值就昑־很有必要?/p>

讄其值方法:(x)

sysctl -w net.core.somaxconn=65535

较大内存的LinuxQ?5535数g般就可以?jin)?/p>

若让其生效,sysctl -p 卛_Q然后重启你的Server应用卛_?/p>

二。网卡设备将h攑օ队列的长度,netdev_max_backlog

内核代码中sysctl.c文g解释Q?/p>

number of unprocessed input packets before kernel starts dropping them, default 300

我所理解的含义,每个|络接口接收数据包的速率比内核处理这些包的速率快时Q允?dng)R到队列的最大数目,一旦超q将被丢弃?/p>

所起作用处Qnet/core/Dev.cQ?/p>

int netif_rx(struct sk_buff *skb)
{
    struct softnet_data *queue;
    unsigned long flags;

    /* if netpoll wants it, pretend we never saw it */
    if (netpoll_rx(skb))
        return NET_RX_DROP;

    if (!skb->tstamp.off_sec)
        net_timestamp(skb);

    /*
     * The code is rearranged so that the path is the most
     * short when CPU is congested, but is still operating.
     */
    local_irq_save(flags);
    queue = &__get_cpu_var(softnet_data);

    __get_cpu_var(netdev_rx_stat).total++;
    if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
        if (queue->input_pkt_queue.qlen) {
enqueue:
            dev_hold(skb->dev);
            __skb_queue_tail(&queue->input_pkt_queue, skb);
            local_irq_restore(flags);
            return NET_RX_SUCCESS;
        }

        netif_rx_schedule(&queue->backlog_dev);
        goto enqueue;
    }

    __get_cpu_var(netdev_rx_stat).dropped++;
    local_irq_restore(flags);

    kfree_skb(skb);
    return NET_RX_DROP;
}

以上代码看一下,大概?x)明白netdev_max_backlog?x)在什么时候v作用?/p>

nieyong 2014-07-30 17:22 发表评论
]]>
Linux服务器端口的那些?/title><link>http://www.tkk7.com/yongboy/archive/2014/06/28/415240.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Sat, 28 Jun 2014 06:15:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2014/06/28/415240.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/415240.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2014/06/28/415240.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/415240.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/415240.html</trackback:ping><description><![CDATA[<h3>前言</h3> <p>公司内技术分享文档,不涉?qing)公司内部技术等Q可以拿出来分n一下?/p> <h3>演示文档</h3><script async class="speakerdeck-embed" data-id="2bdcab50e0b60131fe8936fb43230fd9" data-ratio="1.41436464088398" src="http://speakerdeck.com/assets/embed.js"></script> <p>讉K地址Q?a >https://speakerdeck.com/yongboy/linuxxi-tong-fu-wu-duan-kou-de-na-xie-shi</a></p> <p>有些_糙Q有些点可能未表达清楚,(zhn)若发现谬误之处Q欢q及(qing)时指出?/p><img src ="http://www.tkk7.com/yongboy/aggbug/415240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2014-06-28 14:15 <a href="http://www.tkk7.com/yongboy/archive/2014/06/28/415240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《让|页加蝲快一些》培训演C文?/title><link>http://www.tkk7.com/yongboy/archive/2013/06/28/401054.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Fri, 28 Jun 2013 08:56:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2013/06/28/401054.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/401054.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2013/06/28/401054.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/401054.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/401054.html</trackback:ping><description><![CDATA[<div>《让|页加蝲快一些》,q篇PPT演示文档Q目的在于扩大视野用Q没有涉?qing)到深度Q,便于在处理网|能优化ӞZ些同事提供一些处理思\Q避免到处撞墙?br /> <br /> 目标Q?br /> <div>如何让一个页面加载快一些,q是主题<br /> 面每经q一个环节,都会(x)单涉?br /> 覆盖面广(前前后后都有)Q但蜻蜓Ҏ(gu)<br /> 可能?x)增加些视野Q目的也pC(jin)Q?br /> 前期不要做优化,但需要做规划Q?br /> <br /> <embed src='http://www.docin.com/DocinViewer-671771410-144.swf' width='650' height='490' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'></embed> <br />豆丁地址Q?br /> <div><a >http://www.docin.com/p-671771410.html</a></div></div> </div><img src ="http://www.tkk7.com/yongboy/aggbug/401054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2013-06-28 16:56 <a href="http://www.tkk7.com/yongboy/archive/2013/06/28/401054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ZErlang OTP构徏一个TCP服务?/title><link>http://www.tkk7.com/yongboy/archive/2012/10/24/390185.html</link><dc:creator>nieyong</dc:creator><author>nieyong</author><pubDate>Wed, 24 Oct 2012 10:14:00 GMT</pubDate><guid>http://www.tkk7.com/yongboy/archive/2012/10/24/390185.html</guid><wfw:comment>http://www.tkk7.com/yongboy/comments/390185.html</wfw:comment><comments>http://www.tkk7.com/yongboy/archive/2012/10/24/390185.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.tkk7.com/yongboy/comments/commentRss/390185.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/yongboy/services/trackbacks/390185.html</trackback:ping><description><![CDATA[<h2><strong>套接字模?/strong></h2> <p>d模式Q选项{active, true}Q一般让人很喜欢Q非d消息接收Q但在系l无法应对超大流量请求时Q客L(fng)发送的数据快过服务器可以处理的速度Q那么系l就可能?x)造成消息~冲塞满Q可能出现持l繁忙的量的极端情况下Q系l因h而溢出,虚拟机造成内存?sh)的风险而崩溃?/p> <p>使用被动模式Q选项{active, false}Q的套接字,底层的TCP~冲区可用于抑制hQƈ拒绝客户端的消息Q在接收数据的地斚w?x)调用gen_tcp:recvQ造成dQ单q程模式下就只能消极{待某一个具体的客户端套接字Q很危险Q。需要注意的是,操作pȝ可能q(sh)(x)做一些缓存允许客L(fng)机器l箋(hu)发送少量数据,然后才会(x)其dQ此时Erlang未调用recv函数?/p> <p>混合型模式(半阻塞)(j)Q用选项{active, once}打开Q主动仅针对一个消息,在控制进E发送完一个数据消息后Q必LC用inet:setopts(Socket, [{active, once}])重新ȀzM便接受下一个消息(在此之前Q系l处于阻塞状态)(j)。可见,混合型模式综合了(jin)d模式和被动模式的两者优势,可实现流量控Ӟ防止服务器被q多消息Ҏ(gu)?/p> <p>以下TCP Server代码Q都是徏立在混合型模式(半阻塞)(j)基础上?/p> <h2><strong>prim_inet相关说明</strong></h2> <p>prim_inet没有官方文档Q可以认为是对底层socket的直接包装。淘?a target="_blank">yufeng</a>_(d)q是otp内部实现的细?是针对Erlang库开发者的private moduleQ底层模块,不推荐用。但?a target="_blank">Building a Non-blocking TCP server using OTP principles</a>C中演CZ(jin)prim_inet操作Socket异步Ҏ(gu)?/p> <h2><strong>设计模式</strong></h2> <p>一般来_(d)需要一个单独进E进行客L(fng)套接字监听,每一个子q程q行处理来自具体客户端的socketh?/p> <p>?a target="_blank">Building a Non-blocking TCP server using OTP principles</a>C中,子进E用gen_fsm处理Q很巧妙的结合状态机和消息事Ӟ值得学习(fn)?/p> <p>?a target="_blank">Erlang: A Generalized TCP Server</a>文章中,作者也是用此模式Q但子进E不W合OTP规范Q因此个Z是一个很好的实践模式?/p> <h2><strong>simple_one_for_one</strong></h2> <p>易的一对一监督q程Q用来创Zl动态子q程。对于需要ƈ发处理多个请求的服务器较为合适。比如socket 服务端接受新的客L(fng)q接h以后Q需要动态创Z个新的socketq接处理子进E。若遵守OTP原则Q那是子监督进E?/p> <h2><strong>TCP Server实现</strong> </h2> <h3><strong>Z标准API单实?/strong></h3> <p>也是Z{active, once}模式Q但d的等待下一个客L(fng)q接的Q务被抛给?jin)子监督q程?/p> <p>看一下入口tcp_server_app?/p><script src="https://gist.github.com/3945140.js?file=tcp_server_app.erl"></script> <p>d端口Q然后启动主监督q程Q此时还?sh)?x)监听处理客户端sockethQ,紧接着启动子监督进E,开始处理来自客L(fng)的socket的连接?/p> <p>监督q程tcp_server_sup也很单:(x)</p><script src="https://gist.github.com/3945155.js?file=tcp_server_sup.erl"></script> <p>需要注意的是,只有调用start_child函数Ӟ才真正调用tcp_server_handler:start_link([LSock])函数?/p> <p>tcp_server_handler的代码也不复杂:(x)</p><script src="https://gist.github.com/3945175.js?file=tcp_server_handler.erl"></script> <p>代码很精巧,有些技巧在里面。子监督q程调用start_link函数Qinit?x)返回{ok, #state{lsock = Socket}, 0}. 数字0代表?jin)timeout数|意味着gen_server马上调用handle_info(timeout, #state{lsock = LSock} = State)函数Q执行客L(fng)socket监听Q阻塞于此,但不?x)?jing)响在此模式下其它函数的调用。直到有客户端进来,然后启动一个新的子监督q程tcp_server_handlerQ当前子监督q程解除d?/p> <p> </p> <h3><strong>Zprim_inet实现</strong></h3> <p>q个实现师从于Non-blocking TCP server using OTP principles一文,但子q程改ؓ(f)?jin)gen_server实现?/p> <p>看一看入口,很简单的Q?/p><script src="https://gist.github.com/3945273.js?file=tcp_server_app.erl"></script> <p>监督q程代码Q?/p><script src="https://gist.github.com/3945280.js?file=tcp_server_sup.erl"></script> <p>{略不一Pone_for_one包括?jin)一个监听进Etcp_listenerQ还包含?jin)一个tcp_client_supq程?simple_one_for_one{略)</p> <p>tcp_listener单独一个进E用于监听来自客L(fng)socket的连?</p><script src="https://gist.github.com/3945295.js?file=tcp_listener.erl"></script> <p>很显?dng)接收客户端的q接之后Q{交给tcp_client_handler模块q行处理Q?/p><script src="https://gist.github.com/3945302.js?file=tcp_client_handler.erl"></script> <p>和标准APIҎ(gu)一下,可以感受到异步IO的好处?/p> <h2><strong>结</strong></h2> <p>通过不同的模式,单实C个基于Erlang OTP的TCP服务器,也是学习(fn)ȝQ不至于忘记?/p> <p>(zhn)若有更好的Q欢q告知,谢谢?/p> <h2><strong>参考资?/strong></h2> <ol> <li><a target="_blank">Building a Non-blocking TCP server using OTP principles</a></li> <li><a target="_blank">Erlang: A Generalized TCP Server</a></li> <li>《ErlangE序设计?/li> <li>《Erlang/OTPq发~程实战?/li></ol><img src ="http://www.tkk7.com/yongboy/aggbug/390185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/yongboy/" target="_blank">nieyong</a> 2012-10-24 18:14 <a href="http://www.tkk7.com/yongboy/archive/2012/10/24/390185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://69ct.com" target="_blank">ۺƵ</a>| <a href="http://wwwv27.com" target="_blank">һ岻</a>| <a href="http://kingleadsw.com" target="_blank">baoyu122.Ƶ</a>| <a href="http://zhaoav7.com" target="_blank">mm1313޾Ʒ</a>| <a href="http://www321fafa.com" target="_blank">޹Ʒþ</a>| <a href="http://jiggybaby.com" target="_blank">˳Ƶ</a>| <a href="http://czxgjt.com" target="_blank">У԰޴ɫС˵ϼ</a>| <a href="http://wkk3.com" target="_blank">AVվ</a>| <a href="http://jlzjjy.com" target="_blank">ͩרĻ</a>| <a href="http://963315.com" target="_blank">ø߹ۿ</a>| <a href="http://xp189.com" target="_blank">AV˾ƷӰԺ</a>| <a href="http://gzltchem.com" target="_blank">պһ</a>| <a href="http://sdsxyz.com" target="_blank">avպaɫͷ</a>| <a href="http://wusongtv.com" target="_blank">ֻƬþù</a>| <a href="http://szgreater.com" target="_blank">鶹AVþþƷ </a>| <a href="http://snbbearing.com" target="_blank">һۿ</a>| <a href="http://pjszlw.com" target="_blank">óav</a>| <a href="http://www132126.com" target="_blank">鵺̳Ʒ</a>| <a href="http://bjhuicui.com" target="_blank">aaҹѾ糡</a>| <a href="http://xuanboart.com" target="_blank">˾þþƷӰԺ</a>| <a href="http://cctv69.com" target="_blank">һ</a>| <a href="http://517qq.com" target="_blank">ղһ</a>| <a href="http://sxjttxkywl.com" target="_blank">Ʒ1024Ƶ</a>| <a href="http://jack-fx.com" target="_blank">Ļɫ</a>| <a href="http://sh-lkby.com" target="_blank">պƷѵӰ</a>| <a href="http://zzmm88.com" target="_blank">ҹþAAAAAëƬѿ</a>| <a href="http://wwwyy763.com" target="_blank">þþþù˾Ʒҹ </a>| <a href="http://gxshenquan.com" target="_blank">޸߲</a>| <a href="http://9981tv.com" target="_blank">޻ɫվ</a>| <a href="http://www1616hh.com" target="_blank">ŷպٲ</a>| <a href="http://wangquanai.com" target="_blank">ĻӰ</a>| <a href="http://bjdxpx.com" target="_blank">˺Ů˸߳վ</a>| <a href="http://cswsfz.com" target="_blank">ҹAVƬ</a>| <a href="http://linmh.com" target="_blank">va߹ۿ</a>| <a href="http://001mc.com" target="_blank">޹Ʒ˾Ʒ</a>| <a href="http://91ttvv.com" target="_blank">Ƶ</a>| <a href="http://dghxm168.com" target="_blank">ɫwwwƵ</a>| <a href="http://612662.com" target="_blank">ձԺ޺Ժ</a>| <a href="http://liexion.com" target="_blank">ĻmvѸƵ8</a>| <a href="http://haha02.com" target="_blank">޸Ƶ</a>| <a href="http://js06vip.com" target="_blank">޳avƬþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>