Node.jsとPHPの共存

4.426 görüntüleme
İlk okunmamış mesaja atla

永田真也

okunmadı,
1 Ağu 2013 10:27:371.08.2013
alıcı node...@googlegroups.com
はじめまして。
永田と申します。

Node.js+Socket.IOで通知機能を開発中です。
Node.jsでHTTPサーバを立ててPUSH通知の送受信ができることは確認しておりますが、 
既存のPHPサイトがあり、 
Node.jsでのPUSH通知機能を埋め込みたいのですが、
どういった方法で実現可能かご教示頂けば幸いです。 
iframeを使う、Socket.IOのポーリングプロトコルを使う
などを考えていますが、他に良案はありますでしょうか。
よろしくお願いいたします。

Takayuki Yamaguchi

okunmadı,
1 Ağu 2013 11:33:341.08.2013
alıcı node...@googlegroups.com

はじめまして。山口と申します。

私もPHPのWAFで殆どの機能を構築しPUSH通知のところだけNodeを利用する構成を以前模索したことがあります。

その時は一応動作するところまでいけましたので簡単に説明します。

手元にPCがないので概略になりますがご了承ください。

例えばPHPのアプリケーションがログイン機構を持っていて、
(例えばFacebookのように)ユーザー毎に通知を出し分けたいような場合です。

まずNodeが持つWebsocketのセッションとPHP側で持つHTTPのセッションを共有化します。

http://d.hatena.ne.jp/sugyan/touch/20110406/1302090071

上記のすぎゃーんさんの記事を参考にしつつ、phpのアプリ側のセッションIDが入ったcookieをnodeになげて、
nodeからphpのアプリに投げて、
node側で、websocketのセッションとPHP側のセッション情報、
例えばログインユーザーIDなどを紐付けます。

後は、PHPからNodeに対してHTTPリクエストないし、
RedisのPubSubを使うなどして、
任意のタイミングでPUSHしたい対象ユーザーの一覧と、PUSHしたいJSONデータなどを渡します。

Node側では、そのPHPからのリクエストを元に、必要なデータを必要なユーザーに対して、Socket.IO経由でデータを送る事でPUSH通知が実現できます。

セッション共有化の部分とその後のPHP,node間のデータやりとりのフォーマットだけ決めてしまえば、Node側は100行程度で完結し、
以後nodeのメンテは必要ないのがメリットです。

これだけだと分かりにくいかと思いますが、ご参考になれば幸いです。

@takyam

2013/08/01 23:27 "永田真也" <shin....@gmail.com>:
--
 
---
このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。
このグループから退会し、メールの受信を停止するには、nodejs_jp+...@googlegroups.com にメールを送信します。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
 
 

block.rxc...@gmail.com

okunmadı,
1 Ağu 2013 11:34:001.08.2013
alıcı node...@googlegroups.com
Jxck です。

Node.jsでのPUSH通知機能を埋め込みたいのですが、
> どういった方法で実現可能かご教示頂けば幸いです。 


PHP のサーバに接続している人と、 Socket.IO サーバに接続している人の関係はよくわからないので
「埋め込む」、がどういうイメージなのか探り探りですが、
この手の質問で一番多いのは、
「PHP のサーバに繋がってる人は、その画面の JS で Socket.IO コネクションを Socket.IO サーバと貼ってる」状態で
「PHP に POST で来たメッセージを、そのまま Socket.IO で他の人に PUSH したい」的なユースケースなので、
その前提で書きます。


1, PHP の Socket.IO-client で Socket.IO-node を叩く。

PHP 自体が Socket.IO サーバと Socket.IO の接続を貼ってしまう。
PHP で Post を受け取ったら、 Socket.IO サーバに Push して、 Socket.IO は (PHP サーバ以外の) クライアントに
Broadcast する。

で、PHP の Socket.IO クライアントってあるのかな?と思ったらあるみたい。http://elephant.io/
(ってか、 PHP で Socket.IO サーバできれば解決なのだろうか?)



2, PHP から Socket.IO に HTTP POST する。

PHP で Socket.IO はよくわからないから、 HTTP  がいいという場合。
Socket.IO サーバである Node.js のサーバに
HTTP で POST を受け取って、その Body なりを Socket.IO で broadcast する API を用意する。
(前回じゃない過去に TV 連携で事例発表した人がとってた構成)



3, MQ とか Redis とか挟む

個人的には挟めるなら、何か挟むのを進めたい気はします。 
POST のレスポンスがすぐに返せるし、疎結合に保てます。色々手はかかりますが。
流れは、メール通知が挟まるのとかと同じ感じです。

3-1, PHP で POST を受け取ったら MQ に積んで POST のレスポンスを返す
3-2, Socket.IO は MQ をポーリングなりして、メッセージがあったら取り出して Broadcast

Redis なら、Pubsub 使えるので、この用途は最近 Redis が多い気もします。


ぱっと浮かぶのはそんな感じでしょうか?


2013年8月1日 23:27 永田真也 <shin....@gmail.com>:

--

永田真也

okunmadı,
1 Ağu 2013 12:17:311.08.2013
alıcı node...@googlegroups.com, node...@googlegroups.com
Jxck様

ご回答ありがとうございます。
永田です。

現在、2の方法で途中まで進めているのですが、今後のことを考えると他の選択肢も考慮した方が良いと思いました。
redisはちょっと面倒な印象がありましたが、3の方法も試してみたいと思います。
不明な点は、また質問させて頂くかもしれませんがよろしくお願いいたします。

Sent from Mailbox for iPhone


このトピックの登録を解除するには、https://groups.google.com/d/topic/nodejs_jp/gU2347-33PQ/unsubscribe にアクセスします。このグループから退会し、グループのすべてのトピックの登録を解除するには、nodejs_jp+...@googlegroups.com にメールを送信します。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
 
 

永田真也

okunmadı,
1 Ağu 2013 12:23:001.08.2013
alıcı node...@googlegroups.com, node...@googlegroups.com
山口様

ご回答ありがとうございます。
永田です。

イメージとしては、下記の通りですので一度試してみます。

わかりやすく詳細な説明ありがとうございました。

Sent from Mailbox for iPhone


このトピックの登録を解除するには、https://groups.google.com/d/topic/nodejs_jp/gU2347-33PQ/unsubscribe にアクセスします。このグループから退会し、グループのすべてのトピックの登録を解除するには、nodejs_jp+...@googlegroups.com にメールを送信します。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
 
 

永田真也

okunmadı,
2 Ağu 2013 09:27:162.08.2013
alıcı node...@googlegroups.com
山口様

お世話になります。
永田です。

この場合のセッションの扱いなのですが、以下の認識で合っていますでしょうか。

1.セッション共有化のフェーズ
1-1.PHPサイトにログインしたユーザAのHTTPセッションと
  NodeのWebsocketセッションをユーザIDをキーとして共有化する。
1-2.PHPサイトにログインしたユーザBのHTTPセッションと
  NodeのWebsocketセッションを ユーザIDをキーとして共有化する。
2. PHP,Node間のデータやりとりフェーズ 
 ユーザAからユーザBに通知したい場合、
 ユーザAのアクションのタイミングでNodeにリクエストを投げる。
 このときに通知先のユーザBのIDを渡し、
 それをキーにしてPUSH配信する。

また、教えて頂いた参考サイトや過去投稿を見ても、
expressフレームワークを利用することが前提で記載されております。
基本的には利用する方が望ましいでしょうか。

申し訳ございませんが、ご回答頂ければ幸いです。
よろしくお願いいたします。


2013年8月2日金曜日 0時33分34秒 UTC+9 takyam:

Takayuki Yamaguchi

okunmadı,
2 Ağu 2013 12:39:382.08.2013
alıcı node...@googlegroups.com
永田さん

こんにちは。山口です。

CoffeeScriptですが、簡単なサンプルを記載します。

> 1.セッション共有化のフェーズ
■クライアント(ブラウザ)側のJS
socket = io.connect "http://example.com:3000"
socket.on 'connect', =>
socket.emit('cookie', { cookie: document.cookie })
#Websocketの接続できたらCookieを送る

■Node
http = require('http')
cookie = require('cookie')
querystring = require('querystring')

sockets = {}
io = require('socket.io').listen(3000)
io.sockets.on 'connection', (socket)->
#クライアントからcookie送られてきた時の処理
socket.on 'cookie', (data)->
#Cookieをquerystringに変換
user_cookie = cookie.parse(data.cookie).php_session_id
param = querystring.stringify({php_session_id: user_cookie})

#NodeJSからPHPに対してGETリクエストを飛ばしてPHPのUserIDを取得する
returnd_json = ''
req = http.request({
host: "php.example.com"
port: 80
path: '/backend/cookie' #PHP側のNode用認証API(Nodeにだけ公開しておく)
method: 'get'
headers: {'Cookie': param} #リクエストCookieにユーザーから受け取ったクッキーを渡す
}, (res)->
res.setEncoding('utf-8')
res.on('data', (chunk)-> returnd_json += chunk )
#sockets(適当に作ったオブジェクト)にPHP側のUserIDとsocketを紐付けて格納しておく
res.on('end', -> sockets[JSON.parse(returnd_json).user_id] = socket)
)
req.end()

■PHP側( /backend/cookie )
NodeからCookieが含まれたリクエストを送られるので、
PHP的にはNodeのリクエストもユーザーのリクエストも、
同一のセッションと見なされるとおもいます。
フレームワークによってはUser-Agentなども見てると思うので、
その辺はフレームワークにあわせてNode側からのHTTPリクエストの内容を改変してください。

なので、PHP側は単純にUserIDを含んだJSONを返します。

return json_encode(array('user_id' => $user_id));

-------------
超適当ですが、大体こんな感じで、「Websocket」のセッションに対して、
PHP側のUserIDなどをひもづける事が可能かと思います。

注意点としてはCookieがHTTP onlyだとJSから取得送信できないので、
その場合この手法は使えません。

> 2. PHP,Node間のデータやりとりフェーズ
RedisのPub/Subを使う場合の例です。
■Node
redis = require('redis')
pubsub = redis.createClient()
pubsub.subscribe('notice') #notice チャンネルをsubscribeしておきます
pubsub.on('message', (channel, data_string)-> #PHPから来たら配信します
data = JSON.parse(data_string)
for user_id in data.user_ids #PHPから渡された対象ユーザーにだけemitします
sockets[user_id].emmit('notice', {message: data.message})

■PHP
$redis->publish('notice', json_encode(array(
'user_ids' => $target_user_ids,
'message' => $message,
)));

■クライアントJS
socket.on 'notice', (data)->
alert(data.message)

以上です。動かしてないのでタイポってたらすみません。

> expressフレームワークを利用することが前提で記載されております。
> 基本的には利用する方が望ましいでしょうか。

どの程度の事をやりたいかによるのかと思いますが、
上記のサンプル程度のメッセージのやりとりだけであれば、
特に不要かと思います。

参考になれば幸いです。よろしくお願いします。

==================================
山口 貴之 (ヤマグチ タカユキ)
email: takayuki....@gmail.com
twitter: https://twitter.com/takyam
blog: http://new.takyam.com/
==================================


2013年8月2日 22:27 永田真也 <nagata...@lancers.co.jp>:

小林秀和

okunmadı,
2 Ağu 2013 21:13:122.08.2013
alıcı node...@googlegroups.com

KOBA789です。

クライアント側で document.cookie を使って Cookie を取得するのはセキュリティ上の理由で非推奨です。セッション情報の Cookie は原則 HttpOnly にすべきです。
Socket.IO のコネクションにセッション情報を渡す安全な方法は、Node のサーバーを PHP 側と同じドメインで動くようにして、Socket.IO のハンドシェイク HTTP リクエストのヘッダから Cookie を読むことです。

以上、重箱の隅をつつくようですみませんが、よりセキュアなアプリケーションを構築するためのお役に立てればと思います。

Takayuki Yamaguchi

okunmadı,
2 Ağu 2013 22:10:302.08.2013
alıcı node...@googlegroups.com
山口です。

>KOBA789さん
ご指摘ありがとうございます。
XSSなければ大きな問題ないかとも思いますが、
おっしゃるとおりあまり良い方法ではありませんね。

>永田さん
というわけですのでSame-Originにしたうえで、

io.sockets.on 'connection', (socket)->
user_cookie = cookie.parse(socket.handshake.headers.cookie).php_session_id
param = querystring.stringify({php_session_id: user_cookie})

このようにする事でCookieを取得できそうです。

よろしくお願いします。

==================================
山口 貴之 (ヤマグチ タカユキ)
email: takayuki....@gmail.com
twitter: https://twitter.com/takyam
blog: http://new.takyam.com/
==================================


2013年8月3日 10:13 小林秀和 <kobah...@gmail.com>:

小林秀和

okunmadı,
2 Ağu 2013 22:13:492.08.2013
alıcı node...@googlegroups.com

KOBA789です。

細かいとこですが、Same-Origin と Cookie の読み出しポリシーは異なるので、確かポートが違っても読み出せますね。

Takayuki Yamaguchi

okunmadı,
2 Ağu 2013 22:19:382.08.2013
alıcı node...@googlegroups.com

山口です。

>確かポートが違っても読み出せますね
お、そうなんですね。勘違いしてました。
ポート違いが許可されてるなら構築も簡単になりますね。
ありがとうございます!

2013/08/03 11:13 "小林秀和" <kobah...@gmail.com>:

永田真也

okunmadı,
2 Ağu 2013 22:39:062.08.2013
alıcı node...@googlegroups.com
山口様、KOBA789様

永田です。

セキュリティも考慮した丁寧なご説明ありがとうございます。
頂いたご意見を参考にし、セキュアなアプリを構築したいと思います。



2013年8月3日 11:19 Takayuki Yamaguchi <takayuki....@gmail.com>:
このトピックの登録を解除するには、https://groups.google.com/d/topic/nodejs_jp/gU2347-33PQ/unsubscribe にアクセスします。このグループから退会し、グループのすべてのトピックの登録を解除するには、nodejs_jp+...@googlegroups.com にメールを送信します。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。
 
 



--
--------------------------------------------------------------------
ランサーズ株式会社
システム開発部
永田 真也
Nagata Shinya

Mail: nagata...@lancers.co.jp

〒150‐0002 東京都渋谷区渋谷3-10-13 渋谷Rサンケイビル7F
URL: http://www.lancers.co.jp

ランサーズ [Lancers] - 仕事をフリーランスに発注できるクラウドソーシング
http://www.lancers.jp
--------------------------------------------------------------------

永田真也

okunmadı,
7 Ağu 2013 08:17:267.08.2013
alıcı node...@googlegroups.com
山口様、KOBA789様

永田です。

おかげさまで本件は解決できました。
ありがとうございます。

ただもう一点未解決の問題が発生しました。
下記の環境&SSLにした場合、
IE7-9に対しての通知がされません。

SSLなしの場合は、Flash Socketで動作するのですが、
SSLありの場合は、xhr-pollingまたはjsonp-pollingで動作しそうなのに
通知されません。
送信元からサーバまでメッセージは届いています。
知見がございましたらご教示頂けないでしょうか。
何卒よろしくお願いいたします。



2013年8月3日 11:39 永田真也 <nagata...@lancers.co.jp>:
Tümünü yanıtla
Yazarı yanıtla
Yönlendir
0 yeni ileti