Mcrouter:基于 Memcached协议的缓存层流量管理工具

http://www.infoq.com/cn/news/2014/09/mcrouter-memcached

https://code.facebook.com/posts/296442737213493/introducing-mcrouter-a-memcached-protocol-router-for-scaling-memcached-deployments/

memcache不支持服务端路由,facebook开发了mcrouter(能够处理每秒50亿次的请求).它和memcached之间通过文本协议通信.扮演者memcached服务器的客户端,应用的服务端.他的特性很多,基本上都是我们需要的.我们现在使用的是二进制协议,需要修改为文本协议.

dubbo应用使用的线程池为com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool,如果当queue设置为0时,会使用SynchronousQueue,这个东东导致了任务线程执行”不均衡”(满足了大家的心理预期,其实这种不均衡方式减少了上下文切换,但是SynchronousQueue没有大小,不能起到任务缓冲的作用).

请在dubbo:protocol上加上queues大小(参考tomcat默认配置).

    <dubbo:protocol name="dubbo" port="${dubbo.provider.port}" threads="200" queues="100"/>

测试:

修改前:

grep DubboServerHandler dubbo-demo.log |awk  -F '-'  '{print $6}' |awk  -F ']'  '{print $1}' |sort -n |uniq -c
     1 150
  1 151
  1 152
  1 153
  1 154
  1 168
  1 169
  1 170
117 171
   	5386 172
714 173
   	2646 174
   	3738 175
   	3105 180
   	6332 194
   	2483 195
   	4940 196
   	1211 197
   	5661 198
   	5428 199
   	1393 200

修改后:

grep DubboServerHandler dubbo-demo.log |awk  -F '-'  '{print $6}' |awk  -F ']'  '{print $1}' |sort -n |uniq -c
507 1
498 2
496 3
501 15
488 16
494 17
523 18
502 19
494 20
503 21
491 22
507 23
 		...
 507 133
495 134
498 135
494 136
507 137
508 151
490 152
494 195
496 196
496 197
506 198
493 199
489 200

本文讲了如何用zero copy技术来提高I/O性能.

静态文件服务器需要把磁盘上的数据发送给客户端.这里cpu消耗比较少,但是效率不高:内核从磁盘读数据,内核/用户空间交换数据,最后写到socket.数据在内核/用户空间转换时,需要拷贝数据,消耗cpu和内存带宽.对于java应用来说,还需要合理的使用缓冲区来减少gc的压力.

java提供了transferTo方法来使用zero copy技术.他可以让数据直接从一个channel到另外一个channel.避免上面说到的一些问题.

1.传统的解决办法

过程类似于下面的代码:

File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);

1.1数据拷贝

这种方式会有四次内存拷贝

1.2 上下文切换

这种方式会有四次上下文切换.

1.3 过程说明

  1. read方法导致一次从user modekernel mode的上下文切换.系统调用sys_read从文件读取数据,通过DMA,把磁盘上的数据读到kernel address space buffer.
  2. 数据从kernel address space buffer拷贝到user buffer.read返回,导致从kernel modeuser mode的上下文切换.现在数据被读到了user address space buffer.
  3. send方法导致一次user modekernel mode的上下文切换.第三次拷贝把拷贝到kernel address space buffer.此buffer关联着destination socket
  4. 系统send调用返回时导致第四次上下文切换,DMA把kernel address space buffer中的数据发送到协议引擎导致第四次数据拷贝.

1.4 intermediate kernel buffer

使用intermediate kernel buffer主要为了提高性能,读的时候扮演缓存的角色,写的时候可以让应用程序实现异步(应用程序写到kernel buffer就返回).不幸的是,当我们处理的数据大于内核缓冲大小时,这样的拷贝是完全没有任何意义的.

2.零拷贝的方式

使用如下的代码来完成零拷贝

java方法:

public void transferTo(long position, long count, WritableByteChannel target);

系统调用:

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

2.1 数据拷贝

涉及到3次数据拷贝.

2.2 上下文切换

涉及到2次上下文切换

2.3 过程说明

  1. transferTo方法让DMA把磁盘文件读到kernel read buffer.然后内核把kernel read buffer中的数据拷贝到socket buffer.
  2. DMAsocket buffer中的数据拷贝到协议引擎.

3 更好的方式

通过上面使用这种方式,上下文切换从4次变为了2次.数据拷贝减少了一次.如果网卡支持gather operations,linux 2.4内核就开始提供更好的解决方案.

  1. transferTo方法让DMA engine把磁盘文件内容拷贝到内核缓冲区.
  2. 数据不需要拷贝到socket buffer.socket buffer里只需写入数据的地址和长度.DMA engine从内核缓冲区把数据读到协议引擎.

通过内核带来的特性,数据拷贝变为了2次(这两次拷贝都是DMA在做).cpu copy变为了0.

4 写在最后

文章地址http://www.ibm.com/developerworks/library/j-zerocopy/,里面有性能测试结果.后面附带有性能测试程序.不过这个测试程序不太恰当,应该都用nio的api来测试tansferTo和非tansferTo.

静态文件服务器一般都有静态资源缓存(apache可以配置,其他的服务器不了解).如果使用内存缓存,减少了读的过程.内存拷贝变为cpu copy application buffer -> socket buffer,DMA copy socket buffer ->NIC buffer,磁盘io大大降低了.

NIO不是很熟悉,不知道通过ByteBuffer.allocateDirect()+transferTo+gather operations能不能让copy变为一次.