sql >> Databasteknik >  >> NoSQL >> Redis

Cacha tweets med Node.js, Redis och Socket.io

I den här artikeln kommer vi att skapa en strömningslista med tweets baserat på en sökfråga som användaren har angett. Tweetarna kommer att hämtas med Twitters Streaming API, lagras i en Redis-lista och uppdateras i front-end med Socket.io. Vi kommer i första hand att använda Redis som ett cachinglager för att hämta tweets.

Introduktion

Här är en kort beskrivning av de teknologier vi kommer att använda:

Redis

Redis är en öppen källkod (BSD-licensierad), datastrukturlager i minnet, som används som en databas, cache och meddelandeförmedlare. Den stöder datastrukturer som strängar, hash, listor, uppsättningar, sorterade uppsättningar med intervallfrågor, bitmappar, hyperlogloggar och geospatiala index med radiefrågor.

Node.js

Node.js är en plattform byggd på Chromes JavaScript-runtime för att enkelt bygga snabba och skalbara nätverksapplikationer. Node.js använder en händelsedriven, icke-blockerande I/O-modell som gör den lätt och effektiv, och därmed perfekt för dataintensiva realtidsapplikationer som körs över distribuerade enheter.

Express.js

Express.js är ett Node.js-ramverk. Du kan skapa server- och serverkod för en applikation som de flesta andra webbspråk, men med hjälp av JavaScript.

Socket.IO

Socket.IO är ett JavaScript-bibliotek för webbapplikationer i realtid. Det möjliggör dubbelriktad kommunikation i realtid mellan webbklienter och servrar. Den har två delar:ett bibliotek på klientsidan som körs på webbläsaren och ett bibliotek på serversidan för Node.js. Båda komponenterna har nästan identiska API:er.

Heroku

Heroku är en molnplattform som låter företag bygga, leverera, övervaka och skala appar – det är det snabbaste sättet att gå från idé till webbadress, och kringgår all denna infrastrukturhuvudvärk.

Den här artikeln förutsätter att du redan har Redis, Node.js och Heroku Toolbelt installerat på din maskin.

Inställningar

- Ladda ner koden från följande arkiv: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags

- Kör npm installation för att installera de nödvändiga komponenterna

- Slutligen kan du starta nodservern genom att göra "node index.js". Du kan också köra "nodemon" som tittar efter filändringar också.

Du kan också komma åt en värdversion av denna app här: https://node-socket-redis-stream-tweet.herokuapp.com/

Processen

Här är en kort beskrivning av processen som vi kommer att använda för att bygga demoapplikationen:

1. Vi börjar med att acceptera en sökfråga från användaren. Frågan kan vara Twitter-omnämnanden, hashtags eller valfri slumpmässig söktext.

2. När vi har sökfrågan skickar vi den till Twitters Streaming API för att hämta tweets. Eftersom det är en stream kommer vi att lyssna när tweets skickas av API:et.

3. Så snart en tweet har hämtats kommer vi att lagra den i en Redis-lista och sända den till front-end.

Vad är Redis-listor?

Redis-listor implementeras via länkade listor. Det betyder att även om du har miljontals element i en lista, så utförs operationen att lägga till ett nytt element i huvudet eller slutet på listan konstant. Hastigheten att lägga till ett nytt element med kommandot LPUSH till huvudet på en lista med tio element är densamma som att lägga till ett element till huvudet på en lista med 10 miljoner element.

I vår applikation kommer vi att lagra de tweets som tas emot via API:et i en lista som kallas "tweets". Vi kommer att använda LPUSH för att skicka den nyligen mottagna tweeten till listan, trimma den med LTRIM som begränsar mängden diskutrymme som används (eftersom att skriva en stream kan ta mycket utrymme), hämta den senaste tweeten med LRANGE och sända den till gränssnittet där det kommer att läggas till i strömningslistan.

Vad är LPUSH, LTRIM och LRANGE?

Dessa är en uppsättning Redis-kommandon som används för att lägga till data till en lista. Här är en kort beskrivning:

LPUSH

Infoga alla angivna värden längst upp i listan som lagras vid nyckel. Om nyckeln inte finns skapas den som en tom lista innan push-operationerna utförs. När nyckeln innehåller ett värde som inte är en lista, returneras ett fel.

redis> LPUSH mylist "world"(heltal) 1redis> LPUSH mylist "hello"(heltal) 2redis> LRANGE mylist 0 -11) "hej"2) "värld"

LTRIM

Beskär en befintlig lista så att den bara innehåller det angivna intervallet av element. Både start och stopp är nollbaserade index, där 0 är det första elementet i listan (huvudet), 1 nästa element och så vidare.

redis> RPUSH mylist "one"(heltal) 1redis> RPUSH mylist "två"(heltal) 2redis> RPUSH mylist "tre"(heltal) 3redis> LTRIM mylist 1 -1"OK"redis> LRANGE mylist 0 -11 ) "två"2) "tre"

LRANGE

Returnerar de angivna elementen i listan som lagras vid nyckel. Offsetens start och stopp är nollbaserade index, där 0 är det första elementet i listan (huvudet på listan), 1 är nästa osv.

Dessa förskjutningar kan också vara negativa tal som indikerar positioner från slutet av listan. Till exempel är -1 det sista elementet i listan, -2 det näst sista, och så vidare.

redis> RPUSH mylist "one"(heltal) 1redis> RPUSH mylist "två"(heltal) 2redis> RPUSH mylist "tre"(heltal) 3redis> LRANGE mylist 0 01) "one"redis> LRANGE mylist -3 21 ) "en"2) "två"3) "tre"

Skapa applikationen

Vår demo kräver både en front-end och en back-end. Vårt gränssnitt är en ganska enkel textruta med en knapp som kommer att användas för att starta streamen.

$('body').on('click', '.btn-search', function() { $('#tweets_area').empty(); $(this).text('Streaming.. .').attr('disabled', true); $.ajax({ url:'/search', typ:'POST', data:{ val:$.trim($('.search-txt'). val()) } });});

Vi behöver en hjälpfunktion för att bygga en tweetbox när vi tar emot tweeten från vår back-end:

 var _buildTweetBox =function(status) { var html =''; html +='
'; html +='
'; html +=' '; html +=' ' + status.user.name + ''; html +=' '; html +='
'; html +='
'; html +='
' + status.user.screen_name + '
'; html +='

' + status.text + '

'; html +='
'; html +='
'; $('#tweets_area').prepend(html); $('#tweets_area').find('.tweet-single').first().fadeIn('slow');};

Vi behöver också en lyssnare för att stoppa strömmen och förhindra att fler tweets läggs till i strömningslistan:

socket.on('stream:destroy', function(status) { $('.btn-search').text('Starta streaming').removeAttr('disabled'); $('.alert-warning ').fadeIn('slow'); setTimeout(function() { $('.alert-warning').fadeOut('slow'); }, STREAM_END_TIMEOUT * 1000);});

Låt oss gå över till back-end-sidan av saker och ting och börja skriva vårt /search API.

/** * API - Sök */app.post('/search', function(req, res, next) { _searchTwitter(req.body.val); res.send({ status:'OK' } );});/** * Strömma data från Twitter för inmatningstext * * 1. Använd Twitters streaming-API för att spåra ett specifikt värde som angetts av användaren * 2. När vi har data från Twitter lägger du till den i en Redis lista med LPUSH * 3. Efter att ha lagt till i listan, begränsa listan med LTRIM så att strömmen inte svämmar över disken * 4. Använd LRANGE för att hämta den senaste tweeten och skicka den till front-end med Socket.io * * @ param {String} val Query String * @return */var _searchTwitter =function(val) { twit.stream('statuses/filter', {track:val}, function(stream) { stream.on('data', function (data) { client.lpush('tweets', JSON.stringify(data), function() { client.ltrim('tweets', 0, TWEETS_TO_KEEP, function() { client.lrange('tweets', 0, 1 , function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0])); }); }); }); }); stream.on('destroy', function(response) {io.emit('stream:destroy'); }); stream.on('end', function(response) { io.emit('stream:destroy'); }); setTimeout(stream.destroy, STREAM_TIMEOUT * 1000); });}

Ovanstående kod innehåller kärnan i vår back-end. När en begäran har tagits emot på /search startar vi streamen med Twitters streaming-API som returnerar ett stream-objekt.

twit.stream('statuses/filter', {track:val}, function(stream) {});

Vi kan lyssna på strömningsobjektet efter en nyckel som heter "data" som skickar oss en ny tweet när den är tillgänglig.

stream.on('data', function(data) {});

Objektet "data" innehåller tweeten JSON som kan se ut ungefär så här (en del av svaret har utelämnats):

{ "created_at":"Ons 26 jul 08:01:56 +0000 2017", "id":890119982641803300, "id_str":"890119982641803264", "text":\"RT @FoxNews Det finns ingen bättre man än Jeff Sessions, och ingen större anhängare...av [President #Trumps] agenda.\"...", "källa":"Twitter för Android
", "truncated":false, "in_reply_to_status_id":null, "in_reply_to_status_id_str":null, "in_reply_to_user_id":null, "in_reply_to_str_user null, "in_reply_to_str_user null" "in_reply_to_screen_name":null, "user":{ "id":4833141138, "id_str":"4833141138", "name":"randy joe davis", "screen_name":"randyjoedavis1", "plats":null, " url":null, "description":"Konservativ patriot, pensionerad militär, pensionerad DOD civil. boskapsbonde, ryttare, äventyrare. Lovin Life ! GO HOGS !!", "protected":false, "verified":false, "followers_count" ":226, "friends_count":346, "listed_count":0, "favourites_count":3751, "statuses_count":1339, "created_at":"lör jan 30 03:39:16 +0000 2016", "utc_offset":null, "time_zone":null, "geo_enabled":false, "lang":"en", "contributors_enabled":false, "is_translator":false, " profile_background_color":"F5F8FA", "profile_background_image_url":"", "profile_background_image_url_https":"", "profile_background_tile":false, "profile_link_color":"1DA1F2", "profile_sidebar_border_color":"DEFfill_color"_side:bar",_DEFfill_color"side:bar", , "profile_text_color":"333333", "profile_use_background_image":true, "profile_image_url":"http://pbs.twimg.com/profile_images/883522005210943488/rqyyXlEX_normal.jpg_", "profilebs:https://pbs.twimg.jpg_" twimg.com/profile_images/883522005210943488/rqyyXlEX_normal.jpg", "default_profile":true, "default_profile_image":false, "following":null, "follow_request_sent":null, "notifications}}
 Vi lagrar detta svar i en Redis-lista som kallas "tweets" med LPUSH:

client.lpush('tweets', JSON.stringify(data), function() {});

När tweeten har sparats trimmar vi listan med LTRIM för att behålla ett maximalt antal tweets (så att vårt diskutrymme inte blir fullt):

client.ltrim('tweets', 0, TWEETS_TO_KEEP, function() {});

Efter att ha trimmat listan hämtar vi den senaste tweeten med LRANGE och skickar den till front-end:

client.lrange('tweets', 0, 1, function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0]));});

Eftersom detta är en demoapplikation måste vi också förstöra strömmen manuellt efter en viss tid så att den inte fortsätter att skriva till disken:

stream.on('end', function(response) { io.emit('stream:destroy');});setTimeout(stream.destroy, STREAM_TIMEOUT * 1000);

Och du är klar! Starta servern med npm start och njut av streamingupplevelsen.

En demo av applikationen finns tillgänglig här: https://node-socket-redis-stream-tweet.herokuapp.com/

För att distribuera den här applikationen på Heroku, kolla in deras dokument:https://devcenter.heroku.com/categories/deployment

Hela källkoden finns också tillgänglig på GitHub så att du kan punga och arbeta på: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -twitter-hashtags

Som alltid, om du bygger något fantastiskt, twittra oss om det @scalegridio.

Om du behöver hjälp med hantering och hosting för Redis™*, kontakta oss på [email protected] för mer information.


  1. Meteor-appen — återställer en utplacerad apps DB

  2. json.loads och Redis i python 3.5

  3. Vårdata mongodb sök efter ISO-datum

  4. Hitta ett dokument med ObjectID i mongoDB