2015年1月7日星期三

大熊君大话NodeJS之------Stream模块 - 大熊君{{bb}}

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
大熊君大话NodeJS之------Stream模块 - 大熊君{{bb}}  阅读原文»

一,开篇分析

流是一个抽象接口,被 Node 中的很多对象所实现。比如对一个 HTTP 服务器的请求是一个流,stdout 也是一个流。流是可读,可写或兼具两者的。

最早接触Stream是从早期的unix开始的, 数十年的实践证明Stream 思想可以很简单的开发出一些庞大的系统。

在unix里,Stream是通过 "|" 实现的。在node中,作为内置的stream模块,很多核心模块和三方模块都使用到。

和unix一样,node stream主要的操作也是.pipe(),使用者可以使用反压力机制来控制读和写的平衡。

Stream 可以为开发者提供可以重复使用统一的接口,通过抽象的Stream接口来控制Stream之间的读写平衡。

一个TCP连接既是可读流,又是可写流,而Http连接则不同,一个http request对象是可读流,而http response对象则是可写流。

流的传输过程默认是以buffer的形式传输的,除非你给他设置其他编码形式,以下是一个例子:

1 var http = require('http') ;
2 var server = http.createServer(function(req,res){
3 res.writeHeader(200, {'Content-Type': 'text/plain'}) ;
4 res.end("Hello,大熊!") ;
5 }) ;
6 server.listen(8888) ;
7 console.log("http server running on port 8888 ...") ;

运行后会有乱码出现,原因就是没有设置指定的字符集,比如:“utf-8”

修改一下就好:

1 var http = require('http') ;
2 var server = http.createServer(function(req,res){
3 res.writeHeader(200,{
4 'Content-Type' : 'text/plain;charset=utf-8' // 添加charset=utf-8
5 }) ;
6 res.end("Hello,大熊!") ;
7 }) ;
8 server.listen(8888) ;
9 console.log("http server running on port 8888 ...") ;

运行结果:

为什么使用Stream

node中的I/O是异步的,因此对磁盘和网络的读写需要通过回调函数来读取数据,下面是一个文件下载例子

上代码:

1 var http = require('http') ;
2 var fs = require('fs') ;
3 var server = http.createServer(function (req, res) {
4 fs.readFile(__dirname + '/data.txt', function (err, data) {
5 res.end(data);
6 }) ;
7 }) ;
8 server.listen(8888) ;

代码可以实现需要的功能,但是服务在发送文件数据之前需要缓存整个文件数据到内存,如果"data.txt"文件很
大并且并发量很大的话,会浪费很多内存。因为用户需要等到整个文件缓存到内存才能接受的文件数据,这样导致
用户体验相当不好。不过还好(req,res)两个参数都是Stream,这样我们可以用fs.createReadStream()代替fs.readFile()。如下:

1 var http = require('http') ;
2 var fs = require('fs') ;
3 var server = http.createServer(function (req, res) {
4 var stream = fs.createReadStream(__dirname + '/data.txt') ;
5 stream.pipe(res) ;
6 }) ;
7 server.listen(8888) ;

.pipe()方法监听fs.createReadStream()'data''end'事件,这样"data.txt"文件就不需要缓存整
个文件,当客户端连接完成之后马上可以发送一个数据块到客户端。使用.pipe()另一个好处是可以解决当客户
端延迟非常大时导致的读写不平衡问题。

有五种基本的Stream:readable,writable,transform,duplex,and "classic” 。(具体使用请自己查阅api)

二,实例引入

当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流。NodeJS中通过各种Stream来提供对数据流的操作。

以大文件拷贝程序为例,我们可以为数据源创建一个只读数据流,示例如下:

1 var rs = fs.createReadStream(pathname);
2 rs.on('data', function (chunk) {
4 doSomething(chunk) ; // 具体细节自己任意发挥
5 });
6 rs.on('end', function () {
8 cleanUp() ;
9 }) ;

代码中data事件会源源不断地被触发,不管doSomething函数是否处理得过来。代码可以继续做如下改造,以解决这个问题。

1 var rs = fs.createReadStream(src) ;
2 rs.on('data', function (chunk) {
3 rs.pause() ;
4 doSomething(chunk, function () {
5 rs.resume() ;
spring beans源码解读之--XmlBeanFactory - davidwang456  阅读原文»

导读:

XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力。从本质上讲,XmlBeanFactory等同于DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考虑使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因为该方案可以从多个xml文件读取资源,并且在解析xml上具有更灵活的可配置性。

1 XmlBeanFactory的使用

典型构造方法:

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}

其中,

reader是XmlBeanDefinitionReader的实例,XmlBeanDefinitionReader继承自AbstractBeanDefinitionReader(http://www.cnblogs.com/davidwang456/p/4190428.html 已经介绍过)。

Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。这样所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者。另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由 ResourceLoader 接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,他的默认实现是 DefaultResourceLoader。

1.1 Resource 接口继承了 InputStreamSource 接口,这个接口中有个 getInputStream 方法,返回的是 InputStream 类。

public interface InputStreamSource {
InputStream getInputStream()
throws IOException;
}
getInputStream设计希望每次请求都返回一个新的流。这种要求在一个场合非常重要,例如javamail api,它在创建多个邮件附件时需要多次读取到流,在这中情况下,它就要求每个getInputStream请求能返回一个新的流。

1.2 抽象资源类AbstractResource继承自Source接口,实现了部分方法。
exists方法检查一个文件或者输入量是否可以打开;
isOpen 通常返回false;
  getUrl和getFile 抛出异常;
toString返回描述信息。

另外有一个有用的方法:lastModified()返回文件的最后修改时间。调用了File.lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。
@Override
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
}

1.3 Resource的实现子类:

AbstractResource的直接子类有:AbstractFileResolvingResource, ByteArrayResource,BeanDefinitionResource, DescriptiveResource, FileSystemResource, InputStreamResource, PathResource, VfsResource。
其中,AbstractFileResolvingResource的直接子类有:ClassPathResource, PortletContextResource, ServletContextResource, UrlResource比较重要的实现类为实现了ContextResource接口的资源类,ContextResource接口继承了Resource接口,通过从一个封装的context中加载资源来扩展Resource接口的功能。例如:可以从ServletContext,PortletContext,普通的classpth路径或者文件系统的相对路径(特别是没有指明前缀,因此适用于本地ResourceLoader的context),其子类实现有:PortletContextResource, ServletContextResource,ClassPathContextResource,ClassRelativeContextResource,FileSystemContextResource.

1.4 资源的加载ResourceLoader,ResourceLoader是一个加载资源的策略接口,可以从classpth或者文件系统中,ApplicationContext需要加载资源功能,使用了其扩展类ResourcePatternReolver。DefaultResourceLoader是该接口的标准实现,适用于一个ApplicationContext外部,可以用在ResourceEditor中。在application允许过程中,使用特定的context子原因加载策略,可以将type资源和资源组通过string的形式设置bean的属性。

(来源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

2. Bean的解析与注册详细过程分析

    (来源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

首先, XmlBeanFactory构造函数this.reader.loadBeanDefinitions(resource);

  1. XmlBeanDefinitionReader加载bean定义:loadBeanDefinitions方法

2. XmlBeanDefinitionReader解析bean定义,并注册:doLoadBeanDefinitions方法

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

3.1. 使用标准的JAXP配置的xml解析器从Resource中加载到Document。

/**
* Load the {
@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler,
int validationMode, boolean namespaceAware) throws Exception {

DocumentBuilderFactory factory
= createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug(
"Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder
= createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}

3.2.根据加载的Document 注册Bean definition。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader
= createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

3.2.1 创建BeanDefinitionDocument的解析器BeanUtils.instantiateClass(this.documentReaderClass)

3.2.2 BeanDefinitionDocument解析器注册bean definition,从根节点<beans>开始

/**
* Register each bean definition within the given root {
@code <beans/>} element.
*/
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec
= root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles
= StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}

阅读更多内容

没有评论:

发表评论