We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。 ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。
英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。所以衍生出了针对各种语言的各种形式编码。如GB2312。
即底层的二进制一样,但通过不同的编码方式,可以被编码成不同语言的符号。
由于世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。所以需要一种编码去统一所有语言的编码。这是Unicode的由来。 但是它也只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。这导致了不同语言存储时可能存在资源浪费和效率低下的问题。
UTF-8 是 Unicode 的实现方式之一。 UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。 2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。 下表总结了编码规则,字母x表示可用编码的位。
跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
0x1234567的大端字节序和小端字节序的写法如下图:
为什么会有小端字节序? 计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。
但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
"只有读取的时候,才必须区分字节序,其他情况都不用考虑。"
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。 但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。 可以理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。
Buffer 和 Stream 是 node 为了让 js 在后端拥有处理二进制文件而出现的数据结构。 强调:buffer中记录的是二进制数据。
stream的传输,都是先缓冲到Buffer里,然后一批批的传输的。
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
const buf = Buffer.from('runoob', 'ascii'); // 输出 72756e6f6f62 console.log(buf.toString('hex')); // 输出 cnVub29i console.log(buf.toString('base64'));
Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0 Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据 Buffer.allocUnsafeSlow(size) Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖) Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。 Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例 Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
这里其实可以分成两大类:
注意:Buffer.from不支持传入数字
buf.write(string[, offset[, length]][, encoding])
返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
buf.toString([encoding[, start[, end]]])
解码缓冲区数据并使用指定的编码返回字符串。
Buffer.concat(list[, totalLength])
返回一个多个成员合并的新 Buffer 对象。
清空buffer数据最快的办法是buffer.fill(0)
Double、Float、Int16、Int32、UInt16、UInt32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分**(大端字节序与小端字节序)**。
关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位在buffer的起始位置则是小端
const buf = Buffer.allocUnsafe(2); buf.writeInt16BE(256, 0) console.log(buf); // <Buffer 01 00> buf.writeInt16LE(256, 0) console.log(buf); // <Buffer 00 01>
Buffer.byteLength('Hello', 'utf8') // 5
Buffer.concat([buf1, buf2])
buf.length: buffer 对象所分配的内存数(字节数),它不会随着这个 buffer 对象内容的改变而改变 buf.write(string[, offset[, length]][, encoding]): 将参数 string 数据写入buffer buf.writeUIntLE(value, offset, byteLength[, noAssert]): 最高支持 48 位无符号整数,小端对齐 buf.writeUIntBE(value, offset, byteLength[, noAssert]): 最高支持 48 位无符号整数,大端对齐 buf.writeIntLE(value, offset, byteLength[, noAssert]): 最高支持48位有符号整数,小端对齐 buf.writeIntBE(value, offset, byteLength[, noAssert]): 最高支持48位有符号整数,大端对齐 buf.readUIntLE(offset, byteLength[, noAssert]): 读取 48 位以下的无符号数字,小端对齐 buf.readUIntBE(offset, byteLength[, noAssert]): 读取 48 位以下的无符号数字,大端对齐 buf.readIntLE(offset, byteLength[, noAssert]): 读取 48 位以下的有符号数字,小端对齐 buf.readIntBE(offset, byteLength[, noAssert]): 读取 48 位以下的有符号数字,大端对齐 buf.toString([encoding[, start[, end]]]): 根据 encoding 参数(默认是 'utf8')返回一个解码过的 string 类型 buf.toJSON(): 将 Buffer 实例转换为 JSON 对象 buf[index]: 返回值代表一个字节 buf.equals(otherBuffer): 比较两个缓冲区是否相等 buf.compare(otherBuffer): 比较两个 Buffer 对象,返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同 buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]): 拷贝,源和目标可以相同 buf.slice([start[, end]]): 剪切 Buffer 对象,根据 start(默认是 0 ) 和 end (默认是 buffer.length ) 偏移和裁剪了索引。 负的索引是从 buffer 尾部开始计算的 buf.readUInt8(offset[, noAssert]): 读取一个无符号 8 位整数 buf.readUInt16LE(offset[, noAssert]): 使用特殊的 endian 字节序格式读取一个无符号 16 位整数 buf.readUInt16BE(offset[, noAssert]): 使用特殊的 endian 字节序格式读取一个无符号 16 位整数,大端对齐 buf.readUInt32LE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个无符号 32 位整数,小端对齐 buf.readUInt32BE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个无符号 32 位整数,大端对齐 buf.readInt8(offset[, noAssert]): 读取一个有符号 8 位整数 buf.readInt16LE(offset[, noAssert]): 使用特殊的 endian 格式读取一个 有符号 16 位整数,小端对齐 buf.readInt16BE(offset[, noAssert]): 使用特殊的 endian 格式读取一个 有符号 16 位整数,大端对齐 buf.readInt32LE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个有符号 32 位整数,小端对齐 buf.readInt32BE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个有符号 32 位整数,大端对齐 buf.readFloatLE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个 32 位双浮点数,小端对齐 buf.readFloatBE(offset[, noAssert]): 使用指定的 endian 字节序格式读取一个 32 位双浮点数,大端对齐 buf.readDoubleLE(offset[, noAssert]): 使用指定的 endian字节序格式读取一个 64 位双精度数,小端对齐 buf.readDoubleBE(offset[, noAssert]): 使用指定的 endian字节序格式读取一个 64 位双精度数,大端对齐 buf.writeUInt8(value, offset[, noAssert]): 将 value 写入 buffer,value 必须是一个合法的无符号 8 位整数 buf.writeUInt16LE(value, offset[, noAssert]): 将 value 写入 buffer,value 必须是一个合法的无符号 16 位整数 buf.writeUInt16BE(value, offset[, noAssert])将 value 写入 buffer,value 必须是一个合法的无符号 16 位整数,大端对齐 buf.writeUInt32LE(value, offset[, noAssert]): value 必须是一个合法的无符号 32 位整数,小端对齐 buf.writeUInt32BE(value, offset[, noAssert]): value 必须是一个合法的无符号 32 位整数,打大端对齐 buf.writeInt8(value, offset[, noAssert]) buf.writeInt16LE(value, offset[, noAssert]): value 必须是一个合法的 signed 16 位整数 buf.writeInt16BE(value, offset[, noAssert]): value 必须是一个合法的 signed 16 位整数 buf.writeInt32LE(value, offset[, noAssert]): value 必须是一个合法的 signed 32 位整数 buf.writeInt32BE(value, offset[, noAssert]): value 必须是一个合法的 signed 32 位整数 buf.writeFloatLE(value, offset[, noAssert]): 当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的 buf.writeFloatBE(value, offset[, noAssert]): 当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的 buf.writeDoubleLE(value, offset[, noAssert]): value 必须是一个有效的 64 位double 类型的值 buf.writeDoubleBE(value, offset[, noAssert]): value 必须是一个有效的 64 位double 类型的值 buf.fill(value[, offset][, end]): 使用指定的 value 来填充这个 buffer。如果没有指定 offset (默认是 0) 并且 end (默认是 buffer.length) ,将会填充整个buffer。
流 怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。
存储需要占用大量内存的数据 Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。
后台语言之间的通信,经常使用的是UDP或者socket的方式,一般通信体都是 自定义协议头 H + 消息体字符串 S,并且这里的 ‘自定义协议头‘H 是 S.length的二进制形式,然后将 H + S整体打包成二进制进行传输。
自定义协议头规则:无符号长整型(32位,大端字节序)
$string=... $buffer= pack('N', strlen($string)).$string; $socket = stream_socket_client('udp://' . $address, $errno, $errmsg, $timeout); stream_socket_sendto($socket, $buffer)
// 开辟1200字节的堆外内存 const buf1 = Buffer.alloc(1200); // 将消息体字符串所占内存字节长度以32位无符号长整型形式写入到buf1中(作为消息头) buf1.writeInt32BE(Buffer.byteLength(str)); // 将消息体字符串写入到buf2中(作为消息体) const buf2 = Buffer.from(str); // 合并消息头+消息体为同一个二进制 const buffer = Buffer.concat([ buf1, buf2 ]); // 使用udp方式上报数据 const client = dgram.createSocket('udp4'); client.send(buffer, PORT, HOST, err => { if (err) throw err; client.close(); });
假设使用TCP/UDP通过自定义的二进制数据序列化协议与android/iOS客户端进行通信。在发送数据的时候将要发送的Buffer从本地序转换为网络序,当收到客户端的回包时,需要将收到的Buffer从网络序转换为本地序。跟客户端的同学已经协商好了二进制的数据序列化协议如下:
Node.js发给客户端的包体协议: 按字段的前后顺序拼装数据包: 用户id(4个字节,不能为空)+用户类型(1个字节,可以为空)+消息序列号(4个字节,可以为空)+消息命令字(1个字节,不能为空)+消息体(给客户端的文案,1个字节buffer长度+utf-8编码的buffer) 客户端回包给Node.js的包体协议: 按字段的前后顺序拼装数据包: 返回码(2个字节,不能为空)
const BUFFER_OFFSET = 0; const USERID_LENGTH = 4; const USER_TYPE_LENGTH = 1; const MSG_SEQ_LENGTH = 4; const MSG_CMD_LENGTH = 1; // 因为组好的包是要通过网络发送给客户端,所以要把数据以大端法写到数据包中。 function encodePkg(params) { let userId = Buffer.alloc(USERID_LENGTH); userId.writeUInt32BE((params.userId || 0), BUFFER_OFFSET ); let userType = Buffer.alloc(USER_TYPE_LENGTH); userType.writeUIntBE((params.userType || 0), BUFFER_OFFSET , USER_TYPE_LENGTH); let msgSeq = Buffer.alloc(MSG_SEQ_LENGTH); msgSeq.writeUInt32BE((params.msgSeq || 0), BUFFER_OFFSET); let msgCmd = Buffer.alloc(MSG_CMD_LENGTH); msgCmd.writeUIntBE((params.msgCmd || 0), BUFFER_OFFSET , MSG_CMD_LENGTH); let msgBuf = Buffer.from(params.msg, 'utf8'); let msgBufLength = Buffer.alloc(1, msgBuf.length); msgBuf = Buffer.concat([msgBufLength , msgBuf]); // 拼接各个字段的buffer return Buffer.concat([userId, userType, msgSeq, msgCmd, msgBuf]); }
const RETCODE_OFFSET = 2; const USERID_LENGTH = 4; const USER_TYPE_LENGTH = 1; const MSG_SEQ_LENGTH = 4; const MSG_CMD_LENGTH = 1; // 因为组好的包是客户端通过网络发送回来的,所以要把数据以大端法读到本地。 function decodePkg(responseBuf) { let result = {}; let retcodeBuf = responseBuf.slice(0,RETCODE_OFFSET); let retcode = retcodeBuf.readUInt16BE(0); result.retcode = retcode; // 拼接各个字段的buffer return result; }
const dgram = require('dgram'); const timeout = 2000; // UDP回包超时时间 单位:毫秒 function udpSvr(params) { let socket = dgram.createSocket({ type: 'udp4' }); let udpTimeoutWatcher = setTimeout(function() { console.log('udp response timeout!!!') socket.close(); }, timeout); // 根据参数组包 let message = encodePkg(params.sendData); let result = null; // 发包 socket.send(message, 0, message.length, params.port, params.address, function(err) { console.log('client send done!'); }); // 监听客户端的回包 socket.on('message', function(msg, info) { clearTimeout(udpTimeoutWatcher); // 根据协议解客户端的回包 result = decodePkg(info); }); socket.on('close', function() { console.log('socket closed.'); }); socket.on('error', function(err) { console.log('socket err'); console.log(err); socket.close(); }); }
stream 可以用水流形容数据的流动,在文件 I/O、网络 I/O中数据的传输都可以称之为流。流的使用方式只有一次,释放使用之后将不能被调用。 通过两个 fs 的 api 看出,readFile 不指定字符编码默认返回 buffer 类型,而 createReadStream 将文件转化为一个 stream , nodejs 中的 stream 通过 data 事件能够一点一点地拿到文件内容,直到 end 事件响应为止。 fs.readFile() 函数会缓冲整个文件。 为了最小化内存成本,尽可能通过 fs.createReadStream() 进行流式传输。
缓冲(Buffer)
缓冲(Buffer)是用于处理二进制流数据,将数据缓冲起来,它是临时性的,对于流式数据,会采用缓冲区将数据临时存储起来,等缓冲到一定的大小之后在存入硬盘中。视频播放器就是一个经典的例子,有时你会看到一个缓冲的图标,这意味着此时这一组缓冲区并未填满,当数据到达填满缓冲区并且被处理之后,此时缓冲图标消失,你可以看到一些图像数据。
缓存(Cache)
缓存(Cache)我们可以看作是一个中间层,它可以是永久性的将热点数据进行缓存,使得访问速度更快,例如我们通过 Memory、Redis 等将数据从硬盘或其它第三方接口中请求过来进行缓存,目的就是将数据存于内存的缓存区中,这样对同一个资源进行访问,速度会更快,也是性能优化一个重要的点。
在 HTTP 传输中传输的是二进制数据,如果接口返回的是字符串,这时候 HTTP 在传输之前会先将字符串转换为 Buffer 类型,以二进制数据传输,通过流(Stream)的方式一点点返回到客户端。但是直接返回 Buffer 类型,则少了每次的转换操作,对于性能也是有提升的。
字节序 字符编码笔记:ASCII,Unicode 和 UTF-8 网络序?本地序? 文件上传接口的转发(node)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
基础知识
ASCII 码
计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。
ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。
非 ASCII 编码
英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。所以衍生出了针对各种语言的各种形式编码。如GB2312。
Unicode
由于世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。所以需要一种编码去统一所有语言的编码。这是Unicode的由来。
但是它也只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。这导致了不同语言存储时可能存在资源浪费和效率低下的问题。
UTF-8
UTF-8 是 Unicode 的实现方式之一。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x表示可用编码的位。
跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
字节序
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
0x1234567的大端字节序和小端字节序的写法如下图:

为什么会有小端字节序?
计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。
但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
Buffer
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
可以理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。
Buffer 与字符编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
创建 Buffer 类
这里其实可以分成两大类:
Buffer.from(obj) // obj支持的类型string, buffer, arrayBuffer, array, or array-like object
Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow
写入缓冲区 (默认编码是utf-8)
返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
从缓冲区读取数据
解码缓冲区数据并使用指定的编码返回字符串。
缓冲区合并
返回一个多个成员合并的新 Buffer 对象。
清空buffer
清空buffer数据最快的办法是buffer.fill(0)
综合写入读取数值
Double、Float、Int16、Int32、UInt16、UInt32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分**(大端字节序与小端字节序)**。
关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位在buffer的起始位置则是小端
字符串实际占据的字节长度(默认编码方式为utf8)
将一组Buffer对象合并为一个Buffer对象
方法手册
应用场景
流
怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。
存储需要占用大量内存的数据
Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。
实例
后台语言之间的通信,经常使用的是UDP或者socket的方式,一般通信体都是 自定义协议头 H + 消息体字符串 S,并且这里的 ‘自定义协议头‘H 是 S.length的二进制形式,然后将 H + S整体打包成二进制进行传输。
UDP上报
自定义协议头规则:无符号长整型(32位,大端字节序)
更详尽例
假设使用TCP/UDP通过自定义的二进制数据序列化协议与android/iOS客户端进行通信。在发送数据的时候将要发送的Buffer从本地序转换为网络序,当收到客户端的回包时,需要将收到的Buffer从网络序转换为本地序。跟客户端的同学已经协商好了二进制的数据序列化协议如下:
stream
stream 可以用水流形容数据的流动,在文件 I/O、网络 I/O中数据的传输都可以称之为流。流的使用方式只有一次,释放使用之后将不能被调用。
通过两个 fs 的 api 看出,readFile 不指定字符编码默认返回 buffer 类型,而 createReadStream 将文件转化为一个 stream , nodejs 中的 stream 通过 data 事件能够一点一点地拿到文件内容,直到 end 事件响应为止。
fs.readFile() 函数会缓冲整个文件。 为了最小化内存成本,尽可能通过 fs.createReadStream() 进行流式传输。
缓冲(Buffer)与缓存(Cache)的区别?
缓冲(Buffer)
缓冲(Buffer)是用于处理二进制流数据,将数据缓冲起来,它是临时性的,对于流式数据,会采用缓冲区将数据临时存储起来,等缓冲到一定的大小之后在存入硬盘中。视频播放器就是一个经典的例子,有时你会看到一个缓冲的图标,这意味着此时这一组缓冲区并未填满,当数据到达填满缓冲区并且被处理之后,此时缓冲图标消失,你可以看到一些图像数据。
缓存(Cache)
缓存(Cache)我们可以看作是一个中间层,它可以是永久性的将热点数据进行缓存,使得访问速度更快,例如我们通过 Memory、Redis 等将数据从硬盘或其它第三方接口中请求过来进行缓存,目的就是将数据存于内存的缓存区中,这样对同一个资源进行访问,速度会更快,也是性能优化一个重要的点。
在 HTTP 传输中传输的是二进制数据,如果接口返回的是字符串,这时候 HTTP 在传输之前会先将字符串转换为 Buffer 类型,以二进制数据传输,通过流(Stream)的方式一点点返回到客户端。但是直接返回 Buffer 类型,则少了每次的转换操作,对于性能也是有提升的。
参考
字节序
字符编码笔记:ASCII,Unicode 和 UTF-8
网络序?本地序?
文件上传接口的转发(node)
The text was updated successfully, but these errors were encountered: