HTTP概念进阶

1.什么是回调?javascript

在Java中,就是类A调用类B中的某个方法b,而后类B又在某个时候反过来调用类A中的某个方法a,对于A来讲,这个a方法便叫作回调方法

pubilc interface CallBack{

public void callbackMethod();

}

public class A implements CallBack{ // A实现接口CallBack

B b = new B();

public void do(){

b.doSomething(this); // A运行时调用B中doSomething方法,以自身传入参数,B已取得A,能够随时回调A所实现的CallBack接口中的方法

}

public void callbackMethod(){ // 对A来讲,该方法就是回调方法

System.out.println("callbackMethod is executing!");

}

}

public class B{

public void doSomething(CallBack cb){ // B拥有一个参数为CallBack接口类型的方法

System.out.println(“I am processing my affairs… ”);

System.out.println(“then, I need invoke callbackMethod…”);

cb.callbackMethod();

}

}


2.什么是同步/异步

进程同步用来实现程序并发执行时候的可再现性。html

一.进程同步及异步的概念java

1.进程同步:就是在发出一个功能调用时,在没有获得结果以前,该调用就不返回。也就是必须一件一件事作,等前一件作完了才能作下一件事.就像早上起床后,先洗涮,而后才能吃饭,不能在洗涮没有完成时,就开始吃饭.按照这个定义,其实绝大多数函数都是同步调用(例如sin,isdigit等)。可是通常而言,咱们在说同步、异步的时候,特指那些须要其余部件协做或者须要必定时间完成的任务。最多见的例子就是node

sendmessage。该函数发送一个消息给某个窗口,在对方处理完消息以前,这个函数不返回。当对方处理完毕之后,该函数才把消息处理函数所返回的lresult值返回给调用者。git

2.异步程序员

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能马上获得结果。实际处理这个调用的部件在完成后,经过状态、通知和回调来通知调用者。github

以casycsocket类为例(注意,csocket从casyncsocket派生,可是其功能已经由异步转化为同步),当一个客户端经过调用connect函数发出一个链接请求后,调用者线程马上能够朝下运行。当链接真正创建起来之后,socket底层会发送一个消息通知该对象。web

这里提到执行部件和调用者经过三种途径返回结果:状态、通知和回调。可使用哪种依赖于执行部件的实现,除非执行部件提供多种选择,不然不受调用者控制。若是执行部件用状态来通知,那么调用者就须要每隔必定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这实际上是一种很严重的错误)。若是是使用通知的方式,效率则很高,由于执行部件几乎不须要作额外的操做。至于回调函数,其实和通知没太多区别。数据库

进程同步的基本概念编程

在计算机系统中,因为资源有限而致使了进程之间的资源竞争和共享,所以,进程的并发执行不只仅是用户程序的执行开始时间的随机性和提升资源利用率的结果,也是资源有限性致使资源的竞争与共享对进程的执行过程进行制约所形成的。那么,在进程的并发执行过程当中存在哪些制约呢?

二.同步与异步传输:

1.异步传输

一般,异步传输是以字符为传输单位,每一个字符都要附加 1 位起始位和 1 位中止位,以标记一个字符的开始和结束,并以此实现数据传输同步。所谓异步传输是指字符与字符(一个字符结束到下一个字符开始)之间的时间间隔是可变的,并不须要严格地限制它们的时间关系。起始位对应于二进制值 0,以低电平表示,占用 1 位宽度。中止位对应于二进制值 1,以高电平表示,占用 1~2 位宽度。一个字符占用 5~8位,具体取决于数据所采用的字符集。例如,电报码字符为 5 位、ASCII码字符为 7 位、汉字码则为8 位。此外,还要附加 1 位奇偶校验位,能够选择奇校验或偶校验方式对该字符实施简单的差错控制。发送端与接收端除了采用相同的数据格式(字符的位数、中止位的位数、有无校验位及校验方式等)外,还应当采用相同的传输速率。典型的速率有:9 600 b/s、19.2kb/s、56kb/s等。

异步传输又称为起止式异步通讯方式,其优势是简单、可靠,适用于面向字符的、低速的异步通讯场合。例如,计算机与Modem之间的通讯就是采用这种方式。它的缺点是通讯开销大,每传输一个字符都要额外附加2~3位,通讯效率比较低。例如,在使用Modem上网时,广泛感受速度很慢,除了传输速率低以外,与通讯开销大、通讯效率低也密切相关。

2. 同步传输

一般,同步传输是以数据块为传输单位。每一个数据块的头部和尾部都要附加一个特殊的字符或比特序列,标记一个数据块的开始和结束,通常还要附加一个校验序列 (如16位或32位CRC校验码),以便对数据块进行差错控制。所谓同步传输是指数据块与数据块之间的时间间隔是固定的,必须严格地规定它们的时间关系。

三.同步阻塞与异步阻塞:

同步是阻塞模式,异步是非阻塞模式。 

个人理解:同步是指两个线程的运行是相关的,其中一个线程要阻塞等待另一个线程的运行。异步的意思是两个线程毫无相关,本身运行本身的。 

同步是指:发送方发出数据后,等接收方发回响应之后才发下一个数据包的通信方式。 

异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通信方式。 

举个不太恰当的例子,就像: 

SendMessage(...) 

TRACE0("just  like  send"); 

PostMessage(...) 

TRACE0("just  like  WSASend  using  overlapped"); 

 SendMessage是调用的时候不返回,等消息响应后才执行TRACE0,这就是同步. 

PostMessage是调用后立刻返回,不用消息响应就执行TRACE0,这就是异步.

四.其它解释:

 同步和异步的区别

 举个例子:普通B/S模式(同步)AJAX技术(异步)

同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事

异步: 请求经过事件触发->服务器处理(这是浏览器仍然能够做其余事情)->处理完毕

同步就是你叫我去吃饭,我听到了就和你去吃饭;若是没有听到,你就不停的叫,直到我告诉你听到了,才一块儿去吃饭。

异步就是你叫我,而后本身去吃饭,我获得消息后可能当即走,也可能等到下班才去吃饭。

因此,要我请你吃饭就用同步的方法,要请我吃饭就用异步的方法,这样你能够省钱。

举个例子 打电话时同步 发消息是异步


3.什么是I/O?
I/O是 input/output的缩写,即输入输出端口。每一个设备都会有一个专用的I/O地址,用来处理本身的输入输出信息。CPU与外部设备、存储器的链接和数据交换都须要经过接口设备来实现,。,习惯上说到接口只是指I/O接口。


4.什么是单线程/多线程?
打个比方,单线程就是你去厨房有烧饭又烧菜,一我的来回跑;多线程就是两我的,一个单作饭,一个单作菜。这样的解释应该比纯理论的好理解一点吧?
再补充一下,多线程就是一个CPU虚拟了几个CPU,而双核就是实际上就有两个线程了,固然,还能够每一个核再去虚拟多个线程(也能够理解成多个流水线吧)
 
 
5.什么是阻塞/非阻塞?
阻塞
    阻塞调用是指调用结果返回以前,当前线程会被挂起。函数只有在获得结果以后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上它们是不一样的。对于同步调用来讲,不少时候当前线程仍是激活的,只是从逻辑上当前函数没有返回而已。例如,咱们在CSocket中调用Receive函数,若是缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各类各样的消息。若是主窗口和调用函数在同一个线程中,除非你在特殊的界面操做函数中调用,其实主界面仍是应该能够刷新。socket接收数据的另一个函数recv则是一个阻塞调用的例子。当socket工做在阻塞模式的时候, 若是没有数据的状况下调用该函数,则当前线程就会被挂起,直到有数据为止。
 
非阻塞
    非阻塞和阻塞的概念相对应,指在不能马上获得结果以前,该函数不会阻塞当前线程,而会马上返回。
 
对象的阻塞模式和阻塞函数调用
 
    对象是否处于阻塞模式和函数是否是阻塞调用有很强的相关性,可是并非一一对应的。阻塞对象上能够有非阻塞的调用方式,咱们能够经过必定的API去轮询状态,在适当的时候调用阻塞函数,就能够避免阻塞。而对于非阻塞对象,调用特殊的函数也能够进入阻塞调用。函数select就是这样的一个例子。
 
 
6.什么是事件?
事件:样本空间的一些子集稍为随机事件,简称事件. 
= =本身理解:大概就是进行了操做产生了变化对应的这个事

7.什么是事件驱动?
早期程序使用输入-操做-输出的机制,整个流程彻底由程序员事先设定好。
面向对象程序设计当中采用的就是事件驱动机制。好比说鼠标左击、双击都是具体事件,根据这些事件启用预先设置的相应动做就是事件驱动机制。



8.什么是基于事件驱动的回调?

这一切都归结于“Node.js是事件驱动的”这一事实。好吧,其实我也不是特别确切的了解这句话的意思。不过我会试着解释,为何它对咱们用Node.js写网络应用(Web based application)是有意义的。

当咱们使用 http.createServer 方法的时候,咱们固然不仅是想要一个侦听某个端口的服务器,咱们还想要它在服务器收到一个HTTP请求的时候作点什么。

问题是,这是异步的:请求任什么时候候均可能到达,可是咱们的服务器却跑在一个单进程中。

写PHP应用的时候,咱们一点也不为此担忧:任什么时候候当有请求进入的时候,网页服务器(一般是Apache)就为这一请求新建一个进程,而且开始从头至尾执行相应的PHP脚本。

那么在咱们的Node.js程序中,当一个新的请求到达8888端口的时候,咱们怎么控制流程呢?

嗯,这就是Node.js/JavaScript的事件驱动设计可以真正帮上忙的地方了——虽然咱们还得学一些新概念才能掌握它。让咱们来看看这些概念是怎么应用在咱们的服务器代码里的。

咱们建立了服务器,而且向建立它的方法传递了一个函数。不管什么时候咱们的服务器收到一个请求,这个函数就会被调用。

咱们不知道这件事情何时会发生,可是咱们如今有了一个处理请求的地方:它就是咱们传递过去的那个函数。至于它是被预先定义的函数仍是匿名函数,就可有可无了。

这个就是传说中的 回调 。咱们给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行 回调 。

至少对我来讲,须要一些功夫才能弄懂它。你若是仍是不太肯定的话就再去读读Felix的博客文章。

让咱们再来琢磨琢磨这个新概念。咱们怎么证实,在建立完服务器以后,即便没有HTTP请求进来、咱们的回调函数也没有被调用的状况下,咱们的代码还继续有效呢?咱们试试这个:

var http = require("http"); function onRequest(request, response) {   console.log("Request received.");   response.writeHead(200, {"Content-Type": "text/plain"});   response.write("Hello World");   response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started.");

注意:在 onRequest (咱们的回调函数)触发的地方,我用 console.log 输出了一段文本。在HTTP服务器开始工做以后,也输出一段文本。

当咱们与往常同样,运行它node server.js时,它会立刻在命令行上输出“Server has started.”。当咱们向服务器发出请求(在浏览器访问http://localhost:8888/),“Request received.”这条消息就会在命令行中出现。

这就是事件驱动的异步服务器端JavaScript和它的回调啦!

(请注意,当咱们在服务器访问网页时,咱们的服务器可能会输出两次“Request received.”。那是由于大部分服务器都会在你访问 http://localhost:8888 /时尝试读取 http://localhost:8888/favicon.ico )


9.什么是时间循环?

在了解node.js以前你首先须要了解的一个基本的论点是:I/O是“昂贵”的。

 

所以对于当前的编程技术而言,最大的浪费来自于等待I/O的完成。下面列出了改善该问题的几种方式,其中的某个能够帮助你提升性能:

 

  • 同步:在某一时刻,一次只处理一个请求。但这种状况下,任何一个请求都会“耽误”(阻塞)全部其余的请求。
  • fork一个新进程:对于每一个请求,你启动一个新的进程来处理。这种状况下,没法达到很好的扩展,上百个链接就意味着上百个进程的存在。fork()函数是Unix程序员的锤子,由于使用它很方便,因此每一个程序都看起来像个钉子同样(都喜欢用锤子拿来敲敲它)。因此,常常形成过分使用,而有些过往矫正。
  • 线程:开启一个新的线程来处理每一个请求。这种方式很简单,而且对于内核来说使用线程也比fork进程来得“亲切”,由于一般线程花费比进程更少的开销。缺点:你的机子可能不支持基于线程编程,而且基于线程的程序,其复杂度增加得很是快,同时你还会有对访问共享资源的担心。

 

你须要了解的第二个论点是:被线程处理的每一个链接都是“内存昂贵的”。

Apache是采用多线程处理请求的。它对于每一个请求“孵化”出一个线程(或者进程,这取决于配置)来处理。你将会看到随着并发链接数的增加以及更多的线程须要服务多个客户端时,那些开销有多消耗内存。Nginx跟Node.js都不是基于多线程模型的,由于线程跟进程都须要很是大的内存开销。他们都是单线程的,可是基于事件的。这种基于单线程的模型消除了为了处理不少请求而建立成百上千个线程或进程带来的开销。

 

Node.js为你的代码保持单线程的运行环境

 

它确实是基于单线程运行的,你没法编写任何代码来执行并发;例如执行一个"sleep"操做将阻塞整个服务器1秒钟。

 

[javascript]  view plain copy print ?
 
  1. while(new Date().getTime() < now + 1000) {  
  2.    // do nothing  
  3. }  
 

 

所以,当代码运行的时候,node.js将不会响应来自客户端的其余请求,由于它只有一个线程来执行你的代码。或者,若是你有某些CPU密集型的操做,好比说,重置图片的尺寸,那也将阻塞全部其余的请求。

 

...然而,除了你的代码以外,其余的一切都是并发执行

 

 

在一个单独的请求里,没有办法可使得代码并行执行。然而,全部的I/O都是基于时间的而且是异步的,因此接下来的代码将不会阻塞服务器:

[javascript]  view plain copy print ?
 
  1. c.query(  
  2.    'SELECT SLEEP(20);',  
  3.    function (err, results, fields) {  
  4.      if (err) {  
  5.        throw err;  
  6.      }  
  7.      res.writeHead(200, {'Content-Type': 'text/html'});  
  8.      res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');  
  9.      c.end();  
  10.     }  
  11. );  


若是你在一个请求中这么作,其余请求可以很好得被执行。

 

为何这是更好的方式?何时咱们须要从同步转向异步/并发执行?

 

采用同步执行是个不错的方式,由于它使得编码变得容易(对比线程而言,并发问题经常让你陷入万劫不复)。

在node.js中,你不须要去担忧你的代码在后端会发生。你只须要在你作I/O操做的时候使用回调就能够了。你会获得保证:你的代码不会被中断,而且I/O操做也不会阻塞其余请求(由于没有了那些线程/进程须要花费的开销,好比在Apache中会发生的内存太高等)。


采用异步I/O也很好,由于I/O比那些执行其余操做更昂贵,咱们应该作一些更有意义的事情而不是去等待I/O。

 

一个事件循环指的是——一个实体,它能够处理外部事件而且将它们转化为回调的执行。所以,I/O调用变成了node.js能够从一个请求切换到另一个请求的“点”,你的代码保存了回调并返回控制权给node.js运行时环境。而回调在最终得到了数据以后被执行。

固然,在node.js内部,仍然是依靠线程和进程来进行数据访问、处理其余任务执行。然而,这些都没有明确地对你的代码暴露出来,因此你不须要额外担忧内部如何处理I/O之间的交互。对比Apache的模型,它少去了不少线程以及线程开销,由于对每一个链接来说单独的线程不是必须的。仅仅是当你绝对须要让某个操做并发执行才会须要线程,但即使如此线程也是node.js本身管理的。


除了I/O调用以外,node.js期待全部的请求最好快速返回。好比,那些CPU密集型的工做应该被隔离到另外一个进程上去执行(经过与事件交互或者使用像WebWorker同样的抽象)。这很明显意味着当你与事件交互的时候,若是没有另外一个线程在后端(node.js运行时),那么你是没法并行化执行代码的。基本上,全部能够emit事件的对象(例如EventEmitter的实例)都支持基于事件的异步交互而且你也能够与“blocking code”交互(例如使用文件、sockets或者在node.js中是EventEmitter的子进程)。使用这种方案的话,就可以很好得利用多核的优点了,能够看看:node-http-proxy。

 

内部实现

 

内部,node.js依赖于libev提供的事件循环,libeio是对于libev的补充,node.js使用池化的线程来提供对于异步I/O的支持。若是你想了解更多细节,你能够看一下libev的文档

 

如何在Node.js中实现异步

 

 

Tim Caswell在其PPT中描述了整个模式:

 

  • First-classfunction:例如咱们将function做为数据传递,包裹他们以在须要的时候执行。
  • Function组装:就像你了解的关于异步函数或者闭包同样,在触发了I/O事件以后执行。
  • 回调计数器:对于基于事件的回调,你没法保证对于任何特殊的命令,I/O事件都会被执行。因此,一旦你须要屡次查询来完成某个处理,一般你仅须要对任何的并发I/O操做进行计数,而后在你确实须要最后的结果的时候检查是否必要的操做都已所有完成(其中的一个例子是在事件回调中,经过对返回的数据库查询进行计数)。查询会被并发执行,而且I/O也对此提供支持(例如能够经过链接池的方式实现并发查询)。
  • 事件循环:上面已经提到过,你能够将blockingcode包裹进一个基于事件的抽象中去(好比经过运行一个子进程,而后当它执行完成以后再返回)。

再次申明原文出处:http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

相关文章
相关标签/搜索