netty圖片伺服器搭建
A. netty系列之:NIO和netty詳解
netty為什麼快呢?這是因為netty底層使用了java的NIO技術,並在其基礎上進行了性能的優化,雖然netty不是單純的JAVA nio,但是netty的底層還是基於的是nio技術。
nio是JDK1.4中引入的,用於區別於傳統的IO,所以nio也可以稱之為new io。
nio的三大核心是Selector,channel和Buffer,本文我們將會深入探究NIO和netty之間的關系。
在講解netty中的NIO實現之前,我們先來回顧一下JDK中NIO的selector,channel是怎麼工作的。對於NIO來說selector主要用來接受客戶端的連接,所以一般用在server端。我們以一個NIO的伺服器端和客戶端聊天室為例來講解NIO在JDK中是怎麼使用的。
因為是一個簡單的聊天室,我們選擇Socket協議為基礎的ServerSocketChannel,首先就是open這個Server channel:
然後向server channel中注冊selector:
雖然是NIO,但是對於Selector來說,它的select方法是阻塞方法,只有找到匹配的channel之後才會返回,為了多次進行select操作,我們需要在一個while循環裡面進行selector的select操作:
selector中會有一些SelectionKey,SelectionKey中有一些表示操作狀態的OP Status,根據這個OP Status的不同,selectionKey可以有四種狀態,分別是isReadable,isWritable,isConnectable和isAcceptable。
當SelectionKey處於isAcceptable狀態的時候,表示ServerSocketChannel可以接受連接了,我們需要調用register方法將serverSocketChannel accept生成的socketChannel注冊到selector中,以監聽它的OP READ狀態,後續可以從中讀取數據:
當selectionKey處於isReadable狀態的時候,表示可以從socketChannel中讀取數據然後進行處理:
上面的serverResponse方法中,從selectionKey中拿到對應的SocketChannel,然後調用SocketChannel的read方法,將channel中的數據讀取到byteBuffer中,要想回復消息到channel中,還是使用同一個socketChannel,然後調用write方法回寫消息給client端,到這里一個簡單的回寫客戶端消息的server端就完成了。
接下來就是對應的NIO客戶端,在NIO客戶端需要使用SocketChannel,首先建立和伺服器的連接:
然後就可以使用這個channel來發送和接受消息了:
向channel中寫入消息可以使用write方法,從channel中讀取消息可以使用read方法。
這樣一個NIO的客戶端就完成了。
雖然以上是NIO的server和client的基本使用,但是基本上涵蓋了NIO的所有要點。接下來我們來詳細了解一下netty中NIO到底是怎麼使用的。
以netty的ServerBootstrap為例,啟動的時候需要指定它的group,先來看一下ServerBootstrap的group方法:
ServerBootstrap可以接受一個EventLoopGroup或者兩個EventLoopGroup,EventLoopGroup被用來處理所有的event和IO,對於ServerBootstrap來說,可以有兩個EventLoopGroup,對於Bootstrap來說只有一個EventLoopGroup。兩個EventLoopGroup表示acceptor group和worker group。
EventLoopGroup只是一個介面,我們常用的一個實現就是NioEventLoopGroup,如下所示是一個常用的netty伺服器端代碼:
這里和NIO相關的有兩個類,分別是NioEventLoopGroup和NioServerSocketChannel,事實上在他們的底層還有兩個類似的類分別叫做NioEventLoop和NioSocketChannel,接下來我們分別講解一些他們的底層實現和邏輯關系。
NioEventLoopGroup和DefaultEventLoopGroup一樣都是繼承自MultithreadEventLoopGroup:
他們的不同之處在於newChild方法的不同,newChild用來構建Group中的實際對象,NioEventLoopGroup來說,newChild返回的是一個NioEventLoop對象,先來看下NioEventLoopGroup的newChild方法:
這個newChild方法除了固定的executor參數之外,還可以根據NioEventLoopGroup的構造函數傳入的參數來實現更多的功能。
這里參數中傳入了SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler、taskQueueFactory和tailTaskQueueFactory這幾個參數,其中後面的兩個EventLoopTaskQueueFactory並不是必須的。
最後所有的參數都會傳遞給NioEventLoop的構造函數用來構造出一個新的NioEventLoop。
在詳細講解NioEventLoop之前,我們來研讀一下傳入的這幾個參數類型的實際作用。
SelectorProvider是JDK中的類,它提供了一個靜態的provider()方法可以從Property或者ServiceLoader中載入對應的SelectorProvider類並實例化。
另外還提供了openDatagramChannel、openPipe、openSelector、openServerSocketChannel和openSocketChannel等實用的NIO操作方法。
SelectStrategyFactory是一個介面,裡面只定義了一個方法,用來返回SelectStrategy:
什麼是SelectStrategy呢?
先看下SelectStrategy中定義了哪些Strategy:
SelectStrategy中定義了3個strategy,分別是SELECT、CONTINUE和BUSY_WAIT。
我們知道一般情況下,在NIO中select操作本身是一個阻塞操作,也就是block操作,這個操作對應的strategy是SELECT,也就是select block狀態。
如果我們想跳過這個block,重新進入下一個event loop,那麼對應的strategy就是CONTINUE。
BUSY_WAIT是一個特殊的strategy,是指IO 循環輪詢新事件而不阻塞,這個strategy只有在epoll模式下才支持,NIO和Kqueue模式並不支持這個strategy。
RejectedExecutionHandler是netty自己的類,和 java.util.concurrent.RejectedExecutionHandler類似,但是是特別針對SingleThreadEventExecutor來說的。這個介面定義了一個rejected方法,用來表示因為SingleThreadEventExecutor容量限制導致的任務添加失敗而被拒絕的情況:
EventLoopTaskQueueFactory是一個介面,用來創建存儲提交給EventLoop的taskQueue:
這個Queue必須是線程安全的,並且繼承自java.util.concurrent.BlockingQueue.
講解完這幾個參數,接下來我們就可以詳細查看NioEventLoop的具體NIO實現了。
首先NioEventLoop和DefaultEventLoop一樣,都是繼承自SingleThreadEventLoop:
表示的是使用單一線程來執行任務的EventLoop。
首先作為一個NIO的實現,必須要有selector,在NioEventLoop中定義了兩個selector,分別是selector和unwrappedSelector:
在NioEventLoop的構造函數中,他們是這樣定義的:
首先調用openSelector方法,然後通過返回的SelectorTuple來獲取對應的selector和unwrappedSelector。
這兩個selector有什麼區別呢?
在openSelector方法中,首先通過調用provider的openSelector方法返回一個Selector,這個Selector就是unwrappedSelector:
然後檢查DISABLE_KEY_SET_OPTIMIZATION是否設置,如果沒有設置那麼unwrappedSelector和selector實際上是同一個Selector:
DISABLE_KEY_SET_OPTIMIZATION表示的是是否對select key set進行優化:
如果DISABLE_KEY_SET_OPTIMIZATION被設置為false,那麼意味著我們需要對select key set進行優化,具體是怎麼進行優化的呢?
先來看下最後的返回:
最後返回的SelectorTuple第二個參數就是selector,這里的selector是一個對象。
繼承自selector,構造函數傳入的第一個參數是一個delegate,所有的Selector中定義的方法都是通過調用
delegate來實現的,不同的是對於select方法來說,會首先調用selectedKeySet的reset方法,下面是以isOpen和select方法為例觀察一下代碼的實現:
selectedKeySet是一個SelectedSelectionKeySet對象,是一個set集合,用來存儲SelectionKey,在openSelector()方法中,使用new來實例化這個對象:
netty實際是想用這個SelectedSelectionKeySet類來管理Selector中的selectedKeys,所以接下來netty用了一個高技巧性的對象替換操作。
首先判斷系統中有沒有sun.nio.ch.SelectorImpl的實現:
SelectorImpl中有兩個Set欄位:
這兩個欄位就是我們需要替換的對象。如果有SelectorImpl的話,首先使用Unsafe類,調用PlatformDependent中的objectFieldOffset方法拿到這兩個欄位相對於對象示例的偏移量,然後調用putObject將這兩個欄位替換成為前面初始化的selectedKeySet對象:
如果系統設置不支持Unsafe,那麼就用反射再做一次:
還記得前面我們提到的selectStrategy嗎?run方法通過調用selectStrategy.calculateStrategy返回了select的strategy,然後通過判斷
strategy的值來進行對應的處理。
如果strategy是CONTINUE,這跳過這次循環,進入到下一個loop中。
BUSY_WAIT在NIO中是不支持的,如果是SELECT狀態,那麼會在curDeadlineNanos之後再次進行select操作:
如果strategy > 0,表示有拿到了SelectedKeys,那麼需要調用processSelectedKeys方法對SelectedKeys進行處理:
上面提到了NioEventLoop中有兩個selector,還有一個selectedKeys屬性,這個selectedKeys存儲的就是Optimized SelectedKeys,如果這個值不為空,就調用processSelectedKeysOptimized方法,否則就調用processSelectedKeysPlain方法。
processSelectedKeysOptimized和processSelectedKeysPlain這兩個方法差別不大,只是傳入的要處理的selectedKeys不同。
處理的邏輯是首先拿到selectedKeys的key,然後調用它的attachment方法拿到attach的對象:
如果channel還沒有建立連接,那麼這個對象可能是一個NioTask,用來處理channelReady和channelUnregistered的事件。
如果channel已經建立好連接了,那麼這個對象可能是一個AbstractNioChannel。
針對兩種不同的對象,會去分別調用不同的processSelectedKey方法。
對第一種情況,會調用task的channelReady方法:
對第二種情況,會根據SelectionKey的readyOps()的各種狀態調用ch.unsafe()中的各種方法,去進行read或者close等操作。
NioEventLoop雖然也是一個SingleThreadEventLoop,但是通過使用NIO技術,可以更好的利用現有資源實現更好的效率,這也就是為什麼我們在項目中使用NioEventLoopGroup而不是DefaultEventLoopGroup的原因。
B. netty伺服器在接收消息後,怎麼向另一台伺服器傳遞消息
第一種,netty伺服器接收到消息後,在channelRead方法里可以在起一個客戶端,通過這個客戶端向另一台伺服器傳遞消息。第二種,創建一個消息中轉的類,這個類可以接收消息,然後創建一個netty客戶端再將消息中轉類的消息傳遞給另一台伺服器。兩種都可以,只是第一種是內置客戶端,個人覺得第二種更靈活,不過我在做的時候採用的是第一種方法。
C. android 怎麼使用netty
Netty是由JBOSS提供的一個java開源框架。Netty提供非同步的、事件驅動的網路應用程序框架和工具,用以快速開發高性能、高可靠性的網路伺服器和客戶端程序。也就是說,Netty是一個基於NIO的客戶,伺服器端編程框架,它在socket的基礎上根據各種常用的應用協議又進一步封裝,提供更便利的介面。如果需要快速搭建一個C/S服務框架,那Netty過來用是沒錯。反過來你的情況是需要學習這個課程,你應該掌握基本的socket編程及其通信原理,所以學習時直接用socket編程比較好。也許哪一天,你靈感來了,編出一個比Netty更好的框架,一個更牛的軟體。
D. 用Netty作http靜態資源伺服器,類似Nginx這樣的,大一點的文件響應不正常怎麼回事
為什麼Nginx的性能要比Apache高得多?
????這得益於Nginx使用了最新的epoll(Linux?2.6內核)和kqueue(freebsd)網路I/O模型,而Apache則使用的是傳統的select模型。目前Linux下能夠承受高並發訪問的Squid、Memcached都採用的是epoll網路I/O模型。
處理大量的連接的讀寫,Apache所採用的select網路I/O模型非常低效。下面用一個比喻來解析Apache採用的select模型和Nginx採用的epoll模型進行之間的區別:
假設你在大學讀書,住的宿舍樓有很多間房間,你的朋友要來找你。select版宿管大媽就會帶著你的朋友挨個房間去找,直到找到你為止。而epoll版宿管大媽會先記下每位同學的房間號,你的朋友來時,只需告訴你的朋友你住在哪個房間即可,不用親自帶著你的朋友滿大樓找人。如果來了10000個人,都要找自己住這棟樓的同學時,select版和epoll版宿管大媽,誰的效率更高,不言自明。同理,在高並發伺服器中,輪詢I/O是最耗時間的操作之一,select和epoll的性能誰的性能更高,同樣十分明了。為什麼會出現502錯誤呢?
nginx出現502有很多原因,但大部分原因可以歸結為資源數量不夠用,也就是說後端php-fpm處理有問題,nginx將正確的客戶端請求發給了後端的php-fpm進程,但是因為php-fpm進程的問題導致不能正確解析php代碼,最終返回給了客戶端502錯誤。優化php-fpm,優化代碼,加大內存才是解決502的根源。10000並發的話,nginx的表現怎麼樣?
2009年9月3日下午2:30,金山游戲《劍俠情緣網路版叄》臨時維護1小時,大量玩家上官網,論壇、評論、客服等動態應用Nginx伺服器集群,每台伺服器的Nginx活動連接數達到2.8萬。
E. 如何構建一個基於netty的後端伺服器
下面將分析手頭上一個項目,運用的技術很全,值得學習,先做一個簡單介紹,當然業務部分代碼就不講了。
整個工程採用maven來管理,主要的技術是spring+jedis+netty+disruptor.看這個組合,這個伺服器端性能應該很不錯。
這個工程又引發我對技術無限熱愛 ,哈哈。
這
個工程,目前主要是針對一些基於json/xml/text格式的請求,同時也是支持標准手機請求的,當然,可以自定義一些其他格式或者pc端的請求,而
且針對不同URI,後面掛了不同的handler,這些可能都是一些web處理的基本思想,只是脫離了常規的web容器或者應用伺服器。
xml工具採用xstram來處理,兩個字,方便。
json工具採用jackson\不知道和業界出名的fastjson\gson\sf.json有何區別,待鑒定。
客
戶端的請求,統一繼承ClientRequestModel,經過編碼統一轉化為domainMessage,交由disruptor來處理,其實oop
里什麼繼承,實現,封裝思想,大部分都在圍繞一個東西在走,一句話,把看似各有稜角的東西如何轉化為共同的東西,求同存異啊(比如,水,石頭,空氣等,如
果在這一層,我們沒法統一用一個特徵來表示,我們可以先把它轉化為分子,那是不是可以用同一個東西來表示呢?如何高度抽象封裝,這真是一門藝術)。
看這個工程對客戶端請求,是如何一步步處理的,message->request->event 交由disruptor來處理,很美妙的思想。在了解這些之前,我們有必要深入學習一下disruptor,很特別的一個框架,宣言很牛逼,中文文檔在這里(http://ifeve.com/dissecting-disruptor-whats-so-special/),E文好的同學請移步到這里(http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-whats-so-special.html)
了解disruptor之前,先學習下ringbuffer是如何實現的?
1、ringbuffer的特別之處:
只有一個指針,沒有尾指針,基於數組,且不會刪除元素,元素會覆蓋,充分利用緩存行,減少垃圾回收。
2、如何從ringbuffer讀取數據:
------------------------------------------2013-9-9 補充-----------------------------------------------------
下面主要講一下請求如何處理這塊架構吧,其實架構這個東西,說簡單一點,就是一種簡單可擴展的實現方式,在某些程度上,不要太在意性能。
底層通信建立在netty之上,基本沒做任何改動
Java代碼
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
private ChannelUpstreamHandler channelUpstreamHandler;
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
// Uncomment the following line if you want HTTPS
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
//engine.setUseClientMode(false);
//pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("decoder", new HttpRequestDecoder());
// Uncomment the following line if you don't want to handle HttpChunks.
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
// Remove the following line if you don't want automatic content compression.
pipeline.addLast("deflater", new HttpContentCompressor());
//pipeline.addLast("handler", new HttpRequestHandler());
pipeline.addLast("handler", channelUpstreamHandler);
return pipeline;
}
public void setChannelUpstreamHandler(ChannelUpstreamHandler channelUpstreamHandler) {
this.channelUpstreamHandler = channelUpstreamHandler;
}
}
相關spring配置
Java代碼
<bean id="httpServerPipelineFactory" class="com.yunchao.cm.network.http.HttpServerPipelineFactory">
<property name="channelUpstreamHandler" ref="httpRequestHandler"/>
</bean>
Java代碼
<bean id="httpRequestHandler" class="com.yunchao.cm.network.http.HttpRequestHandler">
<property name="urlMaps">
<map>
<entry key="/payorder">
<ref bean="payOrderCodecFactory"/>
</entry>
<entry key="/question">
<ref bean="questionCodecFactory"/>
</entry>
<entry key="/sms">
<ref bean="smsCodecFactory"/>
</entry>
代碼太多,不全部貼出來,後面整理一下放到我的github上去。
基如此,我們還是得定義一個handler,繼承simpleChannelUpstreamHander,並重寫了messageReceied方法,具體在這里。
Java代碼
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
String url = queryStringDecoder.getPath();
CodecFactory codecFactory = urlMaps.get(url);
if (null == codecFactory) {
logger.error("unsupported url:{} request.", url);
//sendError(ctx, BAD_REQUEST);
e.getChannel().close();
return;
}
//獲取cmwap網路中的手機號碼
String phone = PhoneUtils.getPhone(request.getHeader("x-up-calling-line-id"));
if (request.getMethod().equals(HttpMethod.POST)) {
ChannelBuffer content = request.getContent();
String postParams = content.toString(CharsetUtil.UTF_8);
logger.debug("request content:{}", postParams);
ClientRequestModel model = (ClientRequestModel) codecFactory.decode(postParams);
model.setProperty(model.MESSAGE_EVENT_KEY, e);
model.setProperty(model.HTTP_REQUEST_KEY, request);
model.setProperty(model.HTTP_PHONE_KEY, phone);
InetSocketAddress remoteAddress = (InetSocketAddress) e.getRemoteAddress();
model.setProperty(model.IP_KEY, remoteAddress.getAddress().getHostAddress());
logger.info("user request model:{}", model);
model.fireSelf();
Java代碼
@Override
public DomainMessage fireSelf() {
DomainMessage em = new DomainMessage(this);
EventUtils.fireEvent(em, "alipayNotifyState");
return em;
}
看到這里基本上能夠清楚了,是如何把客戶端請求包裝成ClientRequestModel了,且後面涉及到處理的對象,全部繼承它,在整個架構之
中,has a 優於 is
a,對於客戶端netty的一些對象,也是存儲在ClientRequestModel中,codec無非也是採用了xml/json/kv,如斯,實現
了位元組與對象之間的轉換。
F. 如何用Netty寫一個自己的RPC框架
最後,值得一提的是,衡量一個RPC框架性能的好壞與否,RPC的網路I/O模型的選擇,至關重要。在此基礎上,設計出來的RPC伺服器,可以考慮支持阻塞式同步IO、非阻塞式同步IO、當然還有所謂的多路復用IO模型、非同步IO模型。支持不同的網路IO模型,在高並發的狀態下,處理性能上會有很大的差別。還有一個衡量的標准,就是選擇的傳輸協議。是基於TCP協議、還是HTTP協議、還是UDP協議?對性能也有一定的影響。但是從我目前了解的情況來看,大多數RPC開源實現框架都是基於TCP、或者HTTP的,目測沒有採用UDP協議做為主要的傳輸協議的。
明白了RPC的使用原理和性能要求。現在,我們能不能撇開那些RPC開源框架,自己動手開發一個高性能的RPC伺服器呢?我想,還是可以的。現在本人就使用Java,基於Netty,開發實現一個高性能的RPC伺服器。
G. 如何構建一個基於netty的後端伺服器
Netty服務端創建
當我們直接使用JDK NIO的類庫開發基於NIO的非同步服務端時,需要使用到多路復用器Selector、ServerSocketChannel、SocketChannel、ByteBuffer、SelectionKey等等,相比於傳統的BIO開發,NIO的開發要復雜很多,開發出穩定、高性能的非同步通信框架,一直是個難題。
Netty為了向使用者屏蔽NIO通信的底層細節,在和用戶交互的邊界做了封裝,目的就是為了減少用戶開發工作量,降低開發難度。ServerBootstrap是Socket服務端的啟動輔助類,用戶通過ServerBootstrap可以方便的創建Netty的服務端。
H. 如何構建一個基於netty的後端伺服器
如何構建一個基於netty的後端伺服器,先打個標題
直接上干貨,這個是前奏,比較山寨的實現,大家可先自行看下
下面將分析手頭上一個項目,運用的技術很全,值得學習,先做一個簡單介紹,當然業務部分代碼就不講了。
整個工程採用maven來管理,主要的技術是spring+jedis+netty+disruptor.看這個組合,這個伺服器端性能應該很不錯。
這個工程又引發我對技術無限熱愛
,哈哈。
這個工程,目前主要是針對一些基於json/xml/text格式的請求,同時也是支持標准手機請求的,當然,可以自定義一些其他格式或者pc端的請求,而且針對不同URI,後面掛了不同的handler,這些可能都是一些web處理的基本思想,只是脫離了常規的web容器或者應用伺服器。
xml工具採用xstram來處理,兩個字,方便。
json工具採用jackson\不知道和業界出名的fastjson\gson\sf.json有何區別,待鑒定。
客戶端的請求,統一繼承ClientRequestModel,經過編碼統一轉化為domainMessage,交由disruptor來處理,其實oop里什麼繼承,實現,封裝思想,大部分都在圍繞一個東西在走,一句話,把看似各有稜角的東西如何轉化為共同的東西,求同存異啊(比如,水,石頭,空氣等,如果在這一層,我們沒法統一用一個特徵來表示,我們可以先把它轉化為分子,那是不是可以用同一個東西來表示呢?如何高度抽象封裝,這真是一門藝術)。
看這個工程對客戶端請求,是如何一步步處理的,message->request->event
交由disruptor來處理,很美妙的思想。在了解這些之前,我們有必要深入學習一下disruptor,很特別的一個框架,宣言很牛逼,
了解disruptor之前,先學習下ringbuffer是如何實現的?
1、ringbuffer的特別之處:
只有一個指針,沒有尾指針,基於數組,且不會刪除元素,元素會覆蓋,充分利用緩存行,減少垃圾回收。
2、如何從ringbuffer讀取數據:
------------------------------------------2013-9-9
補充-----------------------------------------------------
下面主要講一下請求如何處理這塊架構吧,其實架構這個東西,說簡單一點,就是一種簡單可擴展的實現方式,在某些程度上,不要太在意性能。
底層通信建立在netty之上,基本沒做任何改動
Java代碼
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
private ChannelUpstreamHandler channelUpstreamHandler;
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
// Uncomment the following line if you want HTTPS
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
//engine.setUseClientMode(false);
//pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("decoder", new HttpRequestDecoder());
// Uncomment the following line if you don't want to handle HttpChunks.
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
// Remove the following line if you don't want automatic content compression.
pipeline.addLast("deflater", new HttpContentCompressor());
//pipeline.addLast("handler", new HttpRequestHandler());
pipeline.addLast("handler", channelUpstreamHandler);
return pipeline;
}
public void setChannelUpstreamHandler(ChannelUpstreamHandler channelUpstreamHandler) {
this.channelUpstreamHandler = channelUpstreamHandler;
}
}
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
private ChannelUpstreamHandler channelUpstreamHandler;
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
// Uncomment the following line if you want HTTPS
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
//engine.setUseClientMode(false);
//pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("decoder", new HttpRequestDecoder());
// Uncomment the following line if you don't want to handle HttpChunks.
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
// Remove the following line if you don't want automatic content compression.
pipeline.addLast("deflater", new HttpContentCompressor());
//pipeline.addLast("handler", new HttpRequestHandler());
pipeline.addLast("handler", channelUpstreamHandler);
return pipeline;
}
public void setChannelUpstreamHandler(ChannelUpstreamHandler channelUpstreamHandler) {
this.channelUpstreamHandler = channelUpstreamHandler;
}
}
相關spring配置
Java代碼
<bean id="httpServerPipelineFactory" class="com.yunchao.cm.network.http.HttpServerPipelineFactory">
<property name="channelUpstreamHandler" ref="httpRequestHandler"/>
</bean>
<bean id="httpServerPipelineFactory" class="com.yunchao.cm.network.http.HttpServerPipelineFactory">
<property name="channelUpstreamHandler" ref="httpRequestHandler"/>
</bean>
Java代碼
<bean id="httpRequestHandler" class="com.yunchao.cm.network.http.HttpRequestHandler">
<property name="urlMaps">
<map>
<entry key="/payorder">
<ref bean="payOrderCodecFactory"/>
</entry>
<entry key="/question">
<ref bean="questionCodecFactory"/>
</entry>
<entry key="/sms">
<ref bean="smsCodecFactory"/>
</entry>
<bean id="httpRequestHandler" class="com.yunchao.cm.network.http.HttpRequestHandler">
<property name="urlMaps">
<map>
<entry key="/payorder">
<ref bean="payOrderCodecFactory"/>
</entry>
<entry key="/question">
<ref bean="questionCodecFactory"/>
</entry>
<entry key="/sms">
<ref bean="smsCodecFactory"/>
</entry>
代碼太多,不全部貼出來,後面整理一下放到我的github上去。
基如此,我們還是得定義一個handler,繼承simpleChannelUpstreamHander,並重寫了messageReceied方法,具體在這里。
Java代碼
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
String url = queryStringDecoder.getPath();
CodecFactory codecFactory = urlMaps.get(url);
if (null == codecFactory) {
logger.error("unsupported url:{} request.", url);
//sendError(ctx, BAD_REQUEST);
e.getChannel().close();
return;
}
//獲取cmwap網路中的手機號碼
String phone = PhoneUtils.getPhone(request.getHeader("x-up-calling-line-id"));
if (request.getMethod().equals(HttpMethod.POST)) {
ChannelBuffer content = request.getContent();
String postParams = content.toString(CharsetUtil.UTF_8);
logger.debug("request content:{}", postParams);
ClientRequestModel model = (ClientRequestModel) codecFactory.decode(postParams);
model.setProperty(model.MESSAGE_EVENT_KEY, e);
model.setProperty(model.HTTP_REQUEST_KEY, request);
model.setProperty(model.HTTP_PHONE_KEY, phone);
InetSocketAddress remoteAddress = (InetSocketAddress) e.getRemoteAddress();
model.setProperty(model.IP_KEY, remoteAddress.getAddress().getHostAddress());
logger.info("user request model:{}", model);
model.fireSelf();
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
String url = queryStringDecoder.getPath();
CodecFactory codecFactory = urlMaps.get(url);
if (null == codecFactory) {
logger.error("unsupported url:{} request.", url);
//sendError(ctx, BAD_REQUEST);
e.getChannel().close();
return;
}
//獲取cmwap網路中的手機號碼
String phone = PhoneUtils.getPhone(request.getHeader("x-up-calling-line-id"));
if (request.getMethod().equals(HttpMethod.POST)) {
ChannelBuffer content = request.getContent();
String postParams = content.toString(CharsetUtil.UTF_8);
logger.debug("request content:{}", postParams);
ClientRequestModel model = (ClientRequestModel) codecFactory.decode(postParams);
model.setProperty(model.MESSAGE_EVENT_KEY, e);
model.setProperty(model.HTTP_REQUEST_KEY, request);
model.setProperty(model.HTTP_PHONE_KEY, phone);
InetSocketAddress remoteAddress = (InetSocketAddress) e.getRemoteAddress();
model.setProperty(model.IP_KEY, remoteAddress.getAddress().getHostAddress());
logger.info("user request model:{}", model);
model.fireSelf();
Java代碼
@Override
public DomainMessage fireSelf() {
DomainMessage em = new DomainMessage(this);
EventUtils.fireEvent(em, "alipayNotifyState");
return em;
}
@Override
public DomainMessage fireSelf() {
DomainMessage em = new DomainMessage(this);
EventUtils.fireEvent(em, "alipayNotifyState");
return em;
}
看到這里基本上能夠清楚了,是如何把客戶端請求包裝成ClientRequestModel了,且後面涉及到處理的對象,全部繼承它,在整個架構之中,has a 優於
is
a,對於客戶端netty的一些對象,也是存儲在ClientRequestModel中,codec無非也是採用了xml/json/kv,如斯,實現了位元組與對象之間的轉換。
除此之外,突然想到剛來杭州工作的第一家公司,基於此,採用的架構師servlet充當伺服器,因為這是一個公司內部的server,而不是一個平台,採用的數據格式也比較單一,就是xml,但是採用的外部類庫也是xstream來處理的,但是整個系統維持的日調用量也是在百萬級別,運用的client則是採用httpclient,對於不同請求後面掛的handler,是在容器啟動時載入到內存中,其餘也沒有什麼亮點了。
轉載,僅供參考,祝你愉快,滿意請採納。
I. netty和tomcat如何一起使用
首先 創建一個 bean 然後用Spring 載入那個bean ,這個bean 的ini方法 就是初始化netty的內容什麼解碼什麼的 自定義處理類什麼的,然後啟動TOMCAT的時候默認 去載入他。就完事咯。