Dalam artikel ini, kami akan membuat daftar streaming tweet berdasarkan kueri penelusuran yang dimasukkan oleh pengguna. Tweet akan diambil menggunakan API Streaming Twitter, disimpan dalam daftar Redis dan diperbarui di front-end menggunakan Socket.io. Kami terutama akan menggunakan Redis sebagai lapisan caching untuk mengambil tweet.
Pengantar
Berikut adalah deskripsi singkat tentang teknologi yang akan kami gunakan:
Merah
Redis adalah sumber terbuka (berlisensi BSD), penyimpanan struktur data dalam memori, digunakan sebagai database, cache, dan perantara pesan. Ini mendukung struktur data seperti string, hash, daftar, set, set yang diurutkan dengan kueri rentang, bitmap, hyperloglog, dan indeks geospasial dengan kueri radius.
Node.js
Node.js adalah platform yang dibangun di atas runtime JavaScript Chrome untuk dengan mudah membangun aplikasi jaringan yang cepat dan skalabel. Node.js menggunakan model I/O non-pemblokiran yang digerakkan oleh peristiwa yang membuatnya ringan dan efisien, sehingga sempurna untuk aplikasi waktu-nyata intensif data yang berjalan di seluruh perangkat terdistribusi.
Express.js
Express.js adalah kerangka kerja Node.js. Anda dapat membuat kode server dan sisi server untuk aplikasi seperti kebanyakan bahasa web lainnya, tetapi menggunakan JavaScript.
Socket.IO
Socket.IO adalah pustaka JavaScript untuk aplikasi web waktu nyata. Ini memungkinkan komunikasi dua arah secara real-time antara klien web dan server. Ini memiliki dua bagian:perpustakaan sisi klien yang berjalan di browser, dan perpustakaan sisi server untuk Node.js. Kedua komponen memiliki API yang hampir identik.
Heroku
Heroku adalah platform cloud yang memungkinkan perusahaan membangun, mengirimkan, memantau, dan menskalakan aplikasi — ini adalah cara tercepat untuk beralih dari ide ke URL, melewati semua masalah infrastruktur tersebut.
Artikel ini mengasumsikan bahwa Anda telah menginstal Redis, Node.js, dan Heroku Toolbelt di mesin Anda.
Penyiapan
- Unduh kode dari repositori berikut: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags
- Jalankan npm install untuk menginstal komponen yang diperlukan
- Terakhir, Anda dapat memulai server node dengan melakukan “node index.js”. Anda juga dapat menjalankan “nodemon” yang juga mengawasi perubahan file.
Anda juga dapat mengakses versi aplikasi yang dihosting di sini: https://node-socket-redis-stream-tweet.herokuapp.com/
Proses
Berikut adalah deskripsi singkat tentang proses yang akan kita gunakan untuk membangun aplikasi demo:
1. Kami akan mulai dengan menerima permintaan pencarian dari pengguna. Kueri dapat berupa sebutan Twitter, tagar, atau teks pencarian acak apa pun.
2. Setelah kami memiliki permintaan pencarian, kami akan mengirimkannya ke API Streaming Twitter untuk mengambil tweet. Karena ini adalah streaming, kami akan mendengarkan ketika tweet dikirim oleh API.
3. Segera setelah tweet diambil, kami akan menyimpannya dalam daftar Redis dan menyiarkannya ke front-end.
Apa itu daftar Redis?
Daftar Redis diimplementasikan melalui Daftar Tertaut. Artinya, meskipun Anda memiliki jutaan elemen di dalam daftar, operasi penambahan elemen baru di bagian atas atau di bagian belakang daftar dilakukan dalam waktu yang konstan. Kecepatan menambahkan elemen baru dengan perintah LPUSH ke kepala daftar dengan sepuluh elemen sama dengan menambahkan elemen ke kepala daftar dengan 10 juta elemen.
Dalam aplikasi kami, kami akan menyimpan tweet yang diterima melalui API dalam daftar yang disebut "tweet". Kami akan menggunakan LPUSH untuk mendorong tweet yang baru diterima ke daftar, memangkasnya menggunakan LTRIM yang membatasi jumlah ruang disk yang digunakan (karena menulis aliran mungkin memakan banyak ruang), mengambil tweet terbaru menggunakan LRANGE, dan menyiarkannya ke front-end yang akan ditambahkan ke daftar streaming.
Apa itu LPUSH, LTRIM, dan LRANGE?
Ini adalah sekumpulan perintah Redis yang digunakan untuk menambahkan data ke daftar. Berikut penjelasan singkatnya:
LPUSH
Masukkan semua nilai yang ditentukan di bagian atas daftar yang disimpan di kunci. Jika kunci tidak ada, itu dibuat sebagai daftar kosong sebelum melakukan operasi push. Saat kunci menyimpan nilai yang bukan merupakan daftar, kesalahan akan ditampilkan.
redis> LPUSH mylist "world" (integer) 1 redis> LPUSH mylist "hello" (integer) 2 redis> LRANGE mylist 0 -1 1) "hello" 2) "world"
LTRIM
Pangkas daftar yang ada sehingga hanya berisi rentang elemen yang ditentukan. Baik start dan stop adalah indeks berbasis nol, di mana 0 adalah elemen pertama dari daftar (kepala), 1 elemen berikutnya dan seterusnya.
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
Mengembalikan elemen tertentu dari daftar yang disimpan di kunci. Offset start dan stop adalah indeks berbasis nol, dengan 0 sebagai elemen pertama dari daftar (kepala daftar), 1 sebagai berikutnya, dan seterusnya.
Offset ini juga dapat berupa angka negatif yang menunjukkan posisi dari akhir daftar. Misalnya, -1 adalah elemen terakhir dari daftar, -2 kedua dari belakang, dan seterusnya.
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"
Membangun aplikasi
Demo kami membutuhkan front-end dan back-end. Front-end kami adalah kotak teks yang cukup sederhana dengan tombol yang akan digunakan untuk memulai streaming.
$('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()) } }); });
Kami membutuhkan fungsi pembantu untuk membuat kotak tweet setelah kami menerima tweet dari back-end kami:
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'); };
Kami juga membutuhkan pendengar untuk menghentikan streaming dan mencegah menambahkan tweet lagi ke daftar streaming:
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); });
Mari beralih ke sisi belakang dan mulai menulis /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); }); }
Kode di atas berisi inti dari back-end kami. Setelah permintaan diterima di /search, kami memulai streaming menggunakan API streaming Twitter yang mengembalikan objek streaming.
twit.stream('statuses/filter', {track: val}, function(stream) {});
Kami dapat mendengarkan objek aliran untuk kunci yang disebut "data" yang akan mengirimi kami tweet baru jika tersedia.
stream.on('data', function(data) {});
Objek "data" berisi tweet JSON yang mungkin terlihat seperti ini (bagian dari respons telah dihilangkan):
{ "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 } }
Kami menyimpan tanggapan ini dalam daftar Redis yang disebut “tweets” menggunakan LPUSH:
client.lpush('tweets', JSON.stringify(data), function() {});
Setelah tweet disimpan, kami memangkas daftar menggunakan LTRIM untuk menyimpan jumlah tweet maksimum (sehingga ruang disk kami tidak penuh):
client.ltrim('tweets', 0, TWEETS_TO_KEEP, function() {});
Setelah memangkas daftar, kami mengambil tweet terbaru menggunakan LRANGE dan mengirimkannya ke front-end:
client.lrange('tweets', 0, 1, function(err, tweetListStr) { io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0])); });
Karena ini adalah aplikasi demo, kami juga perlu memusnahkan streaming secara manual setelah waktu tertentu agar tidak terus menulis ke disk:
stream.on('end', function(response) { io.emit('stream:destroy'); }); setTimeout(stream.destroy, STREAM_TIMEOUT * 1000);
Dan Anda sudah selesai! Jalankan server menggunakan npm start dan nikmati pengalaman streaming.
Demo aplikasi tersedia di sini: https://node-socket-redis-stream-tweet.herokuapp.com/
Untuk menerapkan aplikasi ini di Heroku, lihat dokumen mereka:https://devcenter.heroku.com/categories/deployment
Seluruh kode sumber juga tersedia di GitHub untuk Anda gunakan dan kerjakan: https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -twitter-hashtag
Seperti biasa, jika Anda membuat sesuatu yang luar biasa, kirimkan tweet kepada kami tentangnya @scalegridio.
Jika Anda memerlukan bantuan dengan manajemen dan hosting untuk Redis™*, hubungi kami di [email protected] untuk informasi lebih lanjut.