动态ip代理服务nodejs的融会贯通,你学会了吗_

网络

获取本地 IP

function get_local_ip() { const interfaces = require(os).networkInterfaces(); let IPAdress = ; for (const devName in interfaces) { const iface = interfaces[devName]; for (let i = 0; i < iface.length; i++) { const alias = iface[i]; if (alias.family === IPv4 && alias.address !== 127.0.0.1 && !alias.internal) { IPAdress = alias.address; } } } return IPAdress; } 复制代码

TCP 客户端

NodeJS 使用 net 模块创建 TCP 动态ip代理服务连接和服务。

启动与测试 TCP

const assert = require(assert); const net = require(net); let clients = 0; let expectedAssertions = 2; const server = net.createServer(function (client) { clients++; const clientId = clients; console.log(Client connected:, clientId); client.on(end, function () { console.log(Client disconnected:, clientId); }); client.write(Welcome client: + clientId); client.pipe(client); }); server.listen(8000, function () { console.log(Server started on port 8000); runTest(1, function () { runTest(2, function () { console.log(Tests finished); assert.equal(0, expectedAssertions); server.close(); }); }); }); function runTest(expectedId, done) { const client = net.connect(8000); client.on(data, function (data) { const expected = Welcome client: + expectedId; assert.equal(data.toString(), expected); expectedAssertions--; client.end(); }); client.on(end, done); } 复制代码

UDP 客户端

利用 dgram 模块创建数据报 socket,然后利用socket.send发送数据。

文件发送服务

const dgram = require(dgram); const fs = require(fs); const port = 41230; const defaultSize = 16; function Client(remoteIP) { const inStream = fs.createReadStream(__filename); // 从当前文件创建可读流 const socket = dgram.createSocket(udp4); // 创建新的数据流 socket 作为客户端 inStream.on(readable, function () { sendData(); // 当可读流准备好,开始发送数据到服务器 }); function sendData() { const message = inStream.read(defaultSize); // 读取数据块 if (!message) { return socket.unref(); // 客户端完成任务后,使用 unref 安全关闭它 } // 发送数据到服务器 socket.send(message, 0, message.length, port, remoteIP, function () { sendData(); }); } } function Server() { const socket = dgram.createSocket(udp4); // 创建一个 socket 提供服务 socket.on(message, function (msg) { process.stdout.write(msg.toString()); }); socket.on(listening, function () { console.log(Server ready:, socket.address()); }); socket.bind(port); } if (process.argv[2] === client) { // 根据命令行选项确定运行客户端还是服务端 new Client(process.argv[3]); } else { new Server(); } 复制代码

HTTP 客户端

使用 http.createServer 和 http.createClient 运行 HTTP 服务。

启动与测试 HTTP

const assert = require(assert); const http = require(http); const server = http.createServer(function (req, res) { res.writeHead(200, { Content-Type: text/plain }); // 写入基于文本的响应头 res.write(Hello, world.); // 发送消息回客户端 res.end(); }); server.listen(8000, function () { console.log(Listening on port 8000); }); const req = http.request({ port: 8000 }, function (res) { // 创建请求 console.log(HTTP headers:, res.headers); res.on(data, function (data) { // 给 data 事件创建监听,动态ip代理服务确保和期望值一致 console.log(Body:, data.toString()); assert.equal(Hello, world., data.toString()); assert.equal(200, res.statusCode); server.unref(); console.log(测试完成); }); }); req.end(); 复制代码

重定向

HTTP 标准定义了标识重定向发生时的状态码,它也指出了客户端应该检查无限循环。

300:多重选择301:永久移动到新位置302:找到重定向跳转303:参见其他信息304:没有改动305:使用代理307:临时重定向 const http = require(http); const https = require(https); const url = require(url); // 有很多接续 URLs 的方法 // 构造函数被用来创建一个对象来构成请求对象的声明周期 function Request() { this.maxRedirects = 10; this.redirects = 0; } Request.prototype.get = function (href, callback) { const uri = url.parse(href); // 解析 URLs 成为 Node http 模块使用的格式,确定是否使用 HTTPS const options = { host: uri.host, path: uri.path }; const httpGet = uri.protocol === http: ? http.get : https.get; console.log(GET:, href); function processResponse(response) { if (response.statusCode >= 300 && response.statusCode < 400) { // 检查状态码是否在 HTTP 重定向范围 if (this.redirects >= this.maxRedirects) { this.error = new Error(Too many redirects for:+ href); } else { this.redirects++; // 重定向计数自增 href = url.resolve(options.host, response.headers.location); // 使用 url.resolve 确保相对路径的 URLs 转换为绝对路径 URLs return this.get(href, callback); } } response.url = href; response.redirects = this.redirects; console.log(Redirected:, href); function end() { console.log(Connection ended); callback(this.error, response); } response.on(data, function (data) { console.log(Got data, length:, data.length); }); response.on(end, end.bind(this)); // 绑定回调到 Request 实例,确保能拿到实例属性 } httpGet(options, processResponse.bind(this)) .on(error, function (err) { callback(err); }); 复制代码 }; const request = new Request(); request.get(google.com/, function (err, res) { if (err) { console.error(err); } else { console.log( Fetched URL: ${res.url} with ${res.redirects} redirects ); process.exit(); } });

HTTP 代理

ISP 使动态ip代理服务用透明代理使网络更加高效使用缓存代理服务器减少宽带Web 应用程序的 DevOps 利用他们提升应用程序性能 const http = require(http); const url = require(url); http.createServer(function (req, res) { console.log(start request:, req.url); const options = url.parse(req.url); console.log(options); options.headers = req.headers; const proxyRequest = http.request(options, function (proxyResponse) { // 创建请求来复制原始的请求 proxyResponse.on(data, function (chunk) { // 监听数据,返回给浏览器 console.log(proxyResponse length:, chunk.length); res.write(chunk, binary); }); proxyResponse.on(end, function () { // 追踪代理请求完成 console.log(proxied request ended); res.end(); }); res.writeHead(proxyResponse.statusCode, proxyResponse.headers); // 发送头部信息给服务器 }); req.on(data, function (chunk) { // 捕获从浏览器发送到服务器的数据 console.log(in request length:, chunk.length); proxyRequest.write(chunk, binary); }); req.on(end, function () { // 追踪原始的请求什么时候结束 console.log(original request ended); proxyRequest.end(); }); 复制代码 }).listen(8888); // 监听来自本地浏览器的连接

封装 request-promise

const https = require(https); const promisify = require(util).promisify; https.get[promisify.custom] = function getAsync(options) { return new Promise((resolve, reject) => { https.get(options, (response) => { response.end = new Promise((resolve) => response.on(end, resolve)); resolve(response); }).on(error, reject); }); }; const rp = promisify(https.get); (async () => { const res = await rp(https://jsonmock.hackerrank.com/api/movies/search/?Title=Spiderman&page=1); let body = ; res.on(data, (chunk) => body += chunk); await res.end; console.log(body); })(); 复制代码

DNS 请求

使用 dns 模块创建 DNS 请求。

A:`dns.resolve`,A 记录存储 IP 地址TXT:`dns.resulveTxt`,文本值可以用于在 - DNS 上构建其他服务SRV:`dns.resolveSrv`,服务记录定义服务的定位数据,通常包含主机名和端口号NS:`dns.resolveNs`,指定域名服务器CNAME:`dns.resolveCname`,相关的域名记录,动态ip代理服务设置为域名而不是 IP 地址 const dns = require(dns); dns.resolve(www.chenng.cn, function (err, addresses) { if (err) { console.error(err); } console.log(Addresses:, addresses); 复制代码 });

crypto 库加密解密

const crypto = require(crypto) function aesEncrypt(data, key = key) { const cipher = crypto.createCipher(aes192, key) let crypted = cipher.update(data, utf8, hex) crypted += cipher.final(hex) return crypted } function aesDecrypt(encrypted, key = key) { const decipher = crypto.createDecipher(aes192, key) let decrypted = decipher.update(encrypted, hex, utf8) decrypted += decipher.final(utf8) return decrypted } 复制代码

发起 HTTP 请求的方法

HTTP 标准库无需安装外部依赖需要以块为单位接受数据,自己监听 end 事件HTTP 和 HTTPS 是两个模块,需要区分使用Request 库使用方便有 promise 版本 request-promiseAxios既可以用在浏览器又可以用在 NodeJS可以使用 axios.all 并发多个请求SuperAgent可以链式使用node-fetch浏览器的 fetch 移植过来的

子进程

执行外部应用

基本概念

4个异步方法:exec、execFile、fork、spawnspawn:处理一些会有很多子进程 I/O 时、进程会有大量输出时使用execFile:只需执行一个外部程序的时候使用,执行速度快,处理用户输入相对安全exec:想直接访问线程的 shell 命令时使用,动态ip代理服务一定要注意用户输入fork:想将一个 Node 进程作为一个独立的进程来运行的时候使用,是的计算处理和文件描述器脱离 Node 主进程Node非 Node3个同步方法:execSync、execFileSync、spawnSync通过 API 创建出来的子进程和父进程没有任何必然联系

execFile

会把输出结果缓存好,通过回调返回最后结果或者异常信息

const cp = require(child_process); cp.execFile(echo, [hello, world], (err, stdout, stderr) => { if (err) { console.error(err); } console.log(stdout: , stdout); console.log(stderr: , stderr); }); 复制代码

pawn

通过流可以使用有大量数据输出的外部应用,节约内存使用流提高数据响应效率spawn 方法返回一个 I/O 的流接口

单一任务

const cp = require(child_process); const child = cp.spawn(echo, [hello, world]); child.on(error, console.error); child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr); 复制代码

多任务串联

const cp = require(child_process); const path = require(path); const cat = cp.spawn(cat, [path.resolve(__dirname, messy.txt)]); const sort = cp.spawn(sort); const uniq = cp.spawn(uniq); cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout); 复制代码

exec

只有一个字符串命令和 shell 一模一样 const cp = require(child_process); cp.exec(cat ${__dirname}/messy.txt | sort | uniq, (err, stdout, stderr) => { console.log(stdout); });

fork

fork 方法会开发一个 IPC 通道,不同的 Node 进程进行消息动态ip代理服务传送一个子进程消耗 30ms 启动时间和 10MB 内存子进程:`process.on(message)`、`process.send()`父进程:`child.on(message)`、`child.send()`

父子通信

// parent.js const cp = require(child_process); const child = cp.fork(./child, { silent: true }); child.send(monkeys); child.on(message, function (message) { console.log(got message from child, message, typeof message); }) child.stdout.pipe(process.stdout); setTimeout(function () { child.disconnect(); }, 3000); // child.js process.on(message, function (message) { console.log(got one, message); process.send(no pizza); process.send(1); process.send({ my: object }); process.send(false); process.send(null); }); console.log(process); 复制代码

常用技巧

退出时杀死所有子进程

保留对由 spawn 返回的 ChildProcess 对象的引用,并在退出主进程时将其杀死

const spawn = require(child_process).spawn; const children = []; process.on(exit, function () { console.log(killing, children.length, child processes); children.forEach(function (child) { child.kill(); }); }); children.push(spawn(/bin/sleep, [10])); children.push(spawn(/bin/sleep, [10])); children.push(spawn(/bin/sleep, [10])); setTimeout(function () { process.exit(0); }, 3000); 复制代码

Cluster 的理解

解决 NodeJS 单进程无法充分利用多核 CPU 问题通过 master-cluster 模式可以使得应用更加健壮Cluster 底层是 child_process 模块,除了可以发送普通消息,还可以发送底层对象 TCP、UDP 等TCP 主进程发送到子进程,子进程能根据消息重建出 TCP 连接,Cluster 可以决定 fork 出合适的硬件资源的子进程数

Node 多线程

单线程问题

对 cpu 利用不足某个未捕获的异常可能会导致整个程序的退出

Node 线程

Node 动态ip代理服务进程占用了 7 个线程Node 中最核心的是 v8 引擎,在 Node 启动后,会创建 v8 的实例,这个实例是多线程的主线程:编译、执行代码编译/优化线程:在主线程执行的时候,可以优化代码分析器线程:记录分析代码运行时间,为 Crankshaft 优化代码执行提供依据垃圾回收的几个线程JavaScript 的执行是单线程的,但 Javascript 的宿主环境,无论是 Node 还是浏览器都是多线程的

异步 IO

Node 中有一些 IO 操作(DNS,FS)和一些 CPU 密集计算(Zlib,Crypto)会启用 Node 的线程池线程池默认大小为 4,可以手动更改线程池默认大小 process.env.UV_THREADPOOL_SIZE = 64

cluster 多进程

const cluster = require(cluster);const http = require(http);const numCPUs = require(os).cpus().length;if (cluster.isMaster) {console.log(`主进程 ${process.pid} 正在运行`);for (let i = 0; i < numCPUs; i++) {cluster.fork();}cluster.on(exit, (worker, code, signal) => {console.log(`工作进程 ${worker.process.pid} 已退出`);});} else {// 工作进程可以共享任何 TCP 连接。// 动态ip代理服务在本例子中,共享的是 HTTP 服务器。http.createServer((req, res) => {res.writeHead(200);res.end(Hello World);}).listen(8000);console.log(`工作进程 ${process.pid} 已启动`);} 复制代码一共有 9 个进程,其中一个主进程,cpu 个数 x cpu 核数 = 2 x 4 = 8 个 子进程无论 child_process 还是 cluster,都不是多线程模型,而是多进程模型应对单线程问题,通常使用多进程的方式来模拟多线程

真 Node 多线程

Node 10.5.0 的发布,给出了一个实验性质的模块 worker_threads 给 Node 提供真正的多线程能力

worker_thread 模块中有 4 个对象和 2 个类

isMainThread: 是否是主线程,源码中是通过 threadId === 0 进行判断的。MessagePort: 用于线程之间的通信,继承自 EventEmitter。MessageChannel: 用于创建异步、双向通信的通道实例。threadId: 线程 ID。Worker: 用于在主线程中创建子线程。第一个参数为 filename,表示子线程执行的入口。parentPort: 在 worker 线程里是表示父进程的 MessagePort 类型的对象,在主线程里为 nullworkerData: 用于在主进程中向子进程传递数据(data 副本) isMainThread, parentPort, workerData, threadId, MessageChannel, MessagePort, Worker } = require(worker_threads); function mainThread() { for (let i = 0; i < 5; i++) { const worker = new Worker(__filename, { workerData: i }); worker.on(exit, code => { console.log(main: worker stopped with exit code ${code}); }); worker.on(message, msg => { console.log(main: receive ${msg}); worker.postMessage(msg + 1); }); } } function workerThread() { console.log(worker: workerDate ${workerData}); parentPort.on(message, msg => { console.log(worker: receive ${msg}); }), parentPort.postMessage(workerData); } if (isMainThread) { mainThread(); } else { workerThread(); }

线程通信

const assert = require(assert); const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require(worker_threads); if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on(message, (value) => { console.log(received:, value); }); } else { parentPort.once(message, (value) => { assert(value.hereIsYourPort instanceof MessagePort); value.hereIsYourPort.postMessage(the worker is sending this); value.hereIsYourPort.close(); }); } 复制代码

多进程 vs 多线程

进程是资源分配的最小单位,线程是CPU调度的最小单位

最后,咱给小编:

1. 点赞+关注

2. 点头像关注后多多评论,转发给有需要的朋友。

谢谢!!