Redis
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> Redis

Lưu vào bộ đệm các tweet bằng Node.js, Redis và Socket.io

Trong bài viết này, chúng tôi sẽ xây dựng danh sách các tweet phát trực tuyến dựa trên truy vấn tìm kiếm do người dùng nhập. Các tweet sẽ được tìm nạp bằng API phát trực tuyến của Twitter, được lưu trữ trong danh sách Redis và được cập nhật trong giao diện người dùng bằng Socket.io. Chúng tôi chủ yếu sẽ sử dụng Redis làm lớp bộ nhớ đệm để tìm nạp các tweet.

Giới thiệu

Dưới đây là mô tả ngắn gọn về các công nghệ chúng tôi sẽ sử dụng:

Redis

Redis là một kho lưu trữ cấu trúc dữ liệu trong bộ nhớ, mã nguồn mở (được cấp phép BSD), được sử dụng làm cơ sở dữ liệu, bộ đệm và môi giới tin nhắn. Nó hỗ trợ các cấu trúc dữ liệu như chuỗi, băm, danh sách, tập hợp, tập hợp được sắp xếp với truy vấn phạm vi, bitmap, siêu nhật ký và chỉ mục không gian địa lý với truy vấn bán kính.

Node.js

Node.js là nền tảng được xây dựng dựa trên thời gian chạy JavaScript của Chrome để dễ dàng tạo các ứng dụng mạng nhanh và có thể mở rộng. Node.js sử dụng mô hình I / O hướng sự kiện, không chặn, giúp nó nhẹ và hiệu quả, đồng thời hoàn hảo cho các ứng dụng thời gian thực sử dụng nhiều dữ liệu chạy trên các thiết bị phân tán.

Express.js

Express.js là một khung công tác Node.js. Bạn có thể tạo máy chủ và mã phía máy chủ cho một ứng dụng giống như hầu hết các ngôn ngữ web khác, nhưng sử dụng JavaScript.

Socket.IO

Socket.IO là một thư viện JavaScript dành cho các ứng dụng web thời gian thực. Nó cho phép giao tiếp theo thời gian thực, hai chiều giữa các máy khách và máy chủ web. Nó có hai phần:thư viện phía máy khách chạy trên trình duyệt và thư viện phía máy chủ cho Node.js. Cả hai thành phần đều có các API gần giống nhau.

Heroku

Heroku là một nền tảng đám mây cho phép các công ty xây dựng, phân phối, giám sát và mở rộng quy mô ứng dụng - đây là cách nhanh nhất để chuyển từ ý tưởng đến URL, vượt qua tất cả những vấn đề đau đầu về cơ sở hạ tầng.

Bài viết này giả định rằng bạn đã cài đặt Redis, Node.js và Heroku Toolbelt trên máy tính của mình.

Thiết lập

- Tải xuống mã từ kho lưu trữ sau:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis-twitter-hashtags

- Chạy npm install để cài đặt các thành phần cần thiết

- Cuối cùng, bạn có thể khởi động máy chủ nút bằng cách thực hiện “node index.js”. Bạn cũng có thể chạy “gật đầu” để theo dõi các thay đổi của tệp.

Bạn cũng có thể truy cập phiên bản được lưu trữ trên máy chủ của ứng dụng này tại đây:https://node-socket-redis-stream-tweet.herokuapp.com/

Quy trình

Dưới đây là mô tả ngắn gọn về quy trình mà chúng tôi sẽ sử dụng để xây dựng ứng dụng demo:

1. Chúng tôi sẽ bắt đầu bằng cách chấp nhận truy vấn tìm kiếm từ người dùng. Truy vấn có thể là đề cập trên Twitter, thẻ bắt đầu bằng # hoặc bất kỳ văn bản tìm kiếm ngẫu nhiên nào.

2. Khi chúng tôi có truy vấn tìm kiếm, chúng tôi sẽ gửi nó tới API phát trực tuyến của Twitter để tìm nạp các tweet. Vì nó là một luồng, chúng tôi sẽ lắng nghe khi các tweet được gửi bởi API.

3. Ngay sau khi một tweet được truy xuất, chúng tôi sẽ lưu trữ nó trong danh sách Redis và phát nó lên front-end.

Danh sách Redis là gì?

Danh sách Redis được triển khai thông qua Danh sách liên kết. Điều này có nghĩa là ngay cả khi bạn có hàng triệu phần tử bên trong danh sách, thì thao tác thêm phần tử mới ở đầu hoặc ở cuối danh sách vẫn được thực hiện trong thời gian không đổi. Tốc độ thêm một phần tử mới bằng lệnh LPUSH vào đầu danh sách có 10 phần tử cũng giống như việc thêm một phần tử vào đầu danh sách có 10 triệu phần tử.

Trong ứng dụng của chúng tôi, chúng tôi sẽ lưu trữ các tweet nhận được qua API trong một danh sách được gọi là “tweet”. Chúng tôi sẽ sử dụng LPUSH để đẩy tweet mới nhận được vào danh sách, cắt nó bằng LTRIM hạn chế dung lượng ổ đĩa được sử dụng (vì việc viết một luồng có thể chiếm nhiều dung lượng), tìm nạp tweet mới nhất bằng LRANGE và phát nó lên giao diện người dùng nơi nó sẽ được thêm vào danh sách phát trực tuyến.

LPUSH, LTRIM và LRANGE là gì?

Đây là một tập hợp các lệnh Redis được sử dụng để thêm dữ liệu vào danh sách. Đây là một mô tả ngắn gọn:

LPUSH

Chèn tất cả các giá trị đã chỉ định vào đầu danh sách được lưu trữ tại khóa. Nếu khóa không tồn tại, nó sẽ được tạo dưới dạng danh sách trống trước khi thực hiện các thao tác đẩy. Khi khóa giữ một giá trị không phải là danh sách, thì một lỗi sẽ được trả về.

redis> LPUSH mylist "world"
(integer) 1

redis> LPUSH mylist "hello"
(integer) 2

redis> LRANGE mylist 0 -1
1) "hello"
2) "world"

LTRIM

Cắt danh sách hiện có để nó chỉ chứa phạm vi các phần tử được chỉ định. Cả điểm bắt đầu và điểm dừng đều là các chỉ mục dựa trên 0, trong đó 0 là phần tử đầu tiên của danh sách (phần đầu), 1 là phần tử tiếp theo, v.v.

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"

NGÔN NGỮ

Trả về các phần tử được chỉ định của danh sách được lưu trữ tại khóa. Các hiệu số bắt đầu và dừng là các chỉ mục dựa trên 0, với 0 là phần tử đầu tiên của danh sách (phần đầu của danh sách), 1 là phần tử tiếp theo, v.v.

Các hiệu số này cũng có thể là số âm cho biết các vị trí từ cuối danh sách. Ví dụ:-1 là phần tử cuối cùng của danh sách, -2 là phần tử áp chót, v.v.

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"

Xây dựng ứng dụng

Bản demo của chúng tôi yêu cầu cả front-end và back-end. Giao diện người dùng của chúng tôi là một hộp văn bản khá đơn giản với một nút sẽ được sử dụng để bắt đầu luồng.

$('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())
       }
   });
});

Chúng tôi cần một chức năng trợ giúp để tạo hộp tweet sau khi chúng tôi nhận được tweet từ back-end của chúng tôi:

 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');
};

Chúng tôi cũng cần một người nghe để dừng luồng và ngăn thêm bất kỳ tweet nào khác vào danh sách phát trực tuyến:

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);
});

Hãy chuyển sang mặt sau của mọi thứ và bắt đầu viết / API tìm kiếm của chúng tôi.

/**
 * 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);
    });
}

Đoạn mã trên chứa cốt lõi của back-end của chúng tôi. Sau khi nhận được yêu cầu tại / search, chúng tôi bắt đầu luồng bằng API phát trực tuyến của Twitter trả về một đối tượng luồng.

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

Chúng tôi có thể lắng nghe đối tượng luồng để tìm một khóa gọi là “dữ liệu” sẽ gửi cho chúng tôi một tweet mới khi có sẵn.

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

Đối tượng "data" chứa JSON tweet có thể trông giống như sau (một phần của phản hồi đã bị bỏ qua):

{
 "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
 }
}

Chúng tôi lưu trữ phản hồi này trong danh sách Redis có tên là “tweet” sử dụng LPUSH:

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

Sau khi tweet đã được lưu, chúng tôi sẽ cắt danh sách bằng cách sử dụng LTRIM để giữ lại số lượng tweet tối đa (để không gian đĩa của chúng tôi không bị đầy):

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

Sau khi cắt bỏ danh sách, chúng tôi tìm nạp tweet mới nhất bằng LRANGE và phát nó ra front-end:

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

Vì đây là ứng dụng demo nên chúng tôi cũng cần phải hủy luồng theo cách thủ công sau một thời gian cụ thể để luồng không tiếp tục ghi vào đĩa:

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

Và bạn đã hoàn thành! Khởi động máy chủ bằng npm start và tận hưởng trải nghiệm phát trực tuyến.

Bản demo của ứng dụng có sẵn tại đây:https://node-socket-redis-stream-tweet.herokuapp.com/

Để triển khai ứng dụng này trên Heroku, hãy xem tài liệu của họ:https://devcenter.heroku.com/categories/deployment

Toàn bộ mã nguồn cũng có sẵn trên GitHub để bạn fork và làm việc trên:https://github.com/Scalegrid/code-samples/tree/sg-redis-node-socket-twitter-search/node-socket-redis -twitter-hashtags

Như mọi khi, nếu bạn xây dựng một thứ gì đó tuyệt vời, hãy tweet cho chúng tôi về nó @scalegridio.

Nếu bạn cần trợ giúp về quản lý và lưu trữ cho Redis ™ *, hãy liên hệ với chúng tôi theo địa chỉ [email protected] để biết thêm thông tin.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Tập lệnh Lua cho Redis tính tổng các giá trị của các khóa

  2. Cách phân tích và tối ưu hóa việc sử dụng bộ nhớ trong Redis

  3. Tiết kiệm HASH cho Redis trên ứng dụng rails

  4. Khắc phục sự cố Quyền bị từ chối khi cố gắng kết nối với Redis từ tập lệnh PHP

  5. Cách tách cơ sở dữ liệu redis cho hai ứng dụng giống nhau trong node.js