In dit artikel bouwen we een streaminglijst met tweets op basis van een zoekopdracht die door de gebruiker is ingevoerd. De tweets worden opgehaald met behulp van Twitter's Streaming API, opgeslagen in een Redis-lijst en bijgewerkt in de front-end met behulp van Socket.io. We zullen Redis voornamelijk gebruiken als cachelaag voor het ophalen van tweets.
Inleiding
Hier is een korte beschrijving van de technologieën die we zullen gebruiken:
Opnieuw
Redis is een open-source (BSD-licentie), in-memory datastructuuropslag, die wordt gebruikt als database, cache en berichtenmakelaar. Het ondersteunt datastructuren zoals strings, hashes, lijsten, sets, gesorteerde sets met bereikquery's, bitmaps, hyperloglogs en georuimtelijke indexen met radiusquery's.
Node.js
Node.js is een platform gebouwd op Chrome's JavaScript-runtime voor het eenvoudig bouwen van snelle en schaalbare netwerkapplicaties. Node.js gebruikt een gebeurtenisgestuurd, niet-blokkerend I/O-model dat het lichtgewicht en efficiënt maakt, en dus perfect voor data-intensieve realtime-applicaties die op gedistribueerde apparaten worden uitgevoerd.
Express.js
Express.js is een Node.js-framework. U kunt de server en server-side code maken voor een toepassing zoals de meeste andere webtalen, maar dan met JavaScript.
Socket.IO
Socket.IO is een JavaScript-bibliotheek voor realtime webapplicaties. Het maakt realtime, bidirectionele communicatie tussen webclients en servers mogelijk. Het bestaat uit twee delen:een bibliotheek aan de clientzijde die in de browser wordt uitgevoerd en een bibliotheek aan de serverzijde voor Node.js. Beide componenten hebben bijna identieke API's.
Heroku
Heroku is een cloudplatform waarmee bedrijven apps kunnen bouwen, leveren, monitoren en schalen - het is de snelste manier om van idee naar URL te gaan, waarbij al die infrastructuurproblemen worden omzeild.
In dit artikel wordt ervan uitgegaan dat Redis, Node.js en de Heroku Toolbelt al op uw computer zijn geïnstalleerd.
Instellen
- Download de code uit de volgende repository: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags
- Voer npm install uit om de benodigde componenten te installeren
- Ten slotte kunt u de node-server starten door "node index.js" te doen. U kunt ook een "nodemon" uitvoeren die ook let op bestandswijzigingen.
U kunt hier ook toegang krijgen tot een gehoste versie van deze app: https://node-socket-redis-stream-tweet.herokuapp.com/
Het proces
Hier is een korte beschrijving van het proces dat we zullen gebruiken om de demo-applicatie te bouwen:
1. We beginnen met het accepteren van een zoekopdracht van de gebruiker. De zoekopdracht kan Twitter-vermeldingen, hashtags of willekeurige zoekteksten zijn.
2. Zodra we de zoekopdracht hebben, sturen we deze naar de Streaming API van Twitter om tweets op te halen. Omdat het een stream is, luisteren we wanneer er tweets worden verzonden door de API.
3. Zodra een tweet is opgehaald, slaan we deze op in een Redis-lijst en zenden deze uit naar de front-end.
Wat zijn Redis-lijsten?
Redis-lijsten worden geïmplementeerd via gekoppelde lijsten. Dit betekent dat zelfs als u miljoenen elementen in een lijst heeft, de bewerking van het toevoegen van een nieuw element aan het begin of aan het einde van de lijst in constante tijd wordt uitgevoerd. De snelheid van het toevoegen van een nieuw element met het LPUSH-commando aan de kop van een lijst met tien elementen is hetzelfde als het toevoegen van een element aan de kop van een lijst met 10 miljoen elementen.
In onze applicatie zullen we de via de API ontvangen tweets opslaan in een lijst met de naam "tweets". We zullen LPUSH gebruiken om de nieuw ontvangen tweet naar de lijst te pushen, deze bij te snijden met LTRIM, wat de hoeveelheid gebruikte schijfruimte beperkt (omdat het schrijven van een stream veel ruimte in beslag kan nemen), de nieuwste tweet ophalen met LRANGE en deze uitzenden naar de front-end waar het aan de streaminglijst wordt toegevoegd.
Wat is LPUSH, LTRIM en LRANGE?
Dit is een set Redis-opdrachten die worden gebruikt om gegevens aan een lijst toe te voegen. Hier is een korte beschrijving:
LPUSH
Voeg alle opgegeven waarden toe aan de kop van de lijst die is opgeslagen op sleutel. Als de sleutel niet bestaat, wordt deze gemaakt als een lege lijst voordat de push-bewerkingen worden uitgevoerd. Als de sleutel een waarde bevat die geen lijst is, wordt een fout geretourneerd.
redis> LPUSH mylist "world" (integer) 1 redis> LPUSH mylist "hello" (integer) 2 redis> LRANGE mylist 0 -1 1) "hello" 2) "world"
LTRIM
Trim een bestaande lijst zodat deze alleen het gespecificeerde bereik van elementen bevat. Zowel start als stop zijn op nul gebaseerde indexen, waarbij 0 het eerste element van de lijst is (de kop), 1 het volgende element enzovoort.
redis> RPUSH mylist "one" (integer) 1 redis> RPUSH mylist "two" (integer) 2 redis> RPUSH mylist "three" (integer) 3 redis> LTRIM mylist 1 -1 "OK" redis> LRANGE mylist 0 -1 1) "two" 2) "three"
LRANGE
Retourneert de opgegeven elementen van de lijst die is opgeslagen op sleutel. De begin- en eindpuntverschuivingen zijn op nul gebaseerde indexen, waarbij 0 het eerste element van de lijst is (de kop van de lijst), 1 het volgende is, enzovoort.
Deze offsets kunnen ook negatieve getallen zijn die posities vanaf het einde van de lijst aangeven. -1 is bijvoorbeeld het laatste element van de lijst, -2 het voorlaatste, enzovoort.
redis> RPUSH mylist "one" (integer) 1 redis> RPUSH mylist "two" (integer) 2 redis> RPUSH mylist "three" (integer) 3 redis> LRANGE mylist 0 0 1) "one" redis> LRANGE mylist -3 2 1) "one" 2) "two" 3) "three"
De applicatie bouwen
Onze demo vereist zowel een front-end als een back-end. Onze front-end is een vrij eenvoudig tekstvak met een knop die zal worden gebruikt om de stream te starten.
$('body').on('click', '.btn-search', function() { $('#tweets_area').empty(); $(this).text('Streaming...').attr('disabled', true); $.ajax({ url: '/search', type: 'POST', data: { val: $.trim($('.search-txt').val()) } }); });
We hebben een hulpfunctie nodig om een tweetbox te bouwen zodra we de tweet van onze back-end hebben ontvangen:
var _buildTweetBox = function(status) { var html = ''; html += '<div class="media tweet-single">'; html += ' <div class="media-left">'; html += ' <a href="https://twitter.com/' + status.user.screen_name + '" target="_blank" title="' + status.user.name + '">'; html += ' <img class="media-object" src="' + status.user.profile_image_url_https + '" alt="' + status.user.name + '" />'; html += ' </a>'; html += ' </div>'; html += ' <div class="media-body">'; html += ' <h5 class="media-heading"><a href="https://twitter.com/' + status.user.screen_name + '" target="_blank">' + status.user.screen_name + '</a></h5>'; html += '<p class="tweet-body" title="View full tweet" data-link="https://twitter.com/' + status.user.screen_name + '/status/' + status.id_str + '">' + status.text + '</p>'; html += ' </div>'; html += '</div>'; $('#tweets_area').prepend(html); $('#tweets_area').find('.tweet-single').first().fadeIn('slow'); };
We hebben ook een luisteraar nodig om de stream te stoppen en te voorkomen dat er nog meer tweets aan de streaminglijst worden toegevoegd:
socket.on('stream:destroy', function(status) { $('.btn-search').text('Start streaming').removeAttr('disabled'); $('.alert-warning').fadeIn('slow'); setTimeout(function() { $('.alert-warning').fadeOut('slow'); }, STREAM_END_TIMEOUT * 1000); });
Laten we overschakelen naar de achterkant en beginnen met het schrijven van onze /search-API.
/** * API - Search */ app.post('/search', function(req, res, next) { _searchTwitter(req.body.val); res.send({ status: 'OK' }); }); /** * Stream data from Twitter for input text * * 1. Use the Twitter streaming API to track a specific value entered by the user * 2. Once we have the data from Twitter, add it to a Redis list using LPUSH * 3. After adding to list, limit the list using LTRIM so the stream doesn't overflow the disk * 4. Use LRANGE to fetch the latest tweet and emit it to the front-end using 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); }); }
Bovenstaande code bevat de kern van onze back-end. Zodra een verzoek is ontvangen bij /search, starten we de stream met behulp van de streaming-API van Twitter die een stream-object retourneert.
twit.stream('statuses/filter', {track: val}, function(stream) {});
We kunnen naar het stream-object luisteren voor een sleutel genaamd 'data' die ons een nieuwe tweet zal sturen wanneer deze beschikbaar is.
stream.on('data', function(data) {});
Het "data" -object bevat de tweet-JSON die er ongeveer zo uit kan zien (een deel van de reactie is weggelaten):
{ "created_at": "Wed Jul 26 08:01:56 +0000 2017", "id": 890119982641803300, "id_str": "890119982641803264", "text": "RT @FoxNews: Jim DeMint: \"There is no better man than Jeff Sessions, and no greater supporter...of [President #Trump's] agenda.\"… ", "source": "<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>", "truncated": false, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "in_reply_to_screen_name": null, "user": { "id": 4833141138, "id_str": "4833141138", "name": "randy joe davis", "screen_name": "randyjoedavis1", "location": null, "url": null, "description": "Conservative Patriot, retired military, retired DOD civilian. cattle farmer, horseman, adventurer. 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": "Sat 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": "C0DEED", "profile_sidebar_fill_color": "DDEEF6", "profile_text_color": "333333", "profile_use_background_image": true, "profile_image_url": "http://pbs.twimg.com/profile_images/883522005210943488/rqyyXlEX_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/883522005210943488/rqyyXlEX_normal.jpg", "default_profile": true, "default_profile_image": false, "following": null, "follow_request_sent": null, "notifications": null } }
We slaan dit antwoord op in een Redis-lijst met de naam "tweets" met LPUSH:
client.lpush('tweets', JSON.stringify(data), function() {});
Zodra de tweet is opgeslagen, trimmen we de lijst met LTRIM om een maximaal aantal tweets te behouden (zodat onze schijfruimte niet vol raakt):
client.ltrim('tweets', 0, TWEETS_TO_KEEP, function() {});
Nadat we de lijst hebben bijgesneden, halen we de nieuwste tweet op met LRANGE en sturen deze naar de front-end:
client.lrange('tweets', 0, 1, function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0])); });
Aangezien dit een demo-applicatie is, moeten we de stream na een bepaalde tijd ook handmatig vernietigen, zodat deze niet naar de schijf blijft schrijven:
stream.on('end', function(response) { io.emit('stream:destroy'); }); setTimeout(stream.destroy, STREAM_TIMEOUT * 1000);
En je bent klaar! Start de server op met npm start en geniet van de streamingervaring.
Een demo van de applicatie is hier beschikbaar: https://node-socket-redis-stream-tweet.herokuapp.com/
Bekijk hun documenten voor het implementeren van deze applicatie op Heroku:https://devcenter.heroku.com/categories/deployment
De volledige broncode is ook beschikbaar op GitHub zodat u kunt forken en werken aan:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -twitter-hashtags
Als je iets geweldigs bouwt, kun je ons er zoals altijd over tweeten @scalegridio.
Als je hulp nodig hebt bij het beheer en de hosting van Redis™*, neem dan contact met ons op via [email protected] voor meer informatie.