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的时候默认 去加载他。就完事咯。