Վերջերս մի հետաքրքիր միտք ունեի կապված մեծ տվյալների վերլուծման հետ։ Այդ միտքը իրագործելու համար անհրաժեշտ էր տվյալների բազայում ունենալ որոշակի տվյալներ հայտնի սոցիալական կայքից, որը կօգտագործվեր հաշվարկներ իրականացնելու համար։
Առաջին հայացքից տվյալներ ձեռք բերելը այնքան էլ բարդ գործ չեր թվում, մինչ այն պահը երբ հայտնվեց IP հասցեների արգելափակման խնդիրը։
Որոշակի ակտիվություն նկատելուն պես, վերոնշյալ կայքը ժամանակավորապես արգելափակում էր IP հասցեն՝ որտեղից գալիս էին հարցումները։
Տարատեսակ եղանակներ փորձելով, ի վերջո այդ խնդիրը լուծվեց։ Այստեղ ներկայացված է առավել հետաքրքիր լուծումներից մեկը։
Մեր նպատակն է ունենալ փոքրիկ NodeJS ծրագիր, որը տեղադրված և կարգավորված կլինի Heroku համակարգում և որը կկատարի բազմաթիվ HTTP հարցումներ մեր ցանկացած հասցեներով, և անհրաժեշտության դեպքում կկարողանա փոփոխել սեփական IP հասցեն։
Մեխանիզմը հիմնված է բացառապես Heroku համակարգի այն հատկության վրա, որ նրա սերվերները ամեն անգամ վերսկսվելիս փոփոխում են իրենց IP հասցեները։
Սկսենք փոքրիկ NodeJS ծրագրից, որը կկատարի հարցումներ տրամադրված URL-ներից յուրաքանչյուրին և կցուցադրի յուրաքանչյուր ստացված պատասխան։ Ծրագիրը նաև պետք է ճիշտ կարգավորված լինի այն դեպքերի համար` երբ հարցումներից որևէ մեկի կամ մի քանիսի ընթացքում տեղի ունենա հարցումների քանակի սահմանափակման հետ կապված խափանումներ։
Ներկայացված օրինակում ծրագիրը կատարում է հարցումներ և ինչ-որ քանակի հարցումներից հետո արհեստականորեն կազմակերպում է խափանում։ Խափանում առաջացնելու նպատակը բացառապես IP հասցեների փոփոխման մեթոդը թեստավորելն է, այն անհրաժեշտ չէ ունենալ իրական ծրագրում։ Ներքևում կարելի է տեսնել ծրագրի աշխատանքի արդյունքը։
Ինչպես արդեն նկատեցիք սահմանված բոլոր URL-ները որպես հարցման պատասխան վերադարձնում են հարցումը կատարող համակարգչի IP հասցեն։
Այժմ անրադառնանք IP հասցեի փոփոխման մեխանիզմին։ Ինչպես արդեն ասվեց Heroku համակարգը(հոդվածը գրելու պահին) ունի հատկություն, ըստ որի յուրաքանչյուր վերսկսումից հետո փոփոխում է տվյալ սերվերի IP հասցեն։ Օգտվելով այդ հատկությունից, յուրաքանչյուր անգամ երբ պետք լինի փոփոխել սերվերի IP հասցեն, մենք ընդամենը կվերսկսենք սերվերի աշխատանքը։
Heroku համակարգը տրամադրում է API-ներ որոնք կարող են օգտակար լինել այդ համակարգում որոշակի գործերի ավտոմատացման համար։ Մեր դեպքում անհրաժեշտ է սերվերի վերսկսման հնարավորություն, որը թույլ է տալիս հետևյալ API մեթոդը՝
DELETE /apps/{APP_NAME}/dynos/{DYNO_ID_OR_NAME}
Որտեղ `
APP_NAME-ը Heroku-ում ստեղծված հավելվածի անունն է, որը կարելի է գտնել հետևյալ էջում՝
DYNO_NAME-ը տվյալ պրոցեսի անունն է, որը սահմանված է լինում բնութագրիչ ֆայլում(Procfile)։ Մեր դեպքում այն կլինի worker.1։
Բացի այս երկուսից մեզ անհրաժեշտ է նաև բանալի առանց որի հնարավոր չէ հարցումներ կատարել նշված API մեթոդին։ Վերջինս կարելի է գտնել այս էջում՝
Այժմ կատարենք հետևյալ փոփոխությունները վերոնշյալ NodeJS ծրագրում՝
Մինչ ծրագրի տեղադրումը Heroku համակարգ, ստեղծենք ևս մեկ ֆայլ, որը կպարունակի սերվերի սկսման հրամանի կանչը։ Այդ ֆայլի անունը պետք է լինի Procfile, քանի որ համակարգը հաշվի է առնեւմ հենց այդ անունով բնութագրիչ ֆայլը։
worker: node ip-script.js
Չմոռանանք նաև տեղադրել անհրաժեշտ փոփոխականները (անուններ, բանալի), որպեսզի restartMe() մեթոդը կարողանա վերսկսել սերվերի աշխատանքը։ Դրանք կարելի է տեղադրել Heroku-ի կայքում, տվյալ սերվերի կարգավորումներում։
Այժմ հետևյալ հրմանների միջոցով տեղադրենք ստացված ծրագիրը Heroku համակարգում։
$ heroku login
$ cd your-project-folder
$ git init
$ heroku git:remote -a your-app-name
$ git add .
$ git commit -am "Initial commit"
$ git push heroku master
Ահա ծրագրի աշխատանքի արդյունքը։ Կարելի է նկատել, որ յուրաքանչյուր անգամ երբ 429 կոդով խափանում է տեղի ունենում, ծրագիրը վերսկսվում է և որի արդյունքում էլ սերվերի IP հասցեն փոփոխվում է։
NodeJS ծրագրի ամբողջական տարբերակը՝
const axios = require('axios');
const Heroku = require('heroku-client');
const heroku = new Heroku({ token: process.env.API_KEY })
const URLS = [
'https://api.myip.com/?id=1',
'https://api.myip.com/?id=2',
'https://api.myip.com/?id=3',
'https://api.myip.com/?id=4',
'https://api.myip.com/?id=5',
'https://api.myip.com/?id=6',
'https://api.myip.com/?id=7',
];
function throwRateLimitErrorIfNeeded(url) {
if (url.includes('id=5')) {
const err = new Error('Rate limit exceeded');
err.status = 429;
throw err;
}
}
async function main() {
try {
for await (const url of URLS) {
const response = await axios.get(url);
console.log(`URL: ${url} (${response.data.ip})`);
throwRateLimitErrorIfNeeded(url);
}
} catch (err) {
console.log(err.message);
if (err.status === 429) {
await restartMe();
}
}
}
async function restartMe() {
console.log('Restarting myself..')
await heroku.delete(`/apps/${process.env.APP_NAME}/dynos/${process.env.DYNO_NAME}.1`);
}
main();