DIYインスタントメッセージングサービス

私たちは皆ICQの使用に慣れており、多くの人がプロジェクトでこの機能を実装しています。 eJabberなどの既製のソリューションがあります。

自分でこれを行う方法に興味がある場合は、インスタントメッセージングサービスのサーバー部分が考慮されるカースト向けのウェルカムです。 クライアントでは、あなたが自分でそれを理解できることを願っています...

「Short Message Service-これは基本的にHTTP上で実行されるキューサーバーであり、JSフレームワークと簡単に統合できるはずです。 すべての結果はJSONで返されます。 サーバーとの交換は、AJAXを介してブラウザーから実行されます。

POSTメソッドを使用して、キー(URL(またはその一部))によってデータを書き込みます。 GETメソッドを使用して、書き込まれた内容をキューから(URLであるキーによって)抽出します。 したがって、次のようになります。uriがキーで、queueがデータである1つのハッシュテーブル。 キューはメッセージ文字列を表します。 必要に応じて、時間を追加できます。 開発のための多くのアイデアがありますが。

トピックにとても近い:
  1. #include <sys / types.h>
  2. #include <sys / time.h>
  3. #include <sys / queue.h>
  4. #include <stdlib.h>
  5. #include <err.h>
  6. #include <event.h>
  7. #include <evhttp.h>
  8. #include <map>
  9. #include <string>
  10. #include <queue>
  11. 名前空間 std を使用し ます 。
  12. std :: map < string、queue < string >> ht ;
  13. void generic_handler ( struct evhttp_request * req、 void * arg )
  14. {
  15. struct evbuffer * buf ;
  16. buf = evbuffer_new ( ) ;
  17. if ( buf == NULL )
  18. err ( 1 、 "応答バッファの作成に失敗しました" ) ;
  19. string key = evhttp_request_uri ( req ) ;
  20. ストリングアウト;
  21. if ( req- > type == EVHTTP_REQ_POST ) {
  22. const char * str_len = evhttp_find_header ( req- > input_headers、 "Content-Length" ) ;
  23. int len = atoi ( str_len ) ;
  24. アウト。 assign ( ( const char * ) EVBUFFER_DATA ( req- > input_buffer ) 、len ) ;
  25. if ( ht。find ( key ) == ht。end ( ) ) {
  26. キュー<文字列> q ;
  27. q。 プッシュ (アウト) ;
  28. ht。 insert ( pair < string、queue < string >> ( key、q ) ) ;
  29. } その他
  30. ht [キー] 。 プッシュ (アウト) ;
  31. evbuffer_add_printf ( buf、 "{ \" result \ " : \" Ok \ " } \ r \ n " ) ;
  32. } else {
  33. if ( ht。find ( key ) == ht。end ( ) ) {
  34. evbuffer_add_printf ( buf、 "{ \" result \ " :null} \ r \ n " ) ;
  35. } else {
  36. queue <文字列> q = ht [キー] ;
  37. if ( q。 サイズ ( ) ) {
  38. out = q。 フロント ( ) ;
  39. q。 ポップ ( ) ;
  40. ht [キー] = q ;
  41. evbuffer_add_printf ( buf、 "{ \" result \ " : \" %s \ " }" 、 out。c_str ( ) ) ;
  42. } else {
  43. evbuffer_add_printf ( buf、 "{ \" result \ " :null}" )
  44. }
  45. }
  46. }
  47. evhttp_send_reply ( req、HTTP_OK、 "OK" 、buf ) ;
  48. }
  49. int main ( int argc、 char ** argv )
  50. {
  51. struct evhttp * httpd ;
  52. event_init ( ) ;
  53. httpd = evhttp_start ( "0.0.0.0" 、 8080 ) ;
  54. evhttp_set_gencb ( httpd、generic_handler、 NULL ) ;
  55. event_dispatch ( ) ;
  56. evhttp_free ( httpd ) ;
  57. 0を 返し ます 。
  58. }

コードに関する説明:
行58〜63は、WEBサーバーを初期化します。 基礎として、libeventに基づくWEBサーバーを使用します。 それは素晴らしい性能を持っています。 2.3GHzラップトップでは、2k qpsのパフォーマンスが得られます。 すべてのURLが処理されます。
pg。20-22-バッファの初期化
23ページでは、REQUEST_IRIを取得してキーに使用しています。 最適化のための多くの提案があります。
26ページでPOSTを確認します。 間違いなく、HEADを確認できます(evhttpがサポートしていない他のメソッド)。 人生が複雑になるまで。
p。28-30は、データが保存される変数を形成します。 ガベージはバッファに蓄積されるため、Content-Lengthヘッダーに示されているバイト数を書き込みます
30-35ページそのようなキーが存在しない場合、新しいキューを開始し、データ要素をキューに挿入します
それ以外の場合は、データ項目をキューに挿入するだけです
ページ38-GETメソッドを満たす
39ページでキーが存在するかどうかを確認します
40ページ-いいえ-空の結果に関するメッセージを表示する
42ページ-キーに関するデータを取得
43ページ-キューが空かどうかを確認
p。44-47-いいえ、キューからメッセージを選択して表示します。キューは1つのメッセージで削減されます
逃げる必要性に少し火をつけることができます。 はい、間違いなく追加します。
49ページ、はい、キューは空です。これについてお知らせします。
53ページでリクエストを確定し、レスポンスコード200 OKを送信します

モデルはシングルスレッドであるため、レコードをロックする必要はありません。 この問題はまだ解決されますが。

Ab結果
Concurrency Level: 3
Time taken for tests: 0.415 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 83000 bytes
HTML transferred: 19000 bytes
Requests per second: 2409.31 [#/sec] (mean)
Time per request: 1.245 [ms] (mean)
Time per request: 0.415 [ms] (mean, across all concurrent requests)
Transfer rate: 195.29 [Kbytes/sec] received

10,000メッセージで消費されるメモリ量は600K強

実際、nginxをインストールし、セキュリティを担当する予定です(ngx_http_accesskey_module)
設定の一部:
location /test {
proxy_pass 127.0.0.1:8080;
## ngx_http_accesskey_module
}

しかし、nginxを使用すると、パフォーマンスは800 rpsに達します。

多くのアイデアがあります-どのように、どこに進むべきか。 たとえば、アクティビティステータスの表示、スパム対策。
他のアイデアは大歓迎です。 私は仕事をせずに座っていますが、それを試すことができるプロジェクトはありません。 私の推定によると、同時に、1〜1.5分(130〜200 rps)の頻度でリクエストを行うと、約1万人のクライアントが落ち着いてプルします。

Source: https://habr.com/ru/post/J91927/


All Articles