この記事では、ユーザーが入力した検索クエリに基づいて、ツイートのストリーミングリストを作成します。ツイートはTwitterのストリーミングAPIを使用してフェッチされ、Redisリストに保存され、Socket.ioを使用してフロントエンドで更新されます。主に、ツイートをフェッチするためのキャッシュレイヤーとしてRedisを使用します。
はじめに
使用するテクノロジーの簡単な説明は次のとおりです。
Redis
Redisは、オープンソース(BSDライセンス)のメモリ内データ構造ストアであり、データベース、キャッシュ、およびメッセージブローカーとして使用されます。文字列、ハッシュ、リスト、セット、範囲クエリを使用した並べ替えられたセット、ビットマップ、ハイパーログログ、半径クエリを使用した地理空間インデックスなどのデータ構造をサポートします。
Node.js
Node.jsは、ChromeのJavaScriptランタイム上に構築されたプラットフォームであり、高速でスケーラブルなネットワークアプリケーションを簡単に構築できます。 Node.jsは、イベント駆動型の非ブロッキングI / Oモデルを使用しており、軽量で効率的であるため、分散デバイス間で実行されるデータ集約型のリアルタイムアプリケーションに最適です。
Express.js
Express.jsはNode.jsフレームワークです。他のほとんどのWeb言語と同様に、JavaScriptを使用して、アプリケーションのサーバーおよびサーバー側のコードを作成できます。
Socket.IO
Socket.IOは、リアルタイムWebアプリケーション用のJavaScriptライブラリです。これにより、Webクライアントとサーバー間のリアルタイムの双方向通信が可能になります。これには、ブラウザーで実行されるクライアント側ライブラリとNode.js用のサーバー側ライブラリの2つの部分があります。どちらのコンポーネントにもほぼ同じAPIがあります。
Heroku
Herokuは、企業がアプリを構築、配信、監視、スケーリングできるようにするクラウドプラットフォームです。これは、インフラストラクチャの問題をすべて回避して、アイデアからURLに移行するための最速の方法です。
この記事は、Redis、Node.js、およびHerokuToolbeltがマシンにすでにインストールされていることを前提としています。
セットアップ
-次のリポジトリからコードをダウンロードします:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags
-npm installを実行して、必要なコンポーネントをインストールします
-最後に、「nodeindex.js」を実行してノードサーバーを起動できます。ファイルの変更を監視する「nodemon」を実行することもできます。
こちらから、このアプリのホストバージョンにアクセスすることもできます:https://node-socket-redis-stream-tweet.herokuapp.com/
プロセス
デモアプリケーションの構築に使用するプロセスの簡単な説明は次のとおりです。
1.ユーザーからの検索クエリを受け入れることから始めます。クエリには、Twitterのメンション、ハッシュタグ、または任意のランダム検索テキストを使用できます。
2.検索クエリを取得したら、それをTwitterのStreaming APIに送信して、ツイートを取得します。ストリームなので、APIからツイートが送信されたときにリッスンします。
3.ツイートが取得されるとすぐに、Redisリストに保存され、フロントエンドにブロードキャストされます。
Redisリストとは何ですか?
Redisリストは、リンクリストを介して実装されます。つまり、リスト内に数百万の要素がある場合でも、リストの先頭または末尾に新しい要素を追加する操作は一定時間で実行されます。 LPUSHコマンドを使用して新しい要素を10個の要素を含むリストの先頭に追加する速度は、1,000万個の要素を含むリストの先頭に要素を追加するのと同じです。
このアプリケーションでは、APIを介して受信したツイートを「ツイート」というリストに保存します。 LPUSHを使用して、新しく受信したツイートをリストにプッシュし、使用するディスク容量を制限するLTRIMを使用してトリミングし(ストリームの書き込みには多くのスペースが必要になる可能性があるため)、LRANGEを使用して最新のツイートを取得し、にブロードキャストします。ストリーミングリストに追加されるフロントエンド。
LPUSH、LTRIM、LRANGEとは何ですか?
これらは、リストにデータを追加するために使用されるRedisコマンドのセットです。簡単な説明は次のとおりです:
LPUSH
キーに保存されているリストの先頭に、指定されたすべての値を挿入します。キーが存在しない場合は、プッシュ操作を実行する前に空のリストとして作成されます。キーがリストではない値を保持している場合、エラーが返されます。
redis> LPUSH mylist "world" (integer) 1 redis> LPUSH mylist "hello" (integer) 2 redis> LRANGE mylist 0 -1 1) "hello" 2) "world"
LTRIM
指定された要素の範囲のみが含まれるように、既存のリストをトリミングします。 startとstopはどちらもゼロベースのインデックスであり、0はリストの最初の要素(先頭)、1は次の1つの要素などです。
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
キーに保存されているリストの指定された要素を返します。オフセットの開始と停止はゼロベースのインデックスであり、0はリストの最初の要素(リストの先頭)、1は次の要素などです。
これらのオフセットは、リストの最後からの位置を示す負の数にすることもできます。たとえば、-1はリストの最後の要素、-2は最後から2番目の要素などです。
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"
アプリケーションの構築
このデモには、フロントエンドとバックエンドの両方が必要です。フロントエンドは、ストリームの開始に使用されるボタンが付いた非常にシンプルなテキストボックスです。
$('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())
}
});
});
バックエンドからツイートを受け取ったら、ツイートボックスを作成するためのヘルパー関数が必要です:
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');
}; また、ストリームを停止し、ストリーミングリストにツイートが追加されないようにするリスナーも必要です。
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);
});
バックエンド側に切り替えて、/searchAPIの作成を始めましょう。
/**
* 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);
});
}
上記のコードには、バックエンドのコアが含まれています。 / searchでリクエストを受信すると、ストリームオブジェクトを返すTwitterのストリーミングAPIを使用してストリームを開始します。
twit.stream('statuses/filter', {track: val}, function(stream) {});
ストリームオブジェクトで「データ」と呼ばれるキーをリッスンできます。このキーは、利用可能になったときに新しいツイートを送信します。
stream.on('data', function(data) {});
「data」オブジェクトには、次のようなツイートJSONが含まれています(応答の一部が省略されています):
{
"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=\"https://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": "https://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
}
}
この応答は、LPUSHを使用して「ツイート」と呼ばれるRedisリストに保存されます:
client.lpush('tweets', JSON.stringify(data), function() {});
ツイートが保存されたら、LTRIMを使用してリストをトリミングし、ツイートの最大数を維持します(ディスク容量がいっぱいにならないようにします):
client.ltrim('tweets', 0, TWEETS_TO_KEEP, function() {});
リストをトリミングした後、LRANGEを使用して最新のツイートを取得し、フロントエンドに送信します。
client.lrange('tweets', 0, 1, function(err, tweetListStr) {
io.emit('savedTweetToRedis', JSON.parse(tweetListStr[0]));
});
これはデモアプリケーションであるため、特定の時間後にストリームを手動で破棄して、ディスクへの書き込みを継続しないようにする必要もあります。
stream.on('end', function(response) {
io.emit('stream:destroy');
});
setTimeout(stream.destroy, STREAM_TIMEOUT * 1000);
これで完了です。 npm startを使用してサーバーを起動し、ストリーミングエクスペリエンスをお楽しみください。
アプリケーションのデモはこちらから入手できます:https://node-socket-redis-stream-tweet.herokuapp.com/
このアプリケーションをHerokuにデプロイするには、ドキュメントを確認してください:https://devcenter.heroku.com/categories/deployment
ソースコード全体はGitHubでも入手でき、フォークして作業できます:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -twitter-ハッシュタグ
いつものように、素晴らしいものを作成する場合は、@scalegridioでツイートしてください。
Redis™*の管理とホスティングについてサポートが必要な場合は、support@scalegrid.ioまでお問い合わせください。