开启nodejs之旅

228 阅读4分钟

学了前端那么久,各种框架的运行都离不开Node,于是便对Node产生了浓厚的兴趣,之前还是对后端有所了解的,对于Php还是多少了解过,自从接触到node异步式I/O便发现跟其他后台语言不太一样,便对Node 的兴趣更加强烈,好吧,废话不多说,先来一段Node与php的对比吧。


一.http服务器

从上图可知,我们node跟php运行是不太一样的,对于php,我需要自己配置如apache,ngix之类服务器,再配合本身php语言的解析再执行从客户端请求到后端解析再反馈的过程,而Node本身含有server,无需配置,轻松完成请求。

const http=require('http');
http.createServer((req,res)=>{

	//响应头状态和响应头内容格式
	res.writeHead(200,{'Context-Type':'text-html'}); 
    //Respose内容
	res.write('<h1>Node.js</h1>');
	res.end('<p>hello world</p>');

}).listen(3000);
console.log('http server is listening at port 3000');

http中如此强大,其中包含居多api,以至于我们能调用createServer,其他api都有哪些呢

本人console.log以下内容:(METHODS,STATUS_CODES,Agent,server,server response,

createServer,get,request,maxHeaderSize等api其中用createServer就创建了一个http服务器)

http { _connectionListener: [Function: connectionListener],
  METHODS:
   [ 'ACL',
     'BIND',
     'CHECKOUT',
     'CONNECT',
     'COPY',
     'DELETE',
     'GET',
     'HEAD',
     'LINK',
     'LOCK',
     'M-SEARCH',
     'MERGE',
     'MKACTIVITY',
     'MKCALENDAR',
     'MKCOL',
     'MOVE',
     'NOTIFY',
     'OPTIONS',
     'PATCH',
     'POST',
     'PROPFIND',
     'PROPPATCH',
     'PURGE',
     'PUT',
     'REBIND',
     'REPORT',
     'SEARCH',
     'SOURCE',
     'SUBSCRIBE',
     'TRACE',
     'UNBIND',
     'UNLINK',
     'UNLOCK',
     'UNSUBSCRIBE' ],
  STATUS_CODES:
   { '100': 'Continue',
     '101': 'Switching Protocols',
     '102': 'Processing',
     '103': 'Early Hints',
     '200': 'OK',
     '201': 'Created',
     '202': 'Accepted',
     '203': 'Non-Authoritative Information',
     '204': 'No Content',
     '205': 'Reset Content',
     '206': 'Partial Content',
     '207': 'Multi-Status',
     '208': 'Already Reported',
     '226': 'IM Used',
     '300': 'Multiple Choices',
     '301': 'Moved Permanently',
     '302': 'Found',
     '303': 'See Other',
     '304': 'Not Modified',
     '305': 'Use Proxy',
     '307': 'Temporary Redirect',
     '308': 'Permanent Redirect',
     '400': 'Bad Request',
     '401': 'Unauthorized',
     '402': 'Payment Required',
     '403': 'Forbidden',
     '404': 'Not Found',
     '405': 'Method Not Allowed',
     '406': 'Not Acceptable',
     '407': 'Proxy Authentication Required',
     '408': 'Request Timeout',
     '409': 'Conflict',
     '410': 'Gone',
     '411': 'Length Required',
     '412': 'Precondition Failed',
     '413': 'Payload Too Large',
     '414': 'URI Too Long',
     '415': 'Unsupported Media Type',
     '416': 'Range Not Satisfiable',
     '417': 'Expectation Failed',
     '418': 'I\'m a Teapot',
     '421': 'Misdirected Request',
     '422': 'Unprocessable Entity',
     '423': 'Locked',
     '424': 'Failed Dependency',
     '425': 'Unordered Collection',
     '426': 'Upgrade Required',
     '428': 'Precondition Required',
     '429': 'Too Many Requests',
     '431': 'Request Header Fields Too Large',
     '451': 'Unavailable For Legal Reasons',
     '500': 'Internal Server Error',
     '501': 'Not Implemented',
     '502': 'Bad Gateway',
     '503': 'Service Unavailable',
     '504': 'Gateway Timeout',
     '505': 'HTTP Version Not Supported',
     '506': 'Variant Also Negotiates',
     '507': 'Insufficient Storage',
     '508': 'Loop Detected',
     '509': 'Bandwidth Limit Exceeded',
     '510': 'Not Extended',
     '511': 'Network Authentication Required' },
  Agent:
   { [Function: Agent]
     super_:
      { [Function: EventEmitter]
        EventEmitter: [Circular],
        usingDomains: false,
        defaultMaxListeners: [Getter/Setter],
        init: [Function],
        listenerCount: [Function] },
     defaultMaxSockets: Infinity },
  ClientRequest:
   { [Function: ClientRequest] super_: { [Function: OutgoingMessage] super_: [Fu
nction] } },
  globalAgent:
   Agent {
     _events: [Object: null prototype] { free: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     defaultPort: 80,
     protocol: 'http:',
     options: { path: null },
     requests: {},
     sockets: {},
     freeSockets: {},
     keepAliveMsecs: 1000,
     keepAlive: false,
     maxSockets: Infinity,
     maxFreeSockets: 256 },
  IncomingMessage:
   { [Function: IncomingMessage]
     super_:
      { [Function: Readable]
        ReadableState: [Function: ReadableState],
        super_: [Function],
        _fromList: [Function: fromList] } },
  OutgoingMessage:
   { [Function: OutgoingMessage]
     super_:
      { [Function: Stream]
        super_: [Function],
        Readable: [Function],
        Writable: [Function],
        Duplex: [Function],
        Transform: [Function],
        PassThrough: [Function],
        pipeline: [Function: pipeline],
        finished: [Function: eos],
        Stream: [Circular],
        _isUint8Array: [Function: isUint8Array],
        _uint8ArrayToBuffer: [Function: _uint8ArrayToBuffer] } },
  Server:
   { [Function: Server] super_: { [Function: Server] super_: [Function] } },
  ServerResponse:
   { [Function: ServerResponse] super_: { [Function: OutgoingMessage] super_: [F
unction] } },
  createServer: [Function: createServer],
  get: [Function: get],
  request: [Function: request],
  maxHeaderSize: [Getter] } 

二.异步式 I/O 与事件驱动

Node.js 最大的特点就是采用异步式 I/O 与事件驱动的架构设计。对于高并发的解决方
案,传统的架构是多线程模型,也就是为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式 I/O 调用时的时间开销。Node.js 使用的是单线程模型,对于所有 I/O 都采用异步式的请求方式,避免了频繁的上下文切换


例如,对于简单而常见的数据库查询操作,按照传统方式实现的代码如下:

res = db.query('SELECT*from some_table');res.output();
以上代码在执行到第一行的时候,线程会阻塞,等待数据库返回查询结果,然后再继续处理 一
方面线程长期阻塞等待,另一方面为了应付新请求而不断增加线程,因此会浪费大量系统资源,同时线程的增多也会占用大量的 CPU 时间来处理内存上下文切换,而且还容易遭受低速连接攻击


看看Node.js是如何解决这个问题的:

db.query('SELECT*from some_table', function(res){ res.output();});
这段代码中 db.
query 的第二个参数是一个函数,我们称为回调函数。进程在执行到db.query 的时候,不会等待结果返回,而是直接继续执行后面的语句,直到进入事件循环
当数据库查询结果返回时,会将事件发送到事件队列等到线程进入事件循环以后,才会调
用之前的回调函数继续执行后面的逻辑。
Node.js 的异步机制是基于事件的,返回的结果由事件循环来处理。Node.js 进程在同一时刻只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件