调试比一开始写代码要难两倍。因此,如果你尽可能聪明地编写代码,从定义上来说,你没有足够的聪明去调试它。——布莱恩·w·克尼根T3】
在本章中,我们将介绍:
- 为不同的域部署添加 CORS
- 留言板用户界面
- 留言板 API
- 部署到 Heroku
- 相同的域部署服务器
- 部署到 Amazon Web 服务
现在,如果我们能够将我们的前端和后端应用放在一起,使它们能够协同工作,那就太好了。有几种方法可以做到:
- 前端和后端应用的不同域(Heroku 应用):通过使用 CORS 或 JSONP,确保没有跨域问题。稍后将详细介绍这种方法。
- 相同域部署:确保 Node.js 为前端应用处理静态资源和资产——不推荐用于严肃的生产应用。
到目前为止,这是生产环境的最佳实践。后端应用通常部署在http://app .或http://api .子域。
使不同的域部署工作的一种方法是用 JSONP 克服 AJAX 技术的同域限制:
var request = $.ajax({
url: url,
dataType: ’jsonp’,
data: {...},
jsonpCallback: ’fetchData,
type: ’GET’
})
另一种更好的方法是在输出之前向 Node.js 服务器应用添加 OPTIONS 方法和特殊的头,它们被称为跨源资源共享或 CORS ( https://en.wikipedia.org/wiki/Cross-origin_resource_sharing ):
...
response.writeHead(200,{
’Access-Control-Allow-Origin’: origin,
’Content-Type’:’text/plain’,
’Content-Length’:body.length
})
...
或者
...
res.writeHead(200, {
’Access-Control-Allow-Origin’, ’your-domain-name’,
...
})
...
对选项方法的需求在 HTTP 访问控制(https:// developer。mozilla。org/en-US/docs/Web/HTTP/Access _ control _ CORS。选项请求可以用以下方式处理:
...
if (request.method=="OPTIONS") {
response.writeHead("204", "No Content", {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods":
"GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "content-type, accept",
"Access-Control-Max-Age": 10, // Seconds.
"Content-Length": 0
})
response.end();
};
...
引导您完成实施并演示项目的补充视频: http://bit.ly/1QnsvEb 。
我们的前端应用使用 Parse.com 作为后端应用的替代品。现在,我们可以切换到我们自己的后端,替换端点以及一些其他无痛的更改。让我带你浏览一下。
在app.js文件的开头,取消本地运行的第一行的注释,或者用 Heroku 或 Windows Azure 后端应用公共 URL 替换 URL 值:
// var URL = ’``http://localhost:1337/T2】
var URL =’http://your-app-name.herokuapp.com/T2】
除了用原来的 Backbone.js 模型替换 Parse.com 模型和集合之外,app.js中的大部分代码和文件夹结构在06-board-backbone-parse-sdk项目中保持不变。因此,继续键入或复制 RequireJS 块来加载依赖项(在本例中是模板):
require([
’libs/text!header.html’,
’libs/text!home.html’,
’libs/text!footer.html’],
function (
headerTpl,
homeTpl,
footerTpl) {
ApplicationRouter、HeaderView、FooterView与06-board-backbone-parse-sdk项目中的相同,在此不再赘述。
我们需要使用Parse.Object和Parse.Collection将模型和集合更改为这个。这些是 Backbone.js 查找对应于特定集合和模型的 REST API URLs 的地方:
Message = Backbone.Model.extend({
url: URL + ’messages/create.json’
})
MessageBoard = Backbone.Collection.extend ({
model: Message,
url: URL + ’messages/list.json’
})
接下来是大多数逻辑驻留的HomeView。我对渲染过程做了一些改进,这很好地说明了在 Backbone 中可以对事件做些什么。首先,创建视图,并为 SEND 按钮定义元素选择器、模板(通过 RequireJS 和文本插件加载)和事件:
HomeView = Backbone.View.extend({
el: ’#content’,
template: homeTpl,
events: {
’click #send’: ’saveMessage’
},
现在,在视图的构造函数中,将homeView设置为this,这样我们就可以在闭包内部通过名字使用this(否则,this会在闭包内部变异):
initialize: function() {
var homeView = this
然后,我附加了一个事件监听器refresh来完成渲染。在此之前,我们有了all事件,这不是很好,因为它触发了每条消息的添加的重新呈现。你看,fetch会根据消息触发add很多次(10,100,1000,等等。)如果我们使用all事件监听器,add是all的一部分。而使用这个自定义事件refresh我们可以在适当的地方触发渲染(稍后您将看到它们)。
homeView.collection = new MessageBoard()
homeView.collection.bind(’refresh’, homeView.render, homeView)
homeView.collection.fetch({
fetch方法将执行获取 XHR 请求,它有success和error回调:
success: function(collection, response, options){
console.log(’Fetched ’, collection)
只有在所有消息都在集合中(并且来自服务器响应)后,下一行才会触发呈现:
collection.trigger(’refresh’)
},
error: function(){
console.error(’Error fetching messages’)
}
})
这个事件监听器将由 SEND 按钮和fetch触发。为了避免用message.save()保存现有记录,我们添加了对message.attributes._id的检查。换句话说,如果这是一个现有的消息,并且它来自服务器(fetch),那么它将有_id,我们停止执行流程。否则,我们保存消息并在成功时触发呈现:
homeView.collection.on(’add’, function(message) {
if (message.attributes._id) return false
message.save(null, {
success: function(message) {
homeView.collection.trigger(’refresh’)
console.log(’Saved ’, message)
},
error: function(message) {
console.log(’error’)
}
})
})
},
HomeView对象的其余部分与06-board-parse-sdk项目中的相同。在saveMessage中,我们获得用户名和消息文本的值,并用collection.add()将新的消息对象添加到集合中。这将调用事件监听器add,这是我们在initialize中实现的。
saveMessage: function(){
var newMessageForm = $(’#new-message’)
var username = newMessageForm.find(’[name="username"]’).val()
var message = newMessageForm.find(’[name="message"]’).val()
this.collection.add({
’username’: username,
’message’: message
})
},
最后,我们编写或复制render方法,该方法获取模板和集合,然后将结果 HTML 注入到具有 ID 内容的元素中(this.el):
render: function() {
console.log(’Home view rendered’)
$(this.el).html(_.template(this.template)(this.collection))
}
})
app = new ApplicationRouter()
Backbone.history.start()
})
这里是 13-board-ui/ app 的完整源代码。js 文件( https://github.com/azat-co/fullstack-javascript/blob/master/13-board-ui/app.js ):
var URL = ’``http://localhost:1337/T2】
// var URL =’http://your-app-name.herokuapp.com/T2】
require([
’libs/text!header.html’,
’libs/text!home.html’,
’libs/text!footer.html’],
function (
headerTpl,
homeTpl,
footerTpl) {
var ApplicationRouter = Backbone.Router.extend({
routes: {
’’: ’home’,
’*actions’: ’home’
},
initialize: function() {
this.headerView = new HeaderView()
this.headerView.render()
this.footerView = new FooterView()
this.footerView.render()
},
home: function() {
this.homeView = new HomeView()
this.homeView.render()
}
})
HeaderView = Backbone.View.extend({
el: ’#header’,
templateFileName: ’header.html’,
template: headerTpl,
initialize: function() {
},
render: function() {
$(this.el).html(_.template(this.template))
}
})
FooterView = Backbone.View.extend({
el: ’#footer’,
template: footerTpl,
render: function() {
this.$el.html(_.template(this.template))
}
})
Message = Backbone.Model.extend({
url: URL + ’messages/create.json’
})
MessageBoard = Backbone.Collection.extend ({
model: Message,
url: URL + ’messages/list.json’
})
HomeView = Backbone.View.extend({
el: ’#content’,
template: homeTpl,
events: {
’click #send’: ’saveMessage’
},
initialize: function() {
this.collection = new MessageBoard()
this.collection.bind(’all’, this.render, this)
this.collection.fetch()
this.collection.on(’add’, function(message) {
message.save(null, {
success: function(message) {
console.log(’saved ’ + message)
},
error: function(message) {
console.log(’error’)
}
})
console.log(’saved’ + message)
})
},
saveMessage: function(){
var newMessageForm=$(’#new-message’)
var username=newMessageForm.find(’[name="username"]’).val()
var message=newMessageForm.find(’[name="message"]’).val()
this.collection.add({
’username’: username,
’message’: message
})
},
render: function() {
console.log(this.collection)
$(this.el).html(_.template(this.template, this.collection))
}
})
app = new ApplicationRouter()
Backbone.history.start()
})
就是这里。供你参考,前端 app 源代码在 GitHub 文件夹中的 https://github.com/azat-co/fullstack-javascript/tree/master/13-board-u 。我不会在这里列出它,因为与 Parse SDK 项目相比,我们只有一些变化。拼图的下一块是后端。
引导您完成实施并演示项目的补充视频: http://bit.ly/1QnsvEb 。
后端 Node.js 应用源代码在 GitHub 文件夹的 https://github.com/azat-co/fullstack-javascript/tree/master/14-board-api 中,其结构如下:
/14-board-api
-web.js
-Procfile
-package.json
Procfile 用于 Heroku 部署,package.json用于项目元数据和 Hekoru 部署。
web.js文件与12-board-api-mongo非常相似,但是有 CORS 头文件和选项请求处理程序代码。该文件从依赖项的导入开始:
var http = require(’http’)
var util = require(’util’)
var querystring = require(’querystring’)
var client = require(’mongodb’).MongoClient
然后我们设置 MongoDB 连接字符串:
var uri = process.env.MONGOLAB_URI || ’mongodb://@127.0.0.1:27017/messages’
//MONGOLAB_URI=mongodb://user:pass@server.mongohq.com:port/db_name
我们使用字符串和client.connect方法连接到数据库。重要的是处理错误,如果有错误,用return完成执行流程:
client.connect(uri, function(error, db) {
if (error) return console.error(error)
在我们确定没有错误之后(否则执行流不会进入下一行),我们选择集合,在本例中是messages:
var collection = db.collection(’messages’)
服务器代码如下。我们创建服务器实例,并根据来自请求的信息设置 origin 变量。该值将在Access-Control-Allow-Origin中。这个想法是,响应将具有客户端 URL 的值:
var app = http.createServer(function (request, response) {
var origin = (request.headers.origin || ’*’)
检查 HTTP 方法谓词。如果是OPTIONS,我们必须为 CORS 实现它,我们开始向响应对象写入头:
if (request.method == ’OPTIONS’) {
response.writeHead(’204’, ’No Content’, {
’Access-Control-Allow-Origin’: origin,
下一个标题将说明支持哪些方法:
’Access-Control-Allow-Methods’:
’GET, POST, PUT, DELETE, OPTIONS’,
’Access-Control-Allow-Headers’: ’content-type, accept’,
’Access-Control-Max-Age’: 10, // In seconds
’Content-Length’: 0
})
response.end()
我们已经完成了OPTIONS,但是我们仍然需要实现GET和POST:
} else if (request.method === ’GET’ && request.url === ’/messages/list.json’) {
collection.find().toArray(function(error,results) {
if (error) return console.error(error)
var body = JSON.stringify(results)
我们需要给GET的响应添加几个头:
response.writeHead(200,{
’Access-Control-Allow-Origin’: origin,
’Content-Type’: ’text/plain’,
’Content-Length’: body.length
})
console.log(’LIST OF OBJECTS: ’)
console.dir(results)
response.end(body)
})
最后但同样重要的是,我们处理POST:
} else if (request.method === ’POST’ && request.url === ’/messages/create.json’) {
request.on(’data’, function(data) {
console.log(’RECEIVED DATA:’)
console.log(data.toString(’utf-8’))
我们需要解析data来获取对象,以便稍后我们可以将它保存到数据库中。下一行通常会导致错误,因为前端应用以一种格式发送数据,而服务器解析另一种格式。请确保在浏览器和服务器上使用相同的格式:
collection.insert(JSON.parse(data.toString(’utf-8’)),
{safe:true}, function(error, obj) {
if (error) return console.error(error)
console.log(’OBJECT IS SAVED: ’)
console.log(JSON.stringify(obj))
var body = JSON.stringify(obj)
我们再次添加标题。也许我们应该编写一个函数并调用它,而不是手动编写头文件。等等?Express.js 实际上会为我们做一些工作,但这是另一本书的主题:
response.writeHead(200,{
’Access-Control-Allow-Origin’: origin,
’Content-Type’: ’text/plain’,
’Content-Length’: body.length
})
response.end(body)
})
})
}
})
var port = process.env.PORT || 1337
app.listen(port)
})
下面是一个用 CORS 头文件实现的 Node.js 应用web.js的源代码:
var http = require(’http’)
var util = require(’util’)
var querystring = require(’querystring’)
var client = require(’mongodb’).MongoClient
var uri = process.env.MONGOLAB_URI || ’mongodb://@127.0.0.1:27017/messages’
//MONGOLAB_URI = mongodb://user:pass@server.mongohq.com:port/db_name
client.connect(uri, function(error, db) {
if (error) return console.error(error)
var collection = db.collection(’messages’)
var app = http.createServer(function (request, response) {
var origin = (request.headers.origin || ’*’)
if (request.method == ’OPTIONS’) {
response.writeHead(’204’, ’No Content’, {
’Access-Control-Allow-Origin’: origin,
’Access-Control-Allow-Methods’:
’GET, POST, PUT, DELETE, OPTIONS’,
’Access-Control-Allow-Headers’: ’content-type, accept’,
’Access-Control-Max-Age’: 10, // Seconds.
’Content-Length’: 0
})
response.end()
} else if (request.method === ’GET’ && request.url === ’/messages/list.json’) {
collection.find().toArray(function(error,results) {
if (error) return console.error(error)
var body = JSON.stringify(results)
response.writeHead(200,{
’Access-Control-Allow-Origin’: origin,
’Content-Type’: ’text/plain’,
’Content-Length’: body.length
})
console.log(’LIST OF OBJECTS: ’)
console.dir(results)
response.end(body)
})
} else if (request.method === ’POST’ && request.url === ’/messages/create.json’) {
request.on(’data’, function(data) {
console.log(’RECEIVED DATA:’)
console.log(data.toString(’utf-8’))
collection.insert(JSON.parse(data.toString(’utf-8’)),
{safe:true}, function(error, obj) {
if (error) return console.error(error)
console.log(’OBJECT IS SAVED: ’)
console.log(JSON.stringify(obj))
var body = JSON.stringify(obj)
response.writeHead(200,{
’Access-Control-Allow-Origin’: origin,
’Content-Type’: ’text/plain’,
’Content-Length’: body.length
})
response.end(body)
})
})
}
})
var port = process.env.PORT || 1337
app.listen(port)
})
引导您完成实施并演示项目的补充视频: http://bit.ly/1QnsvEb 。
为了您的方便,我们在 https://github.com/azat-co/fullstack-javascript/tree/master/13-board-ui 有前端 app,在 https://github.com/azat-co/fullstack-javascript/tree/master/14-board-api 有 CORS 的后端 app。到目前为止,您可能已经知道该做什么了,但是作为参考,下面是将这些示例部署到 Heroku 的步骤。
我们将从 API 开始。在14-board-api文件夹中,执行下面的代码($ heroku login是可选的):
$ git init
$ git add .
$ git commit -am "first commit"
$ heroku login
$ heroku create
$ heroku addons:create mongolab:sandbox
$ git push heroku master
观察终端消息。如果 API 部署成功,可以用 CURL 或者 Postman 测试一下。然后从 Heroku 复制 URL(例如 https://guarded-waters-1780.herokuapp.com ),粘贴到13-board-ui/app.js文件中,将值赋给 URL 变量。然后,在13-board-ui文件夹中,执行:
$ git init
$ git add .
$ git commit -am "first commit"
$ heroku create
$ git push heroku master
$ heroku open
就这样。到现在为止,你应该可以看到留言板在云中运行,在一个域上有 UI(浏览器应用),在另一个域上有 API。在高流量的应用中,API 将隐藏在负载平衡器的后面,因此您可以在一个 IP/URL 上拥有多个 API 服务器。这样,他们将处理更多的流量,系统将变得更有弹性。您可以一次取出、重启或部署一个 API,而无需停机。
引导您完成实施并演示项目的补充视频: http://bit.ly/1QnsvEb 。
对于严肃的生产应用,不建议使用相同的域部署,因为静态资产更适合使用 web 服务器,如 Nginx(而不是 Node.js I/O 引擎),并且分离 API 有助于降低测试的复杂性,提高健壮性,并加快故障排除/监控。然而,相同的应用/领域方法可以用于阶段化、测试、开发环境和/或小型应用。
这个想法是,API 也为浏览器应用提供静态文件,而不仅仅是处理对其路由的动态请求。因此,您可以将 14 板 api 代码复制到一个新文件夹 15-board-web 中。新服务器文件的开头是相同的;我们有 GET 和 POST 逻辑(这次不需要 CORS)。if/else链中的最后一个条件需要处理静态文件。我们可以这样做。
...
} else {
我们使用URL v 0。11.0 模块 from https://github.com/defunctzombie/node-url 解析 URL 中的路径名。路径名是域之后的所有内容;例如在 http://webapplog.com/es6 中,路径名是/es6。这将是我们的文件夹和文件名。
var uri = url.parse(request.url).pathname
最好进行一些日志记录,以了解我们的系统正在正常工作:
console.log(’Processing path: ’, uri)
下一行处理根 URI;也就是说,当你访问网站时,路径是空的或者是一条斜线。在这种情况下,我们来服务于index.html(如果存在的话):
if (uri == ’’ || uri == ’/’) uri = ’index.html’
path.join()方法将通过创建一个带有适当斜线的字符串来使代码跨平台:也就是说,\或/作为分隔符。您可以在日志中看到结果路径和文件名:
filename = path.join(__dirname, staticFolder, uri)
console.log(’Processing file: ’, filename)
我总是说不要在 Node.js 中使用同步函数,除非万不得已。情况就是这样。如果没有同步方法,我们的文件会出现竞争情况,这意味着有些文件会比其他文件加载得更快,从而导致冲突:
stats = fs.statSync(filename)
if (error) {
console.error(error)
显然,如果文件不存在,我们要发送 404 Not Found:
response.writeHead(404, {
’Content-Type’: ’text/plain’})
response.write(’404 Not Found\n’)
return response.end()
}
让我们确保请求的资源是文件。如果不是这个文件,您可以像我们对根一样添加index.html。我这里没有这个代码。我们的前端应用只需要包括文件,所以这个代码将服务于文件!
if(!stats.isFile()) {
response.writeHead(404, {
’Content-Type’: ’text/plain’})
response.write(’404 Not Found\n’)
return response.end()
} else {
最后,我们读取文件。出于上述原因,我们再次使用同步功能。
var file = fs.readFileSync(filename)
if (!file) {
response.writeHead(500,
{’Content-Type’: ’text/plain’})
response.write(err + ’\n’)
return response.end()
}
我知道道格拉斯·克洛克福特不喜欢switch,但是我们将在这里使用它来确定响应头的正确内容类型。如果您省略了Content-Type头,大多数浏览器都能理解内容类型,但是为什么不多做一点呢?
var extname = path.extname(filename)
var contentType = ’text/html’
switch (extname) {
case ’.js’:
contentType = ’text/javascript’
break
case ’.css’:
contentType = ’text/css’
break
case ’.json’:
contentType = ’application/json’
break
case ’.png’:
contentType = ’image/png’
break
case ’.jpg’:
case ’.jpeg’:
contentType = ’image/jpg’
break
case ’.wav’:
contentType = ’audio/wav’
break
}
response.writeHead(200, {
’Content-Type’: contentType,
我们随响应发回的另一个报头是Content-Length:
’Content-Length’: file.length
})
response.end(file)
}
}
...
因此这段代码进入服务器的请求处理程序,它位于数据库连接调用的内部。就像俄罗斯套娃一样。迷惑?参考 https://github.com/azat-co/fullstack-javascript/tree/master/15-board-web 的完整源代码即可。
另一种更优雅的方式是使用 Node.js 框架作为 Connect ( http://www.senchalabs.org/connect/static.html ),或者 Express(http://expressjs.com/en/index.html);因为 JS 和 CSS 资产有专门的static中间件。但是这些框架本身就应该有一本书。
现在,在您掌握了 Node.js、MongoDB、Backbone.js 和 Heroku 的基础知识之后,还有一个额外的步骤要做。查看称为 EC2(云计算的基础设施即服务类别)的云解决方案 Amazon Web Services。
云正在吞噬计算世界。有私有云和公有云。AWS 可能是公共云产品中最受欢迎的选择,属于 IaaS 类别。与 PaaS-like Heroku 相比,使用 AWS 等 IaaS 的优势如下:
- 它更具可配置性(任何服务、包或操作系统)。
- 更可控。没有任何约束或限制。
- 维护起来更便宜。PaaS 可能会很快为高性能资源花费一大笔钱。
在本教程中,我们将使用 64 位亚马逊 Linux AMI 和 CentOS ( http://aws.amazon.com/amazon-linux-ami/ )。
假设您已经启动并运行了 EC2 实例,使用 SSH 进入该实例,并使用yum安装所有系统更新:
$ sudo yum update
可以尝试用yum安装 Node。应该可以在 Enterprise Linux repository 的额外包中得到( https://fedoraproject.org/wiki/EPEL ):
$ sudo yum install nodejs npm --enablerepo=epel
这可能需要一段时间。在这个过程中用 y 回答。最后,您应该会看到类似这样的内容(您的结果可能会有所不同):
Installed: nodejs.i686 0:0.10.26-1.el6 npm.noarch 0:1.3.6-4.el6Dependency Installed:...Dependency Updated:...Complete!
您可能知道这一点,但为了以防万一,要检查安装,请键入以下内容:
$ node –V$ npm –v
如果 yum Node 安装失败,查看您是否有 EPEL(只需查看下面的命令是否显示epel):
$ yum repolist
如果没有epel,运行:
$ rpm -Uvhhttp://download-i2.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpmT3】
然后,尝试使用以下命令再次安装 Node.js 和 NPM:
$ sudo yum install nodejs npm --enablerepo=epel
或者,您可以从源代码编译 Node。为此,安装 C++编译器(同样使用yum):
$ sudo yum install gcc-c++ make
openSSL 也是如此:
$ sudo yum install openssl-devel
然后用 yum 安装 Git:
$ sudo yum install git
最后,直接从 GitHub 克隆 Node 库:
$ git clone git://github.com/joyent/node.git
并构建 Node.js:
$ cd node
$ git checkout v0.10.12
$ ./configure
$ make
$ sudo make install
注对于 Node.js 的不同版本,您可以使用$ git tag -l将它们全部列出,并检查您需要的版本。
要安装 npm,请运行:
$ git clonehttps://github.com/isaacs/npm.gitT3】
$ cd npm
$ sudo make install
有关使用 yum 的更多信息可在以下位置找到:
- 用百胜 (
https://www.centos.org/docs/5/html/yum/)管理软件 - 安装 Node。js via 包经理 (
https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager) - 关于保护 EC2 实例的提示 (
http://aws.amazon.com/articles/1233)
一旦有了 Git、npm 和 Node,就可以(手动)部署代码了。从存储库中取出代码。您可能需要提供凭证或将您的 SSH 密钥上传到 AWS。然后用 pm2 ( https://github.com/Unitech/pm2 )或者类似的进程管理器启动 Node 服务器(图 8-1 )。pm2很好,因为它有很多特性,不仅可以保持流程运行,还可以扩展流程;它甚至有负载平衡。
要安装 pm2:
$ npm install pm2 -g
要启动您的应用:
$ pm2 start app.js
要列出所有正在运行的进程:
$ pm2 list
图 8-1。
pm2 running multiple Node processes
这就是你需要做的全部。理想情况下,您希望自动化部署。此外,您可能希望添加一些d.init或upstart脚本来自动启动您的pm2或另一个流程管理器。
AWS 上其他操作系统的步骤类似。您将使用他们的包管理器来安装 Node、Git 和 npm,然后获取代码(Git 或 rsync)并用进程管理器启动它。你不需要过程管理器。你可以用node本身启动,但是最好用一些进程管理器。
现在,当 Node.js 应用运行时,执行$ netstat -apn | grep 80,远程机器应该显示这个过程。例如,对于侦听端口 80 的 Node 应用:
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1064/node
在 EC2 实例上,将防火墙配置为重定向连接(例如,port to Node.js 3000,但这对于我们的示例来说太高级了),或者禁用防火墙(对于我们的快速演示和开发目的来说没问题):
$ service iptables save$ service iptables stop$ chkconfig iptables off
在 AWS 控制台中,找到 EC2 实例并应用适当的规则来允许入站流量,例如,
Protocol: TCPPort Range: 80Source: 0.0.0.0/0
从您的本地机器,也就是您的开发计算机,您可以使用公共 IP 或公共 DNS(域名系统)域,它是从 AWS 控制台的实例描述下找到并复制的。例如,
$ curl XXX.XXX.XXX.XXX –v
值得一提的是,AWS 通过其 AWS Marketplace ( https://aws.amazon.com/marketplace )支持许多其他操作系统。虽然 AWS EC2 是一个非常受欢迎和实惠的选择,但也有其他替代选择: Joyent ( https://www.joyent.com/ ),Windows Azure(https://azure.microsoft.com/en-us/),Rackspace Open Cloud(http://www.rackspace.com/cloud),以及其他。
本章描述了不同的部署方法,留言板应用的最终版本,以及不同域和相同域两种部署方法。我们讨论了使用 Git 和 Heroku 命令行界面部署到 PaaS 的部署。我们还学习了在 AWS EC2 上安装和构建 Node.js 环境以及在 AWS 上使用 CentOS 运行 Node.js 应用的示例。
