狠狠撸

狠狠撸Share a Scribd company logo
自作node.jsフレームワークと
nginxを使ってラジオサイトを作ってみた
自作苍辞诲别.箩蝉フレームワークと苍驳颈苍虫を使ってラジオサイトを作ってみた
MikeTokyo???
しかもなんか見たことあるアイコンじゃね?
これだ!!!!!!
僕と、〇〇でラジオやります!
もうブース押さえてます!(10/20(土)22:00~)
ただ、ラジオやるなら、奥贰叠サイトが必要ですよね!
僕はphperなので、apache+mod-phpで
WEBサイト作(ry
いや、ちょっと待て!
今の時代にapache+mod-phpは古くね?
サイトの規模も小さいし、
勉強がてらnode.js+nginxで作ろう!
What is node.js?
Javasctiptでサーバーサイドプログラミングを行うことが出来る環境の総称です。

特徴
?V8 javascript(google chromeのjavascript実行環境)を利用しているため、動作が軽快

?イベントドリブンなプログラミングスタイル

?シングルスレッドのイベントループモデル
(apacheのようにリクエスト毎にスレッドを立てるのではなく、シングルスレッドで処理をキュー
にためて実行する。このため、メモリの使用量がapacheなどのサーバーに比べて少ない)

?ノンブロッキングI/O
(i/oの完了を待たずに次の処理が実行される。ブラウザで動作するjavascriptのように、パラレル
で処理が実行される。
普通のイベントループモデルだと重い処理がブロッキングを行って次の処理が遅延するが、
ノンブロッキングI/Oのおかげでnode.jsではブロッキングが発生しない。)

?以上のことから、大量アクセスのあるリアルタイムWEBを始めとしたネットワークプログラミ
ングが得意
What is nginx?
軽量かつ、WEBサーバー、リバースプロキシー、IMAP/POP3、LBとして使用
可能な高性能WBサーバーのこと

特徴
?node.js同様、非同期のイベント駆動型のリクエスト処理を用いているため負荷に強い

?apacheほど設定も難解ではないので、扱いやすい

?Fast-CGIをデフォルト備えているので、phperである僕らはphp-FPMを容易に扱うことがで
きる

?とりあえずめちゃめちゃ流行っている。Response HeaderのServer項目がnginxというのをマ
ジでよく見かける
苍辞诲别.箩蝉で奥贰叠アプリケーションとなると....

                           cofeescript、jadeのほか、Redis、
                           MongoDBをすぐに扱えるnode.js?
                            史上最強MVCフレームワーク

http://towerjs.org/




  rubyのsinatraライクな軽量フレーム
                ワーク

                                  http://expressjs.com/
ただ、node.jsでのプログラミングのイロハがまだ不確
かなままFWを使うのも嫌なので、自分でFW作って学
      びながらモノ作りしたい!
本题
Architecter
                                  nginx(静的ファイル)
                                   port 80
クライアント

                    request



                              response

                                               proxy

 簡単な仕様                              node.js(動的処理)
?RonRライクなMVCパターンを採用                 port3000
?ORMなし
?scafoldなし                    response

? Templateエンジンにejsを採用
?jsUnitを採用(予定)
Architecter続き
①リクエスト            ②リクエストハンドラ



                ③コントローラーのインスタンス生成



                   ④アクション実行



                  ⑤レスポンスデータの生成
                   (html、jpg、404等)



                     ⑥レスポンス
MVCFrameWorkに最低限必要なもの

?RequestHandler
?Con?guration
?Router
?Model
?View
?Controller
ディレクトリ構成
miketokyo
   |-app
            |-con?g
                  |-con?g.js
                  |-router.js
                  |-bootstrap.js
            |-controllers
            |-models
            |views
                   |-layouts
  |-system
         |-server
                |-view.js
                |-router.js
                |-controller.js
                |-share.js
                |-response.js
                |-utility.js
  |-server.js
  |-package.json
  |-.gitignore
リクエストハンドラの実装
   server.js(紙面の都合上章や略しております。)
var	
 ?http	
 ?=	
 ?require("http");
var	
 ?url	
 ?=	
 ?require("url");

(function	
 ?start()	
 ?{
	
 ?	
 ?function	
 ?onRequest(request,	
 ?response)	
 ?{
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?//アプリケーション初期化
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?router	
 ?=	
 ?require("./app/config/router.js");
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?pathname	
 ?=	
 ?url.parse(request.url).pathname;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?bootstrap	
 ?=	
 ?require("./app/config/bootstrap");
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?bootstrap.init(request,	
 ?response);
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?env	
 ?=	
 ?require("./app/config/env/"	
 ?+	
 ?bootstrap.env);
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?config	
 ?=	
 ?require("./app/config/config");
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?share	
 ?=	
 ?require("./system/server/share");
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?share.config	
 ?=	
 ?config;

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?__CONTROLLER__	
 ?=	
 ?router.controller;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?__ACTION__	
 ?=	
 ?router.action;

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?if(pathname	
 ?!=	
 ?"/"){
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?__CONTROLLER__	
 ?=	
 ?pathname.split("/")[1];
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?__ACTION__	
 ?=	
 ?(typeof	
 ?pathname.split("/")[2]	
 ?==	
 ?"string")	
 ??	
 ?pathname.split("/")[2]	
 ?:	
 ?"index";
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?}

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?className	
 ?=	
 ?__CONTROLLER__.charAt(0)	
 ?+	
 ?__CONTROLLER__.substring(1)	
 ?+	
 ?"Controller";	
 ?//クラス名を動的に形成する
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?_controller	
 ?=	
 ?require("./app/controllers/"	
 ?+	
 ?className);
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?controllerInstance	
 ?=	
 ?new	
 ?_controller();

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?controllerInstance.request	
 ?=	
 ?request;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?controllerInstance.response	
 ?=	
 ?response;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?controllerInstance.headers	
 ?=	
 ?request.headers;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?controllerInstance.headers.userAgent	
 ?=	
 ?require('user-?‐agent').parse(request.headers['user-?‐agent']).full;

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?controllerInstance.init();
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?assignVars	
 ?=	
 ?eval("controllerInstance."	
 ?+	
 ?__ACTION__	
 ?+	
 ?"Action()");

	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?view	
 ?=	
 ?require("./system/server/view");
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?view.render(controllerInstance.layoutPath	
 ?,	
 ?controllerInstance.renderPath	
 ?,	
 ?assignVars,	
 ?response);
	
 ?	
 ?}
	
 ?	
 ?http.createServer(onRequest).listen(3000);
})();
環境判別等を行うbootstrapを作成する
app/con?g/bootstrap.js
var	
 ?bootstrap	
 ?=	
 ?{
	
 ? env	
 ?:	
 ?"localhost"
	
 ? ,init	
 ?:	
 ?function(req,	
 ?res){	
 ?	
 ?	
 ?
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?//ホストから環境を判別する
                          var	
 ?this.env	
 ?=	
 ?(function(host){
                          	
 ?	
 ?	
 ?	
 ?var	
 ?hostSplits	
 ?=	
 ?host.split(".");
                          	
 ?	
 ?	
 ?	
 ?if(hostSplits[0].match(/dev|stg/)){
                                          return	
 ?hostSplits[0];
                                   }else{
                                          return	
 ?"production";
                                   })(req.headers.host);
                          }
	
 ? }
}

module.exports	
 ?=	
 ?bootstrap;
設定ファイルを作成する
app/con?g/con?g.js

var	
 ?config	
 ?=	
 ?{
	
 ? applicationName	
 ?:	
 ?"MikeTokyo"
}

module.exports	
 ?=	
 ?config;
BaseとなるControllerクラスの作成
system/controller.js
function	
 ?controller(){}
controller.prototype	
 ?=	
 ?{
	
 ? request	
 ?:	
 ?null,
	
 ? response	
 ?:	
 ?null,
	
 ? headers	
 ?:	
 ?null,
	
 ? renderPath	
 ?:	
 ?null,
	
 ? layoutPath	
 ?:	
 ?"base",
	
 ?
	
 ?	
 ?	
 ?init	
 ?:	
 ?function(){},
	
 ?	
 ?	
 ?//void	
 ?redirect
	
 ? redirect	
 ?:	
 ?function(url){
	
 ? 	
 ? this.response.writeHead(302,	
 ?{
	
 ? 	
 ?	
 ?	
 ? 	
 ? 'Location':	
 ?url
	
 ? 	
 ? });
	
 ? 	
 ? this.response.end();	
 ?
	
 ? },
	
 ?	
 ?	
 ?//void	
 ?render
	
 ? render	
 ?:	
 ?function(path){
	
 ? 	
 ? this.renderPath	
 ?=	
 ?path;
	
 ? },
	
 ?	
 ?	
 ?//void	
 ?layout
	
 ? layout	
 ?:	
 ?function(path){
	
 ? 	
 ? this.layoutPath	
 ?=	
 ?path;
	
 ? }
}

module.exports	
 ?=	
 ?controller;
Viewクラスの作成
 system/view.js
var	
 ?fs	
 ?=	
 ?require("fs");
var	
 ?ejs	
 ?=	
 ?require("ejs");
var	
 ?share	
 ?=	
 ?require("./share");

var	
 ?view	
 ?=	
 ?{
	
 ?            templateVars	
 ?:	
 ?{}
	
 ?            ,viewExt	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?:	
 ?".ejs"
	
 ?            ,layoutPath	
 ?	
 ?	
 ?:	
 ?null
	
 ?            ,templatePath	
 ?:	
 ?null
	
 ?	
 ?	
 ?	
 ?//htmlをクライアントに返す
	
 ?             render	
 ?:	
 ?function(layoutPath,	
 ?templatePath,	
 ?templateVars,	
 ?response){
                                   var	
 ?templatePath	
 ?	
 ?=	
 ?share.APP_PATH	
 ?+	
 ?"views/layout/"	
 ?+	
 ?templatePath	
 ?+	
 ?this.viewExt;
	
 ?             	
 ?              fs.readFile(templatePath,"utf8",	
 ?function(err,data){
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?//renderで指定されたtemplateをhtmlに置換して取得する
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?actionData	
 ?=	
 ?ejs.render(data,vars);
	
 ?	
 ?	
 ?	
 ?	
 ? 	
 ? 	
 ?
                 	
 ?	
 ?	
 ?                       fs.readFile((function(path){
	
 ?             	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?templatePath	
 ?=	
 ?share.APP_PATH	
 ?+	
 ?"views/";
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?            	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?if(path	
 ?!==	
 ?undefined	
 ?&&	
 ?path	
 ?!==	
 ?null){
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?        	
 ?return	
 ?templatePath	
 ?+=	
 ?path	
 ?+	
 ?this.viewExt;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ? }else{
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?           	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?return	
 ?templatePath	
 ?+=	
 ?share.__ACTION__	
 ?+	
 ?this.viewExt;
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?}
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?})(layoutPath),"utf8",	
 ?function(err,data2){
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?//layoutで指定されたtemplateに、actionDataを埋め込みHTMLとしてクライアントに返す
	
 ?             	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?var	
 ?finalOutput	
 ?=	
 ?ejs.render(data2,{content	
 ?:	
 ?actionData,	
 ?
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?applicationName	
 ?:	
 ?share.config.applicationName});
	
 ?             	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?response.writeHead(200,	
 ?{	
 ?'Content-?‐Type':	
 ?'text/html;	
 ?charset=utf-?‐8'	
 ?});
	
 ?             	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?response.end(finalOutput);
	
 ?             	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?});	
 ?

	
 ? 	
 ?	
 ?	
 ?	
 ?});
	
 ? },
}
module.exports	
 ?=	
 ?view;
routerクラスの作成
system/server/router.js


var	
 ?url	
 ?=	
 ?require("url");

var	
 ?router	
 ?=	
 ?{
	
 ?	
 ?req:null
	
 ?,controller:	
 ?"app"
	
 ?,action	
 ?:	
 ?"index"
	
 ?,connect	
 ?:	
 ?function(pattern,	
 ?routeUrl){
	
 ?	
 ?	
 ?	
 ?var	
 ?patt	
 ?=	
 ?pattern.replace('/',	
 ?"/");
	
 ?	
 ?	
 ?	
 ?var	
 ?pathname	
 ?=	
 ?url.parse(this.req.url).pathname;
	
 ?	
 ?	
 ?	
 ?if(pathname.match(patt)){
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?this.controller	
 ?=	
 ?routeUrl.split("/")[0];
	
 ?	
 ?	
 ?	
 ?	
 ?	
 ?this.action	
 ?=	
 ?routeUrl.split("/")[1];
	
 ?	
 ?	
 ?	
 ?}
	
 ?	
 ?}
}

module.exports	
 ?=	
 ?router;
Frame Workの基幹が出来たので、実際に使ってみるお!
appControllerの作成
app/controllers/appController.js
//ライブラリインポート
var	
 ?share	
 ?=	
 ?require("../../system/server/share");
var	
 ?utility	
 ?=	
 ?require("../../system/server/utility");

function	
 ?appController(){}
//継承
var	
 ?appController	
 ?=	
 ?	
 ?utility.extend(appController,	
 ?require("../../system/
server/controller"));

appController.prototype.init	
 ?=	
 ?function(){
	
 ? this.layout("base");
}

appController.prototype.indexAction	
 ?=	
 ?function(){
	
 ? return	
 ?{applicationName:"MikeTokyo"	
 ?};
}

appController.prototype.testAction	
 ?=	
 ?function(){
	
 ?	
 ?	
 ?	
 ?this.render("test");
}

module.exports	
 ?=	
 ?appController;
各種viewの作成
app/views/layout/base.ejs
<!doctype	
 ?html>
<html>
	
 ? <head>
	
 ? <meta	
 ?charset="UTF-?‐8">
	
 ? <title><%=applicationName	
 ?%></title>
	
 ? </head>
	
 ? <body>
	
 ? 	
 ? <%-?‐content	
 ?%>
	
 ? </body>
</html>


app/views/index.ejs

<h1><%=applicationName	
 ?%>:Index</h1>
<img	
 ?src=/slideshow/nodejsnginx/15402565/"/img/common/logo.jpg">
<a	
 ?href="/app/test"	
 ?>testへ</a>
routerの設定

app/con?g/router.js

var	
 ?router	
 ?=	
 ?require("../../system/server/router");
router.connect("/",	
 ?"app/index");

module.exports	
 ?=	
 ?router;
nginxの設定
/etc/nginxd/nginx.conf
server {
           listen   80;
           server_name miketokyo.com;
           root /var/www/nginx-default;
           access_log /var/log/nginx/localhost.access.log;

           #指定した拡張子のファイルはnginxでさばく
           location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                   root /var/www/miketokyo/app/webroot/
                   expires 15d;
           }

           #プロキシの設定
           location / {
                   proxy_pass       http://127.0.0.1:3000;
                   proxy_set_header X-Real-IP $remote_addr;
                   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                   proxy_set_header Host $http_host;
                   proxy_set_header X-Nginx-Proxy true;
                   proxy_redirect off;
           }
}
実际にアクセスしてみるお!
http://miketokyo.com/
無理やりですが、
とても簡単にWEBアプリが作れてしましますね!!
node.js開発時のティップス
?クラスの名前は、「camelCase」のようなキャメルケースとなる

?自分でhttpサーバーを作るということを意識する。html以外にも、jpgやcssなどもcontent-typeを指定
してクライアントに返す必要がある。もちろん、404や500も。

?シングルスレッドで動作するため、例えば、「this.propety++」として初期化しないで放置すると、
次のリクエストでは、前のリクエストでインクリメントされた値をさらにインクリメントして処理さ
れるため、phpやjavaのノリで「this.propety++」や「this.propety += hoge」を書くと大変!!

?例外が発生するとnode.jsのプロセス自体が落ちてアボーんする(適切に全ての例外をcatchして500
などを返すか、foreverというnode.jsをデーモン化するモジュールを導入することで対応可能)

?複雑な処理を書くとコールバックの嵐となるため、Defferdとかを使ったほうがよさそう。


?JQueryもつかえるよ!

?CofeeScriptでも書けるよ!
まとめ
?node.jsはsocket.ioだけじゃなく、WEBアプリだって作れる!
 しかも簡単に!

?Smartyやjspと同等レベルで扱えるtemplateエンジンがある!
(その他hamlを扱えるjade等も存在する)

?各クラスやライブラリをモジュール化することで、OOPプロ
グラミングができる

?同じくイベントループモデルのWEBサーバーであるnginxと相
性がいい

?今回は登場していませんが、mysqlやmongodbなどのDBドライ
バ、RedisやmemcacheなどのKVS向けドライバも用意されている
ので、phpやjavaと同等のWEB開発ができる

?依存関係を最小限にクラス設計をするのに時間がかかりそ
う。。
今回紹介したmiketokyoフレームワークは、
github上で開発されています笑

みんなコミッターになってね?
https://github.com/noppoMan/mike_tokyo
ご静聴ありがとうございました

More Related Content

自作苍辞诲别.箩蝉フレームワークと苍驳颈苍虫を使ってラジオサイトを作ってみた