🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

Q&A

解決済

2回答

4028閲覧

Ruby: ロングポーリングについて

nanase21

総合スコア144

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

1グッド

3クリップ

投稿2019/11/14 01:06

編集2019/11/14 05:38

ロングポーリングについて知っている知識
リクエストがサーバに送信する。(ajax or fetch)
サーバはメッセージがあるまで接続を閉じないで待つ(ruby: ここの部分の実装が分からない)
メッセージが現れたら、サーバはそのデータでリクエストに応答する(ruby: renderで返す感じ?(よく分からない))
ブラウザはすぐに新しいリクエストを作る

現状のコード

Post時の該当ソース

slim

1 //divの要素です(擬似インプット) 2 #input.area contenteditable="true" 3 input.btn type="submit" value="post" onclick="send()"

js

1 function send() { 2 var text = document.getElementById("input").textContent; 3 $.ajax({ 4 url: `/dm`, 5 type: "POST", 6 data: {message: {dm: text}}, 7 dataType: "json", 8 }).done(function(data){ 9 console.log(data); 10 console.log(data.dm); 11 }).fail(function(data){ 12 console.log("fail"); 13 }); 14 }

ruby

1class DmController < ApplicationController 2 3 def create 4 dm = Dm.new(params) 5 respond_to do |format| 6 if dm.save 7 format.js 8 format.json { render json: dm } 9 else 10 redirect_to root_path 11 end 12 end 13 end 14 15 private 16 17 def params 18 params.require(:message).permit(:dm).merge(user_info: current_user) 19 end 20end

実現したいこと

リアルタイムに近い通信サービスを作りたく、その手段としてロングポーリングを使って実現したいと考えています。(ショートポーリングの実装はできます)
リアルタイム通信処理をやりたいというと、websokect通信もあると思いますが今回はあくまでもロングポーリングを使った方法を知りたいと考えています。

困っていること

ショートポーリングの実装は、ajaxを定期的に呼び出すことで簡単に実装することができたのですが、ロングポーリングにいては、何をどうしたらよいかが分からない状態でいます。
特にバックエンド側の、メッセージを受け取るまで閉じずに待つという処理が分からないです。(概念は理解できているのですがコードの書き方が分からないです。)
そのため、バックエンド側のコードを交えてご教示いただけると幸いです。

試したこと
動かないですが、私の中でイメージしているロングポーリングの処理です。

ruby

1class DmController < ApplicationController 2 3 def ajax 4 # {message {cnt:9}} パラメーターです。cntはviewに表示されているメッセージの数です。 5 # 例)DBにはメッセージが10こあるとします。 6 dm = Dm.all 7 num = dm.count - params[:message][:cnt].to_i 8 # => 1 9 dm.last(num) 10 respond_to do |format| 11 if dm.present? 12 format.js 13 format.json { render json: dm } 14 else 15 wait 16 end 17 end 18 end 19 20 def wait dm = nil 21 sleep(60) if dm.blank? # sleepをkillしてjsonを返す 22 respond_to do |format| 23 format.js 24 format.json { render json: dm } 25 end 26 end 27 28 def create 29 dm = Dm.new(params) 30 respond_to do |format| 31 if dm.save 32 wait(dm) 33 else 34 redirect_to root_path 35 end 36 end 37 end 38 39 private 40 41 def params 42 params.require(:message).permit(:dm).merge(user_info: current_user) 43 end 44end

更新をバックエンドで待ち続ける時

ruby

1#dmは取得したレコード 2loop do 3break if dm.present? 4sleep 0.1 5end
DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

ActionCable(WebSocket)を使う事はできますが、ロングポーリングについては対応しているという話を聞きません。

Railsでは、イベントがあるまでsleepするしかないように思います。

もし実装する必要に駆られたならば、Railsとは別のロングポーリング対応のサーバーを開発・使用すると思います。


追記

試してはいませんが、こんな感じですかね
(複数人同時に接続するとリソース食い過ぎで酷いことになると思います。)

ruby

1def wait_message(cnt) 2 dm = Dm.where("id > ?", cnt) 3 i = 0 4 sleep(1) until dm.exists? || (i+=1) > 60 5 return dm 6end 7 8def ajax 9 dm = wait_message(params[:message][:cnt].to_i) 10 respond_to do |format| 11 format.js 12 format.json{ render json: dm } 13 end 14end

投稿2019/11/14 01:43

編集2019/11/14 12:10
asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2019/11/14 01:56 編集

actioncableは試しました。ローカルで遊ぶ分にはいいのですが、サービスに応用すると一気にハードルが高くなるので極力XHRで対応で実装したいと考えています。(今回の実装も遊びですが...) 私も、sleepを使用するつもりではいます。 >もし実装する必要に駆られたならば、Railsとは別のロングポーリング対応のサーバーを開発・使用すると思います。 そんなに大規模なサービスを作ろうと考えていないのでポーリング対応のサーバーを開発・使用などは考えてないです。(作るにしてもポートフォリオ的になノリでしか作る予定がないので...)
nanase21

2019/11/14 02:17

すごく安易な発想ですが、データの中身が更新されるまで無限ループするのはまずいでしょうか?(サーバーの負荷的な問題)
asm

2019/11/14 03:05

sleep(0.1) until 更新されているか? || タイムアウト判定 みたいに待つのが普通じゃないですかね
nanase21

2019/11/14 05:47

> sleep(0.1) until 更新されているか? || タイムアウト判定 私の理解不足で申し訳ございません。 > 更新されているか? については、object.present?見たいな感じで大丈夫かなと思うのですが > タイムアウト判定 については、どのように書けばいいのでしょうか? それと、sleepを使った場合は、常にDBにアクセスされているのでしょうか? もしそうではない場合は、loopなどの処理を入れないとsleep期間が終わったら空のオブジェクトを返すことになるのでしょうか? ご迷惑をお掛けしますが、もう少しだけご教示いただけると幸いです。 それと、質問欄に試したことを追記したので見て頂きたいです。
nanase21

2019/11/15 00:47

ご教示いただきありがとうございました。 一点だけお聞きしたいことがあります。 > dm = Dm.where("id > ?", cnt) についてなんですが、whereの中の"id > ?"は何をしているのでしょうか? プログラミング初学者のためご教示いただけると幸いです。(whereの中身が何をやっているか分からない...) 私のDMを取得するイメージとしては、ajaxでviewに表示されているレコード数とroom_idをajaxで送ってきて get_dm = DM.where(room_id: params[room_id]).count res = get_dm - params[:cnt] res.last(num) のような形で最新のdmを取得しようとしているのですが、@asmさんの.where("id > ?", cnt)で対応できるのでしょうか?
asm

2019/11/15 01:49 編集

https://railsguides.jp/active_record_querying.html#配列で表された条件 ここでは、レコードのidがデフォルト設定では単調増加になる事を利用しております。 新しいDMは常に古い(=既に受信済みの)DMよりも大きいidになるので、 js側で受信済みの中で最新のid保持し送信することで get_dm = DM.where("room_id = ? AND id > ?", params[:room_id], params[:old_id]) で取れる筈です。 また、別のアイデアとしてはcreated_atを利用して最終更新時刻から新しいものを取る方法もあります。 このへんの話は https://qiita.com/jnchito/items/8bbbf3e803e56b97a498 がちょっと詳しいです
nanase21

2019/11/15 02:08

ご教示いただきありがとうございます。 なるほど。 私の考えていた方法では、メッセージの方法ではメッセージを一回全て取得する必要がありましたが 、@asmさんの方では、id(例) params[id:5])の場合、6以上のidのメッセージがあるかどうかだけを判定している感じなんですね。(思いつきませんでした。created_atも良さそうですね。) とても勉強になりました。????????‍♂️ このようなソートをする場合は、ソート対象のカラムに対してindexを貼ったほうがいいのでしょうか?(メッセージ量が多くなるとソートに時間がかかるため)
asm

2019/11/15 02:20

貼った方が無難です。 ただ、5人程度で違いが出るほどログ埋められるかというと疑問ですので 気分以上の効果はないと思いますが idだとそのへん貼ってあるので楽ってのもあります。
nanase21

2019/11/15 04:38

承知いたしました。 この度は、何度も質問をしてしまいご迷惑をおかけしてしまい申し訳ございませんでした。 @asmさんのおかげでとても勉強になりました。
nanase21

2019/11/15 04:41

最後に一点だけお聞きしたのですが、タイムアウトの時間を60秒にしたのには何か意図があるのでしょうか? それと、ロングポーリングを実装する場合、ブラウザのタイムアウトも考慮する必要があるのでしょうか?(セッション時間が長いとタイムアウトで途切れるため)
asm

2019/11/15 05:08

残念ながら適当ですし やったことないからよくわからん と言うのが正直なところです
nanase21

2019/11/16 07:05

承知いたしました。 ご教示いただきありがとうございました。
guest

0

サーバー側プログラムとしては、

メッセージが現れたら、

まで、待てばいいとおもいます。
具体的に何をトリガーに「メッセージが現れた」と判断できるのでしょうか?

ロングポーリングということだと、サーバー側でクライアント数だけのプロセスが動き続けることになりますが、リソース的に大丈夫なんでしょうか?
数人ならともかく、クライアントが多い場合、別のフレームワークを検討した方が良いと思います。

投稿2019/11/14 01:17

otn

総合スコア85886

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2019/11/14 01:35

クライアント数は5人程度を見込んでいます。(グルチャをイメージしています) あくまでも練習の一環で作っているだけなので、サービス開発に応用するといった想定はしていないです。 なので今現在はロングポーリングのバックエンド側の処理についてご教示いただきたく思っています。 >具体的に何をトリガーに「メッセージが現れた」と判断できるのでしょうか? これに関しては、私も分からないです。(Webで調べた程度の知識なので) なので、ajaxでサーバーにアクセスした時に、どう待てばよいかが分からないです。(sleepメソッドがrubyにはあるので処理を遅延させることはできそうですが待っている間、メッセージの受信をどうしたらよいかが分からずにいます。)
otn

2019/11/14 01:43

他のクライアントからのAjaxでのリクエストをトリガーにしたい ということですね。 一般論で言えば、 ・DBを経由する ・プロセス間通信をする方法をなんとか考える などを思いつきますが、 Rails関連のgemを使ったいい解法があるかもしれないので、 ・グループチャットをイメージしている ・クライアントからのAjaxでのメッセージ投稿をトリガーに他のクライアントにレスポンスを返したい というあたりを質問に追記して、他の方の回答をお待ちください。
nanase21

2019/11/14 02:17

DBのレコードが増えるまで、サーバー側で無限ループさせるのはサーバーに負荷がかかりますでしょうか?(10秒or20秒毎にレコードの数を取得して増えていなければ繰り返す感じです。)
otn

2019/11/14 15:35

5クライアントくらいならいいんじゃないでしょうか。 5クライアントが10秒ごとだと、平均で2秒に1回なので、「レコードが増えるまで」のチェックを軽いSQLで行えば問題ないと思います。 また、クライアント側の応答待ちがタイムアウトになる可能性がある場合には、データが無くてもタイムアウトするまでにレスポンスを返す必要があります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問