<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sparkle&#039;s Workshop</title>
	<atom:link href="http://weavesky.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://weavesky.com</link>
	<description></description>
	<lastBuildDate>Fri, 19 Feb 2010 09:42:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>基于MINA构建简单高性能的NIO应用-优化指南</title>
		<link>http://weavesky.com/2010/02/19/mina-article-4/</link>
		<comments>http://weavesky.com/2010/02/19/mina-article-4/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 09:26:13 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[mina]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=600</guid>
		<description><![CDATA[本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。
优化指南
MINA默认配置的性能并不是很高的，部分原因是MINA目前还保留初期版本的架构，另外一个原因是因为JVM的发展。
首先我们关闭默认的ThreadModel设置

IoAcceptor acceptor = ...;
IoServiceConfig acceptorConfig = acceptor.getDefaultConfig&#40;&#41;;
acceptorConfig.setThreadModel&#40;ThreadModel.MANUAL&#41;;

ThreadModel是一个很简单的线程实现，用于IoService。但是它实在太弱，以至于在并发环境产生大量问题。在MINA 2.0中，ThreadModel直接被取消。你应该使用ExecutorFilter来实现线程。
然后我们增加I/O处理线程(Article by Sparkle)
每一个Acceptor/Connector都使用一个线程来处理连接，然后把连接发送给I/O processor进行读写操作，我们只可以修改I/O processor使用的线程数，用以下代码设置

IoAcceptor acceptor = new SocketAcceptor&#40;Runtime.getRuntime&#40;&#41;.availableProcessors&#40;&#41; + 1, Executors.newCachedThreadPool&#40;&#41;&#41;;

当然是要将ExecutorFilter加入，上文已经很详细地描述了

acceptor.getDefaultConfig&#40;&#41;.getFilterChain&#40;&#41;.addLast&#40;&#34;threadPool&#34;, new ExecutorFilter&#40;Executors.newCachedThreadPool&#40;&#41;&#41;;

笔者在开发过程中，多次遇到OutOfMemoryError，经过研究之后才发现原因。MINA默认是使用direct memory实现ByteBuffer池的方案（以下简称direct buffer），通过JNI在内存开辟一段空间来使用，该方案在早期的MINA版本中是一个非常好的特性，那是因为MINA开发初期，JVM并没有现在的强大，带有池效果的direct buffer性能比较好。但是当我们使用-Xms -Xmx等指令增加JVM可使用的内存，那仅仅增加了堆的内存空间，而direct memory的空间并没有增加，导致MINA实际使用的时候经常出现OutOfMemoryError。如果你的确想使用direct memory，可以通过-XX:MaxDirectMemorySize选项来设置。不过笔者不建议这样做，因为最新的测试表明，在现代的JVM里面，direct memory比堆的表现更差。这里可能有读者会觉得奇怪，为什么不用池，而要用堆呢，而且还需要gc。那是因为现在的JVM gc能力已经很强了，而且在并发环境里面，pool的同步也是一个性能的问题。我们可以通过这样的代码进行设置(Article by Sparkle)

ByteBuffer.setUseDirectBuffers&#40;false&#41;;
ByteBuffer.setAllocator&#40;new SimpleByteBufferAllocator&#40;&#41;&#41;;

MINA 2.0已经默认把直接内存分配改成堆，为了提供最好的性能和稳定性。
最后一条优化技巧就是，把你的应用部署在Linux上，并且打开Java NIO使用Linux epoll的功能。可能你还没听过epoll，但是你应该听过Lighttpd、Nginx、Squid等，得益于epoll，它们提供很高的网络性能，还占用非常少的系统资源。JDK6已经默认把epoll配置打开，因此笔者建议把你的应用部署在JDK6上面，也同时因为JDK6还有别的优化特性。如果你的应用必须部署在JDK5上，你也可以通过参数把epoll支持打开。
文章快速索引

前言
一个简单的例子
MINA架构
优化指南

]]></description>
			<content:encoded><![CDATA[<blockquote><p>本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。</p></blockquote>
<p><strong>优化指南</strong><br />
MINA默认配置的性能并不是很高的，部分原因是MINA目前还保留初期版本的架构，另外一个原因是因为JVM的发展。</p>
<p>首先我们关闭默认的ThreadModel设置</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">IoAcceptor acceptor <span style="color: #339933;">=</span> ...<span style="color: #339933;">;</span>
IoServiceConfig acceptorConfig <span style="color: #339933;">=</span> acceptor.<span style="color: #006633;">getDefaultConfig</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
acceptorConfig.<span style="color: #006633;">setThreadModel</span><span style="color: #009900;">&#40;</span>ThreadModel.<span style="color: #006633;">MANUAL</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>ThreadModel是一个很简单的线程实现，用于IoService。但是它实在太弱，以至于在并发环境产生大量问题。在MINA 2.0中，ThreadModel直接被取消。你应该使用ExecutorFilter来实现线程。</p>
<p>然后我们增加I/O处理线程<span style="color: #ffffff;">(Article by Sparkle)</span><br />
每一个Acceptor/Connector都使用一个线程来处理连接，然后把连接发送给I/O processor进行读写操作，我们只可以修改I/O processor使用的线程数，用以下代码设置</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">IoAcceptor acceptor <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> SocketAcceptor<span style="color: #009900;">&#40;</span><span style="color: #003399;">Runtime</span>.<span style="color: #006633;">getRuntime</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">availableProcessors</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span>, Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>当然是要将ExecutorFilter加入，上文已经很详细地描述了</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">acceptor.<span style="color: #006633;">getDefaultConfig</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">getFilterChain</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;threadPool&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> ExecutorFilter<span style="color: #009900;">&#40;</span>Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>笔者在开发过程中，多次遇到OutOfMemoryError，经过研究之后才发现原因。MINA默认是使用direct memory实现ByteBuffer池的方案（以下简称direct buffer），通过JNI在内存开辟一段空间来使用，该方案在早期的MINA版本中是一个非常好的特性，那是因为MINA开发初期，JVM并没有现在的强大，带有池效果的direct buffer性能比较好。但是当我们使用-Xms -Xmx等指令增加JVM可使用的内存，那仅仅增加了堆的内存空间，而direct memory的空间并没有增加，导致MINA实际使用的时候经常出现OutOfMemoryError。如果你的确想使用direct memory，可以通过-XX:MaxDirectMemorySize选项来设置。不过笔者不建议这样做，因为最新的测试表明，在现代的JVM里面，direct memory比堆的表现更差。这里可能有读者会觉得奇怪，为什么不用池，而要用堆呢，而且还需要gc。那是因为现在的JVM gc能力已经很强了，而且在并发环境里面，pool的同步也是一个性能的问题。我们可以通过这样的代码进行设置<span style="color: #ffffff;">(Article by Sparkle)</span></p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">ByteBuffer.<span style="color: #006633;">setUseDirectBuffers</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
ByteBuffer.<span style="color: #006633;">setAllocator</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> SimpleByteBufferAllocator<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>MINA 2.0已经默认把直接内存分配改成堆，为了提供最好的性能和稳定性。</p>
<p>最后一条优化技巧就是，把你的应用部署在Linux上，并且打开Java NIO使用Linux epoll的功能。可能你还没听过epoll，但是你应该听过Lighttpd、Nginx、Squid等，得益于epoll，它们提供很高的网络性能，还占用非常少的系统资源。JDK6已经默认把epoll配置打开，因此笔者建议把你的应用部署在JDK6上面，也同时因为JDK6还有别的优化特性。如果你的应用必须部署在JDK5上，你也可以通过参数把epoll支持打开。</p>
<p><strong>文章快速索引</strong></p>
<ol>
<li><a href="http://weavesky.com/2010/02/19/mina-article-1/">前言</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-2/">一个简单的例子</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-3/">MINA架构</a></li>
<li>优化指南</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2010/02/19/mina-article-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于MINA构建简单高性能的NIO应用-MINA架构</title>
		<link>http://weavesky.com/2010/02/19/mina-article-3/</link>
		<comments>http://weavesky.com/2010/02/19/mina-article-3/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 09:21:59 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[mina]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=585</guid>
		<description><![CDATA[本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。
MINA架构
这里，我借用了一张Trustin Lee在Asia 2006的ppt里面的图片来介绍MINA的架构。

Remote Peer就是客户端，而下方的框是MINA的主要结构，各个框之间的箭头代表数据流向。
大家可以对比刚刚的例子来看这个架构图，IoService就是整个MINA的入口，负责底层的IO操作，客户端发过来的消息就是由它处理。刚刚我们使用的IoAcceptor就是一个IoService，之所以抽象成IoService，是因为MINA用同样的架构来处理服务器和客户端编程，IoService的另一个子类就是IoConnector，用于客户端。不过根据笔者的使用经验，使用非阻塞的模型进行客户端编程非常的不方便，你最好寻求其他的阻塞通讯框架。
IoService把数据转化成一个一个的事件，传递给IoFilterChain。你可以加入一连串的IoFilter，进行各种功能。笔者的建议是将一些功能性的，业务不相关的代码，用IoFilter来实现，使得整个应用结构更清晰，也方便代码重用。(Article by Sparkle)
被IoFilter处理过的事件，发送给 IoHandler，然后我们在这里实现具体的业务逻辑。这个部分很简单，如果你有Swing的使用经验的话，你会发现它跟Swing的事件非常相像，你要做的事情，仅仅是重载你需要的方法，然后编写具体的业务功能。在这其中，最重要的一个方法就是messageReceived了。
值得留意的是一个IoSession的类，每一个IoSession实例代表这一个连接，我们需要对连接进行的任何操作都通过这个类来实现。
从IoHandler通过调用IoSession.write等方法向客户端发送的消息，会通过跟输入数据相反的次序依次传递，直至由IoService负责把数据发送给客户端。
这就已经是MINA的全部，是不是很简单。
接下来，我会详细介绍我们编写具体代码的时候主要涉及到的三个类，IoHandler、IoSession和IoFilter。
 IoHandler

public interface IoHandler &#123;
  void sessionCreated&#40;IoSession session&#41; throws Exception;
  void sessionOpened&#40;IoSession session&#41; throws Exception;
  void sessionClosed&#40;IoSession session&#41; throws Exception;
  void sessionIdle&#40;IoSession session, IdleStatus status&#41; throws Exception;
  void exceptionCaught&#40;IoSession session, Throwable cause&#41; throws Exception;
  void messageReceived&#40;IoSession session, Object message&#41; throws Exception;
  void [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。</p></blockquote>
<p><strong>MINA架构</strong></p>
<p>这里，我借用了一张Trustin Lee在Asia 2006的ppt里面的图片来介绍MINA的架构。</p>
<p><a href="http://weavesky.com/wp-content/uploads/2010/02/mina.jpg"><img class="alignnone size-medium wp-image-590" title="mina" src="http://weavesky.com/wp-content/uploads/2010/02/mina-300x225.jpg" alt="" width="300" height="225" /></a></p>
<p>Remote Peer就是客户端，而下方的框是MINA的主要结构，各个框之间的箭头代表数据流向。<br />
大家可以对比刚刚的例子来看这个架构图，IoService就是整个MINA的入口，负责底层的IO操作，客户端发过来的消息就是由它处理。刚刚我们使用的IoAcceptor就是一个IoService，之所以抽象成IoService，是因为MINA用同样的架构来处理服务器和客户端编程，IoService的另一个子类就是IoConnector，用于客户端。不过根据笔者的使用经验，使用非阻塞的模型进行客户端编程非常的不方便，你最好寻求其他的阻塞通讯框架。<br />
IoService把数据转化成一个一个的事件，传递给IoFilterChain。你可以加入一连串的IoFilter，进行各种功能。笔者的建议是将一些功能性的，业务不相关的代码，用IoFilter来实现，使得整个应用结构更清晰，也方便代码重用。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
被IoFilter处理过的事件，发送给 IoHandler，然后我们在这里实现具体的业务逻辑。这个部分很简单，如果你有Swing的使用经验的话，你会发现它跟Swing的事件非常相像，你要做的事情，仅仅是重载你需要的方法，然后编写具体的业务功能。在这其中，最重要的一个方法就是messageReceived了。<br />
值得留意的是一个IoSession的类，每一个IoSession实例代表这一个连接，我们需要对连接进行的任何操作都通过这个类来实现。<br />
从IoHandler通过调用IoSession.write等方法向客户端发送的消息，会通过跟输入数据相反的次序依次传递，直至由IoService负责把数据发送给客户端。<br />
这就已经是MINA的全部，是不是很简单。</p>
<p>接下来，我会详细介绍我们编写具体代码的时候主要涉及到的三个类，IoHandler、IoSession和IoFilter。</p>
<p><strong> IoHandler</strong></p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">interface</span> IoHandler <span style="color: #009900;">&#123;</span>
  <span style="color: #000066; font-weight: bold;">void</span> sessionCreated<span style="color: #009900;">&#40;</span>IoSession session<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> sessionOpened<span style="color: #009900;">&#40;</span>IoSession session<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> sessionClosed<span style="color: #009900;">&#40;</span>IoSession session<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> sessionIdle<span style="color: #009900;">&#40;</span>IoSession session, IdleStatus status<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> exceptionCaught<span style="color: #009900;">&#40;</span>IoSession session, <span style="color: #003399;">Throwable</span> cause<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> messageReceived<span style="color: #009900;">&#40;</span>IoSession session, <span style="color: #003399;">Object</span> message<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">void</span> messageSent<span style="color: #009900;">&#40;</span>IoSession session, <span style="color: #003399;">Object</span> message<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>MINA的内部实现了一个事件模型，而IoHanlder则是所有事件最终产生响应的位置。每一个方法的名字很明确表明该事件的含义。messageReceived是接收客户端消息的事件，我们应该在这里实现业务逻辑。messageSent是服务器发送消息的事件，一般情况下我们不会使用它。sessionClosed是客户端断开连接的事件，可以在这里进行一些资源回收等操作。值得留意的是，客户端连接有两个事件，sessionCreated和sessionOpened，两者稍有不同，sessionCreated是由I/O processor线程触发的，而sessionOpened在其后，由业务线程触发的，由于MINA的I/O processor线程非常少，因此如果我们真的需要使用sessionCreated，也必须是耗时短的操作，一般情况下，我们应该把业务初始化的功能放在sessionOpened事件中。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
细心的读者可能会发现，我们刚刚的例子继承的是IoHandlerAdapter，IoHandlerAdapter其实就是一个IoHanlder的空的实现，这样我们就可以不用重载不感兴趣的事件。</p>
<p><strong> IoSession</strong><br />
IoSession是一个接口，MINA里很多的地方都使用接口，很好地体现了面向接口编程的思想。它提供了对当前连接的操作功能，还有用户定义属性的存储功能，这点非常重要。IoSession是线程安全的，也就是我们能够在多线程环境中随意操作IoSession，这点给开发带来很大的好处。我们来看看具体提供的方法，笔者列举一些比较常用和重要的方法</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">WriteFuture write<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> message<span style="color: #009900;">&#41;</span>
CloseFuture close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003399;">Object</span> getAttribute<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Object</span> setAttribute<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> key, <span style="color: #003399;">Object</span> value<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Object</span> removeAttribute<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Set</span> getAttributeKeys<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000066; font-weight: bold;">boolean</span> isConnected<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000066; font-weight: bold;">boolean</span> isClosing<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
SocketAddress getRemoteAddress<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000066; font-weight: bold;">boolean</span> isIdle<span style="color: #009900;">&#40;</span>IdleStatus status<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>在这里，笔者把IoSession的方法大致分成三类<br />
第一类，连接操作功能。<br />
最主要的方法有两个，向客户端发送消息和断开连接。可以看的出，write接受的变量是一个Object，但是实际上应该传入什么类型呢？具体还得看你是否使用了ProtocolCodecFilter（下面会详细介绍），如果使用了ProtocolCodecFilter，那这个message将可能是一个String，或者是一个用户定义的JavaBean。默认的情况，message是一个ByteBuffer。ByteBuffer是MINA的一个类，跟java.nio.ByteBuffer类同名，MINA 2.0将会将它改成IoBuffer，以避免讨论上的误会。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
另一个值得留意的是Future类，MINA是一个非阻塞的通信框架，其中一个明显的体现就是调用IoSession.write方法是不会阻塞的。用户调用了write方法之后，消息内容会发到底层等候发送，至于什么时候发出，就不得而知了。当然，实际上调用了write之后，数据几乎是立刻发出的，这得益与NIO的高性能。但是，如果我们必须确认了消息发出，然后进行某些处理，我们就需要使用Future类，以下是一个很常见的代码</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">IoSession session <span style="color: #339933;">=</span> ...<span style="color: #339933;">;</span>
WriteFuture future <span style="color: #339933;">=</span> session.<span style="color: #006633;">write</span><span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// Wait until the message is completely written out to the O/S buffer.</span>
future.<span style="color: #006633;">join</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span> future.<span style="color: #006633;">isWritten</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>  <span style="color: #009900;">&#123;</span>
  <span style="color: #666666; font-style: italic;">// The message has been written successfully.</span>
<span style="color: #009900;">&#125;</span>  <span style="color: #000000; font-weight: bold;">else</span>  <span style="color: #009900;">&#123;</span>
  <span style="color: #666666; font-style: italic;">// The messsage couldn't be written out completely for some reason.</span>
  <span style="color: #666666; font-style: italic;">// (e.g. Connection is closed)</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>通过调用future.join，程序就会阻塞，直至消息处理结束。我们还能通过future.isWritten得知消息是否成功发送。<br />
在这里，笔者顺便说一个实际使用的发现，消息发送是会自动合并的，简单来说，如果在很短的时间里，对同一个IoSession进行了两次write操作，客户端有可能只收到一条消息，而这条消息就是服务器发出的两条消息前后接起来。这样的设计可以在高并发的时候节省网络开销，而笔者的实际使用过程中，效果也相当好。但是如果这样行为会导致客户端工作不正常，你也可以通过参数关闭它。</p>
<p>第二类，属性存储操作。<br />
通常来说，我们的系统是有用户状态的，我们就需要在连接上存储用户属性，IoSession的Attribute就是这样一个功能。例如两个连接同时连入服务器，一个连接是用户A，用户ID是13，另一个连接是用户B，用户ID是14，我们就可以在用户登录成功之后，调用IoSession.setAttribute(“login_id”,13)，然后在其后的操作中，通过IoSession.getAttribute(“login_id”)获得当前登录用户ID，并进行相应的操作。简单来说，就是一个类似HttpSession的功能，当然具体的实现方法不一样。<span style="color: #ffffff;">(Article by Sparkle)</span></p>
<p>第三类，连接状态。<br />
这里就不多说了，从方法名上我们就能知道它具体的功能。</p>
<p><strong> IoFilter</strong><br />
过滤器是MINA的一个很重要的功能。IoFilter也是一个接口，但是相对比较复杂，这里就不列举它的方法了。简单来说IoFilter就像ServletFilter，在事件被IoHandler处理之前或之后进行一些特定的操作，但是它比ServletFilter复杂，可以处理很多种事件，除了包括IoHandler的7个事件以外，还有一些内部的事件可以进行操作。<br />
MINA提供了一些常用的IoFilter实现，例如有LoggingFilter（日志功能）、BlacklistFilter（黑名单功能）、CompressionFilter（压缩功能）、SSLFilter（SSL支持），这些过滤器比较简单，通过阅读它们的源代码，能够更进一步理解过滤器的实现。笔者在这里要重点介绍两个过滤器，ProtocolCodecFilter和ExecutorFilter</p>
<p><strong>ProtocolCodecFilter</strong><br />
网络传输的内容其实本质是一个二进制流，但是我们的业务功能不会，或者说不应该去直接操作二进制流。MINA默认向IoHandler传入的message是一个ByteBuffer，如果我们直接在IoHandler操作ByteBuffer，会导致大量协议分析的代码和实际的业务代码混杂在一起。最适合的做法，就是在IoFilter把ByteBuffer转换成String或者JavaBean，ProtocolCodecFilter正是这样的一个功能的过滤器。<br />
使用ProtocolCodecFilter很简单，我们只要把ProtocolCodecFilter加入到FilterChain就可以了，但是我们需要提供一个ProtocolCodecFactory。其实ProtocolCodecFilter仅仅是实现了过滤器部分的功能，它会将最终的转换工作，交给从ProtocolCodecFactory获得的Encode和Decode。如果我们需要编写自己的ProtocolCodec，就应该从ProtocolCodecFactory入手。MINA内置了几个ProtocolCodecFactory，比较常用的就是ObjectSerializationCodecFactory和TextLineCodecFactory。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
ObjectSerializationCodecFactory是Java Object序列化之后的内容直接跟ByteBuffer互相转化，比较适合两端都是Java的情况使用。TextLineCodecFactory就是String跟ByteBuffer的转化，说白了就是文本，例如你要实现一个SMTP服务器，或者POP服务器，就可以使用它。而笔者的实际使用，大多数情况都是使用TextLineCodecFactory。<br />
这里提及一下IoFilter的顺序问题，IoFilter是有加入顺序的，例如，先加入LoggingFilter再加入ProtocolCodecFilter，和先加入ProtocolCodecFilter再加入LoggingFilter的效果是不一样的，前者LoggingFilter写入日志的内容是ByteBuffer，而后者写入日志的是转换后具体的类，例如String。实际使用的时候，一定要处理好过滤器的顺序。</p>
<p><strong>ExecutorFilter</strong><br />
另一个重要的过滤器就是ExecutorFilter。这里，我需要先说明一下MINA的线程工作模式，MINA默认是单线程处理所有客户端的消息，也就是说，即使你在一台8CPU的机器上面跑，可能也只用到一个CPU，另外，如果某次消息处理太耗时，就会导致其他消息等待，整体的吞吐量下降。很多朋友抱怨MINA的性能差，其实是因为他们没有加入ExecutorFilter的缘故。ExecutorFilter设计的很精巧，大家可以仔细阅读一下源代码，它会将同一个连接的消息合并起来按顺序调用，不会出现两个线程同时处理同一个连接的情况。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
这里再次提及IoFitler的顺序问题，一般情况下，我们会将ExecutorFilter放在ProtocolCodecFilter之后，因为我们不需要多线程地执行ProtocolCodec操作，用单一线程来进行ProtocolCodec性能会比较高，而具体的业务逻辑可能还设计数据库操作，因此更适合放在不同的线程中运行。</p>
<p><strong>文章快速索引</strong></p>
<ol>
<li><a href="http://weavesky.com/2010/02/19/mina-article-1/">前言</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-2/">一个简单的例子</a></li>
<li>MINA架构</li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-4/">优化指南</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2010/02/19/mina-article-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于MINA构建简单高性能的NIO应用-一个简单的例子</title>
		<link>http://weavesky.com/2010/02/19/mina-article-2/</link>
		<comments>http://weavesky.com/2010/02/19/mina-article-2/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 09:05:57 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[mina]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=583</guid>
		<description><![CDATA[本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。
一个简单的例子
MINA使用非常简单，笔者以前做过一段时间传统的Java Socket开发，不过一直对Java NIO不是很理解，但是MINA很快就上手了，MINA封装了NIO繁琐的部分，使你可以更专注于业务功能实现。话不多说，让我们来看一个简单的例子，一个很常见的例子，时间服务器。(Article by Sparkle)
我们的实现目标是一个能响应多个客户端的请求，然后返回服务器当前的系统时间的功能。传统的Java Socket程序，我们需要每accept一个客户端连接，就创建一个新的线程来响应，这会令到系统整体负载能力有较大的限制，而且我们必须手工编写连接管理等代码。让我们来看看MINA是怎么处理的。
首先我们从官方网站下载MINA 1.1，这里我们假设JDK为1.5以上的版本，如果你使用的是JDK 1.4，请下载MINA 1.0，MINA 1.0跟1.1几乎一样，但是强烈建议使用JDK 1.5以上以获得更好的性能。
解开压缩包之后，能看见很多jar包，这里暂不介绍每个包的具体作用，可以把所有包都导入项目。值得留意的是MINA使用了一个slf4j的日志库，该日志库大有取缔common-logging之势。

public class TimeServer &#123;
  public static void main&#40;String&#91;&#93; args&#41; throws IOException &#123;
    IoAcceptor acceptor = new SocketAcceptor&#40;&#41;;
&#160;
    SocketAcceptorConfig cfg = new SocketAcceptorConfig&#40;&#41;;
    cfg.getFilterChain&#40;&#41;.addLast&#40; &#34;logger&#34;, new LoggingFilter&#40;&#41; &#41;;
    cfg.getFilterChain&#40;&#41;.addLast&#40; &#34;codec&#34;, new ProtocolCodecFilter&#40; new [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。</p></blockquote>
<p><strong>一个简单的例子</strong></p>
<p>MINA使用非常简单，笔者以前做过一段时间传统的Java Socket开发，不过一直对Java NIO不是很理解，但是MINA很快就上手了，MINA封装了NIO繁琐的部分，使你可以更专注于业务功能实现。话不多说，让我们来看一个简单的例子，一个很常见的例子，时间服务器。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
我们的实现目标是一个能响应多个客户端的请求，然后返回服务器当前的系统时间的功能。传统的Java Socket程序，我们需要每accept一个客户端连接，就创建一个新的线程来响应，这会令到系统整体负载能力有较大的限制，而且我们必须手工编写连接管理等代码。让我们来看看MINA是怎么处理的。</p>
<p>首先我们从官方网站下载MINA 1.1，这里我们假设JDK为1.5以上的版本，如果你使用的是JDK 1.4，请下载MINA 1.0，MINA 1.0跟1.1几乎一样，但是强烈建议使用JDK 1.5以上以获得更好的性能。<br />
解开压缩包之后，能看见很多jar包，这里暂不介绍每个包的具体作用，可以把所有包都导入项目。值得留意的是MINA使用了一个slf4j的日志库，该日志库大有取缔common-logging之势。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> TimeServer <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">IOException</span> <span style="color: #009900;">&#123;</span>
    IoAcceptor acceptor <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> SocketAcceptor<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    SocketAcceptorConfig cfg <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> SocketAcceptorConfig<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cfg.<span style="color: #006633;">getFilterChain</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span> <span style="color: #0000ff;">&quot;logger&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> LoggingFilter<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cfg.<span style="color: #006633;">getFilterChain</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span> <span style="color: #0000ff;">&quot;codec&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> ProtocolCodecFilter<span style="color: #009900;">&#40;</span> <span style="color: #000000; font-weight: bold;">new</span> TextLineCodecFactory<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    acceptor.<span style="color: #006633;">bind</span><span style="color: #009900;">&#40;</span> <span style="color: #000000; font-weight: bold;">new</span> InetSocketAddress<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">8123</span><span style="color: #009900;">&#41;</span>, <span style="color: #000000; font-weight: bold;">new</span> TimeServerHandler<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, cfg<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Time server started.&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这里是我们的主程序，非常简单。<br />
首先我们需要一个IoAcceptor，这里我们选择了一个SocketAcceptor，也就是TCP协议。<br />
然后，我们给应用加上日志过滤器和协议编码过滤器。<span style="color: #ffffff;">(Article by Sparkle)</span><br />
最后，我们把acceptor bind到本机的8123端口，并且使用TimeServerHandler来实现协议。</p>
<p>TimeServerHandler是我们实现具体业务功能的地方。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> TimeServerHandler <span style="color: #000000; font-weight: bold;">extends</span> IoHandlerAdapter <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> messageReceived<span style="color: #009900;">&#40;</span>IoSession session, <span style="color: #003399;">Object</span> msg<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #003399;">String</span> str <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#41;</span> msg<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span> <span style="color: #0000ff;">&quot;quit&quot;</span>.<span style="color: #006633;">equalsIgnoreCase</span><span style="color: #009900;">&#40;</span>str<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    session.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #003399;">Date</span> date <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Date</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  session.<span style="color: #006633;">write</span><span style="color: #009900;">&#40;</span> date.<span style="color: #006633;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Message written...&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> sessionCreated<span style="color: #009900;">&#40;</span>IoSession session<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Session created...&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>IoHandlerAdapter提供了7个事件方法，我们要做的事情仅仅是挑选我们需要做出响应的事件进行重载。在我这个例子了，我重载了两个方法。sessionCreated会在客户端连接的时候调用，通常我们会在这里进行一些初始化操作，我这里仅仅是打印一条信息。messageReceived就是整个Handler的中心部分，每一个从客户端发过来的消息都会转化成对该方法的调用。由于我们加入了协议编码过滤器，因此这里获得的Object msg是一个String，而不是默认的ByteBuffer（下文会详细介绍ProtocolCodecFilter）。这里我们实现了一个很简单的业务功能，如果用户输入的是quit，就断开连接，否则就输入当前时间。可以看出，IoSession封装了对当前连接的操作。</p>
<p>至此，我们就实现了一个时间服务器。</p>
<p><strong>文章快速索引</strong></p>
<ol>
<li><a href="http://weavesky.com/2010/02/19/mina-article-1/">前言</a></li>
<li>一个简单的例子</li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-3/">MINA架构</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-4/">优化指南</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2010/02/19/mina-article-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于MINA构建简单高性能的NIO应用-前言</title>
		<link>http://weavesky.com/2010/02/19/mina-article-1/</link>
		<comments>http://weavesky.com/2010/02/19/mina-article-1/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 08:50:41 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[mina]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=573</guid>
		<description><![CDATA[本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。
前言
MINA是Trustin Lee最新制作的Java通讯框架。通讯框架的主要作用是封装底层IO操作，提供高级的操作API。比较出名的通讯框架有C++的ACE、Python的Twisted，而Java的通讯框架还有QuickServer、Netty2、Cindy、Grizzly等。
2004年6月，Trustin Lee发布了一个通讯框架Netty2，是Java界第一个事件模型架构的通讯框架，Cindy也从中借鉴了不少思想。由于Netty2的架构不是很好，Trustin Lee在2004年底加入Apache Directory组之后，重写了整个框架，取名为MINA。MINA是一个基于Java NIO的通讯框架，Java从1.4开始引入NIO，提供了一个非阻塞、高性能的IO底层。
目前使用MINA的产品并不是很多，比较出名的就有Apache Directory（作者就在为这个项目效力）、Openfire（Jive出品的一个XMPP产品）、red5（研究flash流媒体flv技术的朋友应该很清楚这个东西，adobe fms的竞争者，国内也有视频网站在使用）等等。 (Article by Sparkle)
笔者在07年初的时候，公司新项目需要用Java实现一个Socket Server，对比了Netty2、Cindy、QuickServer和MINA。当时Netty2已经停止开发，也找不到官方网站和代码，比较了另外三个框架之后，毅然选择了当时文档比较缺乏和使用群较少的MINA，一年以来的使用经验来看，感觉还是很不错的，MINA有着清晰的架构，很方便做自定义的扩充。在1.0发布之后，官方网站充实了很多，增加了不少文档，也听到越来越多的朋友开始使用MINA。后来专门针对JDK 1.5发布了1.1的版本，使用JDK内置的concurrent代替backport-util-concurrent。目前1.0和1.1同时存在，但已经不再增加新功能，仅仅发布bug fix的版本，新功能都在2.0中实现，2.0调整了架构，性能有更大的提升，目前还在开发中。
基本特性

通过Java NIO支持TCP和UDP协议，另外还支持RS232和VM内通讯。由于MINA有清晰的架构，你也能很简单地实现一个底层网络协议。目前不支持阻塞IO，似乎还没有计划支持，当然你可以在其之上实现一个阻塞的模型，不过按照笔者的经验来说，非阻塞IO更适合Server端编程。
一个类似ServletFilter的过滤器模型。这是笔者认为MINA的精髓所在，通过引入过滤器模型，可以将一些非业务的功能独立开来，层次更清晰，很有AOP的思想，可以很方便地进行日志、协议转换、压缩等等功能，还能在运行中动态增加或去掉功能。(Article by Sparkle)
可以直接使用底层的ByteBuffer，也可以使用用户定义的消息Object和编码方式。
高度可定制的线程模型，单线程、一个线程池，或者类似SEDA的多个线程池。
SSL支持，攻击防御和流量控制，mock测试友好，JMX支持，Spring集成，你还需要更多吗。

文章快速索引

前言
一个简单的例子
MINA架构
优化指南

]]></description>
			<content:encoded><![CDATA[<blockquote><p>本文为Sparkle发于《程序员》2008年2月刊的文章，与《程序员》的协议，可以在个人博客中发布，转载请保留出处。</p></blockquote>
<p><strong>前言</strong></p>
<p>MINA是Trustin Lee最新制作的Java通讯框架。通讯框架的主要作用是封装底层IO操作，提供高级的操作API。比较出名的通讯框架有C++的ACE、Python的Twisted，而Java的通讯框架还有QuickServer、Netty2、Cindy、Grizzly等。</p>
<p>2004年6月，Trustin Lee发布了一个通讯框架Netty2，是Java界第一个事件模型架构的通讯框架，Cindy也从中借鉴了不少思想。由于Netty2的架构不是很好，Trustin Lee在2004年底加入Apache Directory组之后，重写了整个框架，取名为MINA。MINA是一个基于Java NIO的通讯框架，Java从1.4开始引入NIO，提供了一个非阻塞、高性能的IO底层。</p>
<p>目前使用MINA的产品并不是很多，比较出名的就有Apache Directory（作者就在为这个项目效力）、Openfire（Jive出品的一个XMPP产品）、red5（研究flash流媒体flv技术的朋友应该很清楚这个东西，adobe fms的竞争者，国内也有视频网站在使用）等等。 <span style="color: #ffffff;">(Article by Sparkle)</span></p>
<p>笔者在07年初的时候，公司新项目需要用Java实现一个Socket Server，对比了Netty2、Cindy、QuickServer和MINA。当时Netty2已经停止开发，也找不到官方网站和代码，比较了另外三个框架之后，毅然选择了当时文档比较缺乏和使用群较少的MINA，一年以来的使用经验来看，感觉还是很不错的，MINA有着清晰的架构，很方便做自定义的扩充。在1.0发布之后，官方网站充实了很多，增加了不少文档，也听到越来越多的朋友开始使用MINA。后来专门针对JDK 1.5发布了1.1的版本，使用JDK内置的concurrent代替backport-util-concurrent。目前1.0和1.1同时存在，但已经不再增加新功能，仅仅发布bug fix的版本，新功能都在2.0中实现，2.0调整了架构，性能有更大的提升，目前还在开发中。</p>
<p><strong>基本特性</strong></p>
<ul>
<li>通过Java NIO支持TCP和UDP协议，另外还支持RS232和VM内通讯。由于MINA有清晰的架构，你也能很简单地实现一个底层网络协议。目前不支持阻塞IO，似乎还没有计划支持，当然你可以在其之上实现一个阻塞的模型，不过按照笔者的经验来说，非阻塞IO更适合Server端编程。</li>
<li>一个类似ServletFilter的过滤器模型。这是笔者认为MINA的精髓所在，通过引入过滤器模型，可以将一些非业务的功能独立开来，层次更清晰，很有AOP的思想，可以很方便地进行日志、协议转换、压缩等等功能，还能在运行中动态增加或去掉功能。<span style="color: #ffffff;">(Article by Sparkle)</span></li>
<li>可以直接使用底层的ByteBuffer，也可以使用用户定义的消息Object和编码方式。</li>
<li>高度可定制的线程模型，单线程、一个线程池，或者类似SEDA的多个线程池。</li>
<li>SSL支持，攻击防御和流量控制，mock测试友好，JMX支持，Spring集成，你还需要更多吗。</li>
</ul>
<p><strong>文章快速索引</strong></p>
<ol>
<li>前言</li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-2/">一个简单的例子</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-3/">MINA架构</a></li>
<li><a href="http://weavesky.com/2010/02/19/mina-article-4/">优化指南</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2010/02/19/mina-article-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>python在生产环境可选的memcached lib</title>
		<link>http://weavesky.com/2009/05/15/python-memcached-lib-in-production/</link>
		<comments>http://weavesky.com/2009/05/15/python-memcached-lib-in-production/#comments</comments>
		<pubDate>Fri, 15 May 2009 03:06:56 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=570</guid>
		<description><![CDATA[memcached对动态语言帮助很大，因为动态语言的模式和实际的部署模型，python/php/ruby等不可能像Java那样在JVM内存中开辟一块内存来缓冲数据（而实际上Java这样做也会遇到容量不够，GC，集群等问题）。不过在实际使用中，我发现python memcached lib并没有想象中那么成熟、完整，经过一番研究之后，目前至少能勉强在生产环境中使用。
 从memcached官方wiki中可以看到，python还是有不少可以使用的lib的，但是经过测试和比较之后，发现选择的方案也不多。我们还是从比较出名的web框架入手吧，毫无疑问，Django是目前python最好的框架之一，在生产环境使用它的人也比较多，而且它的文档很详细，有一节就是专门提到cache的部分 。Django推荐使用cmemcache，在不能使用cmemcache的情况（例如windows机器），就使用python-memcached。经过我的实际测试，这个或许不是性能最好，版本最新，功能最多的选择，但是是一个很稳靠的选择。
cmemcache封装的是libmemcache，这个类库很久没有更新了，目前libmemcached比较活跃，功能也比较全面。这里值得一提的是Python libmemcached是来自豆瓣贡献的类库，不过因为使用的人不多，所以最终我没有选择。另外还有一个pylibmc也是对libmemcached的封装，这是cmemcache_hash的作者写的类库（稍后会提到cmemcache_hash），很可惜我一直没有编译成功。
但是实际使用cmemcache/python-memcached的组合也是要进行一些封装的，首先有cmemcache不是线程安全的问题 ，另外就是Python libmemcached的hash算法有问题。什么？你从来不用超过1个的memcached服务器？我还是建议你至少用2个memcached服务器吧，至少有backup。
而cmemcache和Python libmemcached的hash算法不一样。这里我们还需要cmemcache_hash，名字可能有少少误导，它的作用是领到Python libmemcached的hash跟cmemcache一致
于是我们的封装代码大概是这样

try:
    import cmemcache as memcache
    logger.warn&#40;'load cmemcache'&#41;
    # although cmemcache might be thread safe
    # but we still make it thread local
    # also it can use separate socket connect in [...]]]></description>
			<content:encoded><![CDATA[<p>memcached对动态语言帮助很大，因为动态语言的模式和实际的部署模型，python/php/ruby等不可能像Java那样在JVM内存中开辟一块内存来缓冲数据（而实际上Java这样做也会遇到容量不够，GC，集群等问题）。不过在实际使用中，我发现python memcached lib并没有想象中那么成熟、完整，经过一番研究之后，目前至少能勉强在生产环境中使用。</p>
<p> <a href="http://code.google.com/p/memcached/wiki/Clients">从memcached官方wiki中可以看到</a>，python还是有不少可以使用的lib的，但是经过测试和比较之后，发现选择的方案也不多。我们还是从比较出名的web框架入手吧，毫无疑问，Django是目前python最好的框架之一，在生产环境使用它的人也比较多，而且它的文档很详细，<a href="http://docs.djangoproject.com/en/dev/topics/cache/#memcached">有一节就是专门提到cache的部分</a> 。Django推荐使用cmemcache，在不能使用cmemcache的情况（例如windows机器），就使用python-memcached。经过我的实际测试，这个或许不是性能最好，版本最新，功能最多的选择，但是是一个很稳靠的选择。</p>
<p>cmemcache封装的是libmemcache，这个类库很久没有更新了，目前libmemcached比较活跃，功能也比较全面。<a href="http://code.google.com/p/python-libmemcached">这里值得一提的是Python libmemcached是来自豆瓣贡献的类库</a>，不过因为使用的人不多，所以最终我没有选择。<a href="http://pypi.python.org/pypi/pylibmc">另外还有一个pylibmc也是对libmemcached的封装</a>，这是cmemcache_hash的作者写的类库（稍后会提到cmemcache_hash），很可惜我一直没有编译成功。</p>
<p>但是实际使用cmemcache/python-memcached的组合也是要进行一些封装的，<a href="http://weavesky.com/2009/02/14/is-python-cmemcache-threadsafe/">首先有cmemcache不是线程安全的问题</a> ，另外就是Python libmemcached的hash算法有问题。什么？你从来不用超过1个的memcached服务器？我还是建议你至少用2个memcached服务器吧，至少有backup。</p>
<p>而cmemcache和Python libmemcached的hash算法不一样。<a href="http://pypi.python.org/pypi/cmemcache_hash">这里我们还需要cmemcache_hash</a>，名字可能有少少误导，它的作用是领到Python libmemcached的hash跟cmemcache一致</p>
<p>于是我们的封装代码大概是这样</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">import</span> cmemcache <span style="color: #ff7700;font-weight:bold;">as</span> memcache
    logger.<span style="color: black;">warn</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'load cmemcache'</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># although cmemcache might be thread safe</span>
    <span style="color: #808080; font-style: italic;"># but we still make it thread local</span>
    <span style="color: #808080; font-style: italic;"># also it can use separate socket connect in each thread for more performance</span>
    <span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> local
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>, e:
    logger.<span style="color: black;">warn</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'cmemcache error: %s'</span>, e<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">import</span> memcache
    <span style="color: #ff7700;font-weight:bold;">import</span> cmemcache_hash
    logger.<span style="color: black;">warn</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'load python-memcache with cmemcache_hash'</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># python-memcache is already using thread local</span>
    <span style="color: #ff7700;font-weight:bold;">class</span> local<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Memcache<span style="color: black;">&#40;</span>local<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, servers<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>._current = memcache.<span style="color: black;">Client</span><span style="color: black;">&#40;</span>servers<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> current<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._current</pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2009/05/15/python-memcached-lib-in-production/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>python cmemcache是不是线程安全的</title>
		<link>http://weavesky.com/2009/02/14/is-python-cmemcache-threadsafe/</link>
		<comments>http://weavesky.com/2009/02/14/is-python-cmemcache-threadsafe/#comments</comments>
		<pubDate>Sat, 14 Feb 2009 03:45:12 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=560</guid>
		<description><![CDATA[答案是，我不确定，但是我更倾向于认为它是线程不安全的
原因在于
1、网上任何讨论，都从未提及过cmemcache所用的libmemcache是线程安全的，也没有提及过cmemcache实现了线程安全，而libmemcached从一开始就强调自己是线程安全的
2、我个人不太熟悉C代码，所以看libmemcache的代码也没有办法判断出来，但是我看到cmemcache的c接口代码在针对socket操作的代码前后使用了Py_BEGIN_ALLOW_THREADS;Py_END_ALLOW_THREADS;进行包围，而这两个宏的作用，是解除GIL，让多线程可以进入相应的代码。天啊，本来我们还期望对socket操作进行加锁，现在竟然还竟然解除GIL。本来因为GIL的存在，可能还有些希望，现在cmemcache为了让socket操作不要阻塞，反而令到自己是线程不安全的。（如有理解错误请指正）
3、python一直习惯的部署模型是单线程多进程，所以线程安全对很多类库来说，并不重要也不考虑。但是现在越来越多的python程序部署使用了多线程的模型（又或者多线程多进程），线程安全问题也凸显出来
综上所属，我的结论是，我不确定是否线程安全的，呵呵，但是我认为要进行线程安全的修正，至少没有坏处
修正方法很简单，直接套用python-memcached的做法（至于这种做法的优劣也在那篇文章有提及到）

import cmemcache as memcache
from threading import local
class Memcache&#40;local&#41;:
    def __init__&#40;self, servers&#41;:
        self._current = memcache.Client&#40;servers&#41;
    def current&#40;self&#41;:
        return self._current

]]></description>
			<content:encoded><![CDATA[<p>答案是，我不确定，但是我更倾向于认为它是线程不安全的</p>
<p>原因在于<br />
1、网上任何讨论，都从未提及过cmemcache所用的libmemcache是线程安全的，也没有提及过cmemcache实现了线程安全，而libmemcached从一开始就强调自己是线程安全的<br />
2、我个人不太熟悉C代码，所以看libmemcache的代码也没有办法判断出来，但是我看到cmemcache的c接口代码在针对socket操作的代码前后使用了Py_BEGIN_ALLOW_THREADS;Py_END_ALLOW_THREADS;进行包围，而这两个宏的作用，是解除GIL，让多线程可以进入相应的代码。天啊，本来我们还期望对socket操作进行加锁，现在竟然还竟然解除GIL。本来因为GIL的存在，可能还有些希望，现在cmemcache为了让socket操作不要阻塞，反而令到自己是线程不安全的。（如有理解错误请指正）<br />
3、python一直习惯的部署模型是单线程多进程，所以线程安全对很多类库来说，并不重要也不考虑。但是现在越来越多的python程序部署使用了多线程的模型（又或者多线程多进程），线程安全问题也凸显出来</p>
<p>综上所属，我的结论是，我不确定是否线程安全的，呵呵，但是我认为要进行线程安全的修正，至少没有坏处</p>
<p>修正方法很简单，直接套用<a href="http://weavesky.com/2009/01/22/is-python-memcached-threadsafe/">python-memcached的做法</a>（至于这种做法的优劣也在那篇文章有提及到）</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> cmemcache <span style="color: #ff7700;font-weight:bold;">as</span> memcache
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> local
<span style="color: #ff7700;font-weight:bold;">class</span> Memcache<span style="color: black;">&#40;</span>local<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, servers<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>._current = memcache.<span style="color: black;">Client</span><span style="color: black;">&#40;</span>servers<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> current<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._current</pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2009/02/14/is-python-cmemcache-threadsafe/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>python-memcached是不是线程安全的</title>
		<link>http://weavesky.com/2009/01/22/is-python-memcached-threadsafe/</link>
		<comments>http://weavesky.com/2009/01/22/is-python-memcached-threadsafe/#comments</comments>
		<pubDate>Thu, 22 Jan 2009 07:16:45 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=553</guid>
		<description><![CDATA[答案是肯定的，前提你在使用Python 2.4+和python-memcached 1.36+
为什么我们需要线程安全的memcached client，因为我们的实际应用一般是多线程的模型，例如cherrypy、twisted，如果python-memcached不是线程安全的话，引起的问题不仅仅是并发修改共享变量这么简单，是外部socket链接的数据流的混乱
python-memcached怎么实现线程安全的呢？查看源代码看到

try:
    # Only exists in Python 2.4+
    from threading import local
except ImportError:
    # TODO:  add the pure-python local implementation
    class local&#40;object&#41;:
        pass
&#160;
&#160;
class Client&#40;local&#41;:

很取巧的让Client类继承threading.local，也就是Client里面的每一个属性都是跟当前线程绑定的。实现虽然不太优雅，但是很实在
但是别以为这样就可以随便在线程里面用python-memcached了，因为这种thread local的做法，你的应用必须要使用thread pool的模式，而不能不停创建销毁thread，因为每一个新线程的创建，对于就会使用一个全新的Client，也就是一个全新的socket链接，如果不停打开创建销毁thread的话，就会导致不停的创建销毁socket链接，导致性能大量下降。幸好，无论是cherrypy还是twisted，都是使用了thread pool的模式
参考资料：
http://code.djangoproject.com/ticket/3701
http://lists.danga.com/pipermail/memcached/2007-June/004482.html
]]></description>
			<content:encoded><![CDATA[<p>答案是肯定的，前提你在使用Python 2.4+和python-memcached 1.36+<br />
为什么我们需要线程安全的memcached client，因为我们的实际应用一般是多线程的模型，例如cherrypy、twisted，如果python-memcached不是线程安全的话，引起的问题不仅仅是并发修改共享变量这么简单，是外部socket链接的数据流的混乱<br />
python-memcached怎么实现线程安全的呢？查看源代码看到</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #808080; font-style: italic;"># Only exists in Python 2.4+</span>
    <span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> local
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
    <span style="color: #808080; font-style: italic;"># TODO:  add the pure-python local implementation</span>
    <span style="color: #ff7700;font-weight:bold;">class</span> local<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Client<span style="color: black;">&#40;</span>local<span style="color: black;">&#41;</span>:</pre></div></div>

<p>很取巧的让Client类继承threading.local，也就是Client里面的每一个属性都是跟当前线程绑定的。实现虽然不太优雅，但是很实在<br />
但是别以为这样就可以随便在线程里面用python-memcached了，因为这种thread local的做法，你的应用必须要使用thread pool的模式，而不能不停创建销毁thread，因为每一个新线程的创建，对于就会使用一个全新的Client，也就是一个全新的socket链接，如果不停打开创建销毁thread的话，就会导致不停的创建销毁socket链接，导致性能大量下降。幸好，无论是cherrypy还是twisted，都是使用了thread pool的模式</p>
<p>参考资料：<br />
<a href="http://code.djangoproject.com/ticket/3701">http://code.djangoproject.com/ticket/3701</a><br />
<a href="http://lists.danga.com/pipermail/memcached/2007-June/004482.html">http://lists.danga.com/pipermail/memcached/2007-June/004482.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2009/01/22/is-python-memcached-threadsafe/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mercurial和Git的主要区别</title>
		<link>http://weavesky.com/2008/08/17/difference-between-mercurial-git/</link>
		<comments>http://weavesky.com/2008/08/17/difference-between-mercurial-git/#comments</comments>
		<pubDate>Sun, 17 Aug 2008 03:17:58 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[mercurial]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=550</guid>
		<description><![CDATA[1、Mercurial用Python开发，Git用C开发，相对来说，Git比较快，但是Mercurial的性能也不差
2、Mercurial对windows平台支持比较好，而且有一个开发中的TortoiseHg，而且NetBeans内置支持，Git主要还是用命令行，而且对windows不太友好
3、Mercurial核心指令只有10个左右，Git核心指令几十个，全部指令更多了，Mercurial比较简单，也容易上手
4、Mercurial一个目录树就是一个分支，需要使用分支就必须clone一份完整的目录树，这样比较浪费空间，而且使用IDE的时候要为新分支开一个新项目，Git支持本地分支，在一个目录树里面开无限个分支，切换非常方便迅速。这个功能也是我慢慢迁移到Git上的主要原因
5、Git内置对SVN的支持，现在也很多人在SVN repo上使用Git，方便自己的分支开发，当然跟别人合作还是会受到SVN本身的限制，Mercurial有一个第三方的hgsvn，最初的版本还有编码问题，现在已经修正了，但是它只能从SVN导入数据到Mercurial，不能把在Mercurial做的提交导入SVN，作者说未来会制作这个功能，这样就限制了hgsvn的使用，只能作为类似备份的性质
]]></description>
			<content:encoded><![CDATA[<p>1、Mercurial用Python开发，Git用C开发，相对来说，Git比较快，但是Mercurial的性能也不差<br />
2、Mercurial对windows平台支持比较好，而且有一个开发中的TortoiseHg，而且NetBeans内置支持，Git主要还是用命令行，而且对windows不太友好<br />
3、Mercurial核心指令只有10个左右，Git核心指令几十个，全部指令更多了，Mercurial比较简单，也容易上手<br />
4、Mercurial一个目录树就是一个分支，需要使用分支就必须clone一份完整的目录树，这样比较浪费空间，而且使用IDE的时候要为新分支开一个新项目，Git支持本地分支，在一个目录树里面开无限个分支，切换非常方便迅速。这个功能也是我慢慢迁移到Git上的主要原因<br />
5、Git内置对SVN的支持，现在也很多人在SVN repo上使用Git，方便自己的分支开发，当然跟别人合作还是会受到SVN本身的限制，Mercurial有一个第三方的hgsvn，最初的版本还有编码问题，现在已经修正了，但是它只能从SVN导入数据到Mercurial，不能把在Mercurial做的提交导入SVN，作者说未来会制作这个功能，这样就限制了hgsvn的使用，只能作为类似备份的性质</p>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2008/08/17/difference-between-mercurial-git/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>在Squid后面的Nginx如何记录客户端IP</title>
		<link>http://weavesky.com/2008/07/17/nginx-log-remote-ip-behind-squid/</link>
		<comments>http://weavesky.com/2008/07/17/nginx-log-remote-ip-behind-squid/#comments</comments>
		<pubDate>Thu, 17 Jul 2008 08:30:24 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[互联网]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=549</guid>
		<description><![CDATA[（这是我在去年12月的时候查到的资料，不知道现在nginx的文档有没有更完善）
通常我们会在web server前面部署squid，这个时候web server的所有请求都是来自squid，那我们的web server的日志记录到的所有请求都是squid服务器的IP。这个时候，我们会让squid发出一个X-Forwarded-For的header包含真正客户端的IP，我们需要修改web server的日志记录字段用header X-Forwarded-For代替remote_ip，apache的修改方法很简单，这里就不说了。
这里要说的是nginx的修改方法，其实原理很简单，但是nginx的文档里面完全没有提及怎么拿某个header，ft。最后还是在maillist找到答案的
http://thread.gmane.org/gmane.comp.web.nginx.english/944/focus=946

All client headers are available as $http_&#8230; variables.
For example, &#8220;X-Strange-Header&#8221; as $http_x_strange_header.

剩下的就很简单了
]]></description>
			<content:encoded><![CDATA[<p>（这是我在去年12月的时候查到的资料，不知道现在nginx的文档有没有更完善）<br />
通常我们会在web server前面部署squid，这个时候web server的所有请求都是来自squid，那我们的web server的日志记录到的所有请求都是squid服务器的IP。这个时候，我们会让squid发出一个X-Forwarded-For的header包含真正客户端的IP，我们需要修改web server的日志记录字段用header X-Forwarded-For代替remote_ip，apache的修改方法很简单，这里就不说了。<br />
这里要说的是nginx的修改方法，其实原理很简单，但是nginx的文档里面完全没有提及怎么拿某个header，ft。最后还是在maillist找到答案的</p>
<p>http://thread.gmane.org/gmane.comp.web.nginx.english/944/focus=946</p>
<blockquote><p>
All client headers are available as $http_&#8230; variables.<br />
For example, &#8220;X-Strange-Header&#8221; as $http_x_strange_header.
</p></blockquote>
<p>剩下的就很简单了</p>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2008/07/17/nginx-log-remote-ip-behind-squid/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>一份未使用的rails ppt</title>
		<link>http://weavesky.com/2008/06/23/rails-ppt/</link>
		<comments>http://weavesky.com/2008/06/23/rails-ppt/#comments</comments>
		<pubDate>Mon, 23 Jun 2008 12:58:09 +0000</pubDate>
		<dc:creator>Sparkle</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[ppt]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://weavesky.com/?p=546</guid>
		<description><![CDATA[去年受朋友所托,准备了一份在广州BEA user group演讲的rails ppt
可惜后来user group搁浅&#8230;
点击下面的链接下载
[download id="1"]
ppt是去年11月准备的,并未完全完成
]]></description>
			<content:encoded><![CDATA[<p>去年受朋友所托,准备了一份在广州BEA user group演讲的rails ppt<br />
可惜后来user group搁浅&#8230;</p>
<p>点击下面的链接下载<br />
[download id="1"]</p>
<p>ppt是去年11月准备的,并未完全完成</p>
]]></content:encoded>
			<wfw:commentRss>http://weavesky.com/2008/06/23/rails-ppt/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 2.367 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2010-03-12 11:23:17 -->
