W tym artykule zbudujemy przesyłaną strumieniowo listę tweetów na podstawie zapytania wprowadzonego przez użytkownika. Tweety będą pobierane za pomocą Streaming API Twittera, przechowywane na liście Redis i aktualizowane w interfejsie użytkownika za pomocą Socket.io. Będziemy przede wszystkim używać Redisa jako warstwy pamięci podręcznej do pobierania tweetów.
Wprowadzenie
Oto krótki opis technologii, których będziemy używać:
Ponowne
Redis to magazyn struktury danych typu open source (na licencji BSD), używany jako baza danych, pamięć podręczna i broker komunikatów. Obsługuje struktury danych, takie jak ciągi, skróty, listy, zestawy, posortowane zestawy z zapytaniami o zakres, mapy bitowe, hiperloglogi i indeksy geoprzestrzenne z zapytaniami o promień.
Node.js
Node.js to platforma oparta na środowisku wykonawczym JavaScript Chrome do łatwego tworzenia szybkich i skalowalnych aplikacji sieciowych. Node.js wykorzystuje sterowany zdarzeniami, nieblokujący model we/wy, który sprawia, że jest lekki i wydajny, a tym samym idealny do aplikacji czasu rzeczywistego z dużą ilością danych, które działają na urządzeniach rozproszonych.
Express.js
Express.js to framework Node.js. Możesz utworzyć serwer i kod po stronie serwera dla aplikacji, podobnie jak większość innych języków internetowych, ale przy użyciu JavaScript.
Gniazdo.IO
Socket.IO to biblioteka JavaScript dla aplikacji internetowych czasu rzeczywistego. Umożliwia dwukierunkową komunikację w czasie rzeczywistym między klientami sieciowymi a serwerami. Składa się z dwóch części:biblioteki po stronie klienta, która działa w przeglądarce oraz biblioteki po stronie serwera dla Node.js. Oba komponenty mają prawie identyczne interfejsy API.
Heroku
Heroku to platforma w chmurze, która pozwala firmom tworzyć, dostarczać, monitorować i skalować aplikacje — to najszybszy sposób na przejście od pomysłu do adresu URL, omijający wszystkie problemy związane z infrastrukturą.
W tym artykule założono, że masz już zainstalowane Redis, Node.js i Heroku Toolbelt na swoim komputerze.
Konfiguracja
- Pobierz kod z następującego repozytorium: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags
- Uruchom instalację npm, aby zainstalować niezbędne komponenty
- Wreszcie możesz uruchomić serwer węzłów, wykonując „node index.js”. Możesz także uruchomić „nodemona”, który również obserwuje zmiany w plikach.
Dostęp do hostowanej wersji tej aplikacji możesz również uzyskać tutaj:https://node-socket-redis-stream-tweet.herokuapp.com/
Proces
Oto krótki opis procesu, którego użyjemy do zbudowania aplikacji demonstracyjnej:
1. Zaczniemy od zaakceptowania zapytania wyszukiwania od użytkownika. Zapytanie może zawierać wzmianki na Twitterze, hashtagi lub dowolny losowy tekst wyszukiwania.
2. Gdy otrzymamy zapytanie, wyślemy je do API Streaming API na Twitterze, aby pobrać tweety. Ponieważ jest to strumień, będziemy nasłuchiwać tweetów wysyłanych przez API.
3. Gdy tylko tweet zostanie pobrany, zapiszemy go na liście Redis i wyemitujemy do interfejsu użytkownika.
Co to są listy Redis?
Listy Redis są implementowane za pośrednictwem list połączonych. Oznacza to, że nawet jeśli masz miliony elementów na liście, operacja dodawania nowego elementu na początku lub na końcu listy jest wykonywana w stałym czasie. Szybkość dodawania nowego elementu za pomocą polecenia LPUSH na początku listy zawierającej dziesięć elementów jest taka sama, jak dodawanie elementu na początku listy zawierającej 10 milionów elementów.
W naszej aplikacji będziemy przechowywać tweety otrzymane przez API na liście zwanej „tweety”. Użyjemy LPUSH, aby umieścić nowo otrzymany tweet na liście, przyciąć go za pomocą LTRIM, co ogranicza ilość używanego miejsca na dysku (ponieważ pisanie strumienia może zająć dużo miejsca), pobrać najnowszy tweet za pomocą LRANGE i przesłać go do front-end, w którym zostanie dołączony do listy przesyłania strumieniowego.
Co to jest LPUSH, LTRIM i LRANGE?
Jest to zestaw poleceń Redis, które służą do dodawania danych do listy. Oto krótki opis:
LPUSH
Wstaw wszystkie określone wartości na początku listy przechowywanej w kluczu. Jeśli klucz nie istnieje, jest tworzony jako pusta lista przed wykonaniem operacji push. Gdy klucz zawiera wartość, która nie jest listą, zwracany jest błąd.
redis> moja lista LPUSH "świat"(liczba całkowita) 1redis> moja lista LPUSH "hello"(liczba całkowita) 2redis> LRANGE moja lista 0 -11) "hello"2) "świat"
LTRIM
Przytnij istniejącą listę tak, aby zawierała tylko określony zakres elementów. Zarówno start, jak i stop są indeksami liczonymi od zera, gdzie 0 jest pierwszym elementem listy (nagłówkiem), 1 kolejnym elementem i tak dalej.
ROZMIAR
Zwraca określone elementy listy przechowywane w klucz. Przesunięcia początku i końca są indeksami liczonymi od zera, gdzie 0 jest pierwszym elementem listy (nagłówkiem listy), 1 jest następnym i tak dalej.
Te przesunięcia mogą być również liczbami ujemnymi wskazującymi pozycje od końca listy. Na przykład -1 jest ostatnim elementem listy, -2 przedostatnim i tak dalej.
redis> RPUSH mojalista "jeden"(liczba całkowita) 1redis> RPUSH mojalista "dwa"(liczba całkowita) 2redis> RPUSH mojalista "trzy"(liczba całkowita) 3redis> LRANGE mojalista 0 01) "jeden"redis> LRANGE mojalista -3 21 ) „jeden”2) „dwa”3) „trzy”
Tworzenie aplikacji
Nasze demo wymaga zarówno front-endu, jak i back-endu. Nasz interfejs to całkiem proste pole tekstowe z przyciskiem, który będzie używany do rozpoczęcia transmisji.
$('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()) } });});
Potrzebujemy funkcji pomocniczej do zbudowania tweeta po otrzymaniu tweeta z naszego zaplecza:
var _buildTweetBox =function(status) { var html =''; html +=''; html +=''; $('#tweets_area').prepend(html); $('#tweets_area').find('.tweet-single').first().fadeIn('slow');};'; html +=' '; html +=' '; html +=' '; html +=''; html +=''; html +=''; html +='' + status.user.screen_name + '
'; html +='' + status.tekst + '
'; html +='
Potrzebujemy również słuchacza, aby zatrzymać transmisję i zapobiec dodawaniu kolejnych tweetów do listy transmisji:
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);});
Przejdźmy do back-endowej strony rzeczy i zacznijmy pisać nasze /search API.
/** * API - Szukaj */app.post('/search', function(req, res, next) { _searchTwitter(req.body.val); res.send({ status:'OK' } );});/** * Przesyłaj dane z Twittera w celu wprowadzenia tekstu * * 1. Użyj interfejsu API przesyłania strumieniowego Twittera, aby śledzić konkretną wartość wprowadzoną przez użytkownika * 2. Gdy mamy dane z Twittera, dodaj je do Redis lista za pomocą LPUSH * 3. Po dodaniu do listy ogranicz listę za pomocą LTRIM, aby strumień nie przepełniał dysku * 4. Użyj LRANGE, aby pobrać najnowszy tweet i wyemitować go do front-endu za pomocą Socket.io * * @ param {String} val Query String * @return */var _searchTwitter =function(val) { twit.stream('statusy/filtr', {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('tweety', 0, 1 , function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0])); }); }); }); }); stream.on('zniszcz', function(odpowiedź) { io.emit('stream:zniszcz'); }); stream.on('end', function(response) { io.emit('stream:destroy'); }); setTimeout(stream.destroy, STREAM_TIMEOUT * 1000); });}
Powyższy kod zawiera rdzeń naszego zaplecza. Po otrzymaniu żądania w /search rozpoczynamy transmisję za pomocą interfejsu API do przesyłania strumieniowego Twittera, który zwraca obiekt strumienia.
twit.stream('statusy/filtr', {track:val}, function(stream) {});
Możemy nasłuchiwać obiektu strumienia dla klucza o nazwie „dane”, który wyśle nam nowy tweet, gdy będzie dostępny.
stream.on('data', function(data) {});
Obiekt „data” zawiera tweetowy JSON, który może wyglądać mniej więcej tak (część odpowiedzi została pominięta):
{ "created_at":"Środa 26 lipca 08:01:56 +0000 2017", "id":890119982641803300, "id_str":"890119982641803264", "text":"RT @FoxNews:Jim DeMint:\" Nie ma lepszego człowieka niż Jeff Sessions i nie ma większego zwolennika... agendy [Prezydenta #Trumpa].\"… ", "źródło":"Twitter dla Androida ", "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":"Konserwatywny patriota, emerytowany wojskowy, emerytowany cywil DOD. hodowca bydła, jeździec, poszukiwacz przygód. 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"6 , "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 }}
Przechowujemy tę odpowiedź na liście Redis o nazwie „tweety” za pomocą LPUSH:
client.lpush('tweety', JSON.stringify(data), function() {});
Po zapisaniu tweeta przycinamy listę za pomocą LTRIM, aby zachować maksymalną liczbę tweetów (aby nasze miejsce na dysku nie było pełne):
client.ltrim('tweety', 0, TWEETS_TO_KEEP, function() {});
Po przycięciu listy pobieramy najnowszy tweet za pomocą LRANGE i wysyłamy go do interfejsu:
client.lrange('tweets', 0, 1, function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0]));});
Ponieważ jest to aplikacja demonstracyjna, musimy również ręcznie zniszczyć strumień po określonym czasie, aby nie zapisywał się na dysku:
stream.on('end', function(response) { io.emit('stream:destroy');});setTimeout(stream.destroy, STREAM_TIMEOUT * 1000);
I jesteś skończony! Uruchom serwer za pomocą npm start i ciesz się transmisją strumieniową.
Demo aplikacji jest dostępne tutaj:https://node-socket-redis-stream-tweet.herokuapp.com/
Aby wdrożyć tę aplikację w Heroku, zapoznaj się z ich dokumentacją:https://devcenter.heroku.com/categories/deployment
Cały kod źródłowy jest również dostępny na GitHub, aby można było go rozwidlić i pracować:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -hashtagi na Twitterze
Jak zawsze, jeśli zbudujesz coś niesamowitego, tweetuj nam o tym @scalegridio.
Jeśli potrzebujesz pomocy w zarządzaniu i hostingu dla Redis™*, skontaktuj się z nami pod adresem [email protected], aby uzyskać więcej informacji.