在线游戏商店集成与部署及无服务器通知应用
一、在线游戏商店项目完善
在服务正常运行后,我们要完成之前的在线视频游戏商店项目,主要有两项改进:实现订单提交功能和添加订单历史查看视图。
-
配置设置文件
-
打开
gamestore目录下的settings.py文件,添加认证令牌和订单服务基础 URL:
-
打开
ORDER_SERVICE_AUTHTOKEN = '744cf4f8bd628e62f248444a478ce06681cb8089'
ORDER_SERVICE_BASEURL = 'http://127.0.0.1:8001'
-
修改模型文件
-
打开
gamestore/main目录下的models.py文件,添加namedtuple用于准备订单数据:
-
打开
from collections import namedtuple
- 在 `ShoppingCartManager` 类中添加 `empty` 方法,用于清空购物车:
def empty(self, cart):
cart_items = ShoppingCartItem.objects.filter(
cart__id=cart.id
)
for item in cart_items:
item.delete()
- 在文件末尾创建 `OrderItem` 命名元组:
OrderItem = namedtuple('OrderItem',
'name price_per_unit product_id quantity')
-
修改模板文件
-
找到
cart.html模板中的发送订单按钮,替换为包含表单和 CSRF 令牌的代码:
-
找到
<form action="/cart/send">
{% csrf_token %}
<button class='btn btn-primary'>
<i class="fa fa-check" aria-hidden="true"></i>
SEND ORDER
</button>
</form>
-
添加新 URL
-
打开主应用目录下的
urls.py文件,添加两个新 URL:
-
打开主应用目录下的
path(r'cart/send', views.send_cart),
path(r'my-orders/', views.my_orders),
-
修改视图文件
-
打开
views.py文件,添加新的导入:
-
打开
import json
import requests
from http import HTTPStatus
from django.core.serializers.json import DjangoJSONEncoder
from gamestore import settings
- 添加序列化订单数据的函数:
def _prepare_order_data(cart):
cart_items = ShoppingCartItem.objects.values_list(
'game__name',
'price_per_unit',
'game__id',
'quantity').filter(cart__id=cart.id)
order = cart_items.aggregate(
total_order=Sum(F('price_per_unit') * F('quantity'),
output_field=DecimalField(decimal_places=2))
)
order_items = [OrderItem(*x)._asdict() for x in cart_items]
order_customer = {
'customer_id': cart.user.id,
'email': cart.user.email,
'name': f'{cart.user.first_name} {cart.user.last_name}'
}
order_dict = {
'items': order_items,
'order_customer': order_customer,
'total': order['total_order']
}
return json.dumps(order_dict, cls=DjangoJSONEncoder)
- 添加 `send_cart` 视图函数:
@login_required
def send_cart(request):
cart = ShoppingCart.objects.get(user_id=request.user.id)
data = _prepare_order_data(cart)
headers = {
'Authorization': f'Token {settings.ORDER_SERVICE_AUTHTOKEN}',
'Content-type': 'application/json'
}
service_url = f'{settings.ORDER_SERVICE_BASEURL}/api/order/add/'
response = requests.post(
service_url,
headers=headers,
data=data)
if HTTPStatus(response.status_code) is HTTPStatus.CREATED:
request_data = json.loads(response.text)
ShoppingCart.objects.empty(cart)
messages.add_message(
request,
messages.INFO,
('We received your order!'
'ORDER ID: {}').format(request_data['order_id']))
else:
messages.add_message(
request,
messages.ERROR,
('Unfortunately, we could not receive your order.'
' Try again later.'))
return HttpResponseRedirect(reverse_lazy('user-cart'))
- 添加 `my_orders` 视图函数:
@login_required
def my_orders(request):
headers = {
'Authorization': f'Token {settings.ORDER_SERVICE_AUTHTOKEN}',
'Content-type': 'application/json'
}
get_order_endpoint = f'/api/customer/{request.user.id}/orders/get/'
service_url = f'{settings.ORDER_SERVICE_BASEURL}{get_order_endpoint}'
response = requests.get(
service_url,
headers=headers
)
if HTTPStatus(response.status_code) is HTTPStatus.OK:
request_data = json.loads(response.text)
context = {'orders': request_data}
else:
messages.add_message(
request,
messages.ERROR,
('Unfortunately, we could not retrieve your orders.'
' Try again later.'))
context = {'orders': []}
return render(request, 'main/my-orders.html', context)
-
创建新模板文件
-
在
main/templates/main/目录下创建my-orders.html文件,内容如下:
-
在
{% extends 'base.html' %}
{% block 'content' %}
<h3>Order history</h3>
{% for order in orders %}
<div class="order-container">
<div><strong>Order ID:</strong> {{order.id}}</div>
<div><strong>Create date:</strong> {{ order.created_at }}</div>
<div><strong>Status:</strong> <span class="label label-success">{{order.status}}</span><
<div class="table-container">
<table class="table table-striped">
<thead>
<tr>
<th>Product name</th>
<th>Quantity</th>
<th>Price per unit</th>
</tr>
</thead>
<tbody>
{% for item in order.items %}
<tr>
<td>{{item.name}}</td><td>{{item.quantity}}</td>
<td>${{item.price_per_unit}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div><strong>Total amount:</strong>{{order.total}}</div>
<hr/>
</div>
{% endfor %}
{% endblock %}
-
修改样式文件
-
打开
static/styles目录下的site.css文件,进行如下修改:
-
打开
.nav.navbar-nav .fa-home,
.nav.navbar-nav .fa-shopping-cart,
.nav.navbar-nav .fa-truck {
font-size: 1.5em;
}
- 在文件末尾添加订单历史页面的特定样式:
.order-container {
border: 1px solid #000;
margin: 20px;
padding: 10px;
}
-
修改基础模板文件
-
打开
templates目录下的base.html文件,在购物车菜单选项后添加订单菜单选项:
-
打开
<li>
<a href="/my-orders/">
<i class="fa fa-truck" aria-hidden="true"></i> ORDERS
</a>
</li>
- 修改错误消息显示代码:
{% if messages %}
{% for message in messages %}
{% if message.tags == 'error' %}
<div class="alert alert-danger" role="alert">
{% else %}
<div class="alert alert-info" role="alert">
{% endif %}
{{message}}
</div>
{% endfor %}
{% endif %}
二、测试集成
现在所有设置都已完成,需要启动网站和服务来验证一切是否正常工作。
1. 打开两个终端,在一个终端中启动在线视频游戏商店:
python manage.py runserver
- 在另一个终端中启动订单服务:
python manage.py runserver 127.0.0.1:8001
-
打开浏览器,访问
http://localhost:8000并使用凭据登录。登录后,添加一些商品到购物车,然后进入购物车视图,点击发送订单按钮。如果一切正常,页面顶部会显示通知,并且发送订单后购物车会清空。点击顶部菜单中的“ORDERS”选项,应该能看到刚刚提交的订单。
三、部署到 AWS
现在要将游戏商店 Django 应用和订单服务部署到 Amazon Web Services 的 EC2 实例上。
-
部署游戏商店应用
- 通过 SSH 连接到要部署游戏在线应用的 EC2 实例:
ssh -i gamestore-keys.pem ec2-user@35.176.16.157
- 更新已安装的软件包:
sudo yum update
- 安装 Python 和 nginx:
sudo yum install python36.x86_64 python36-pip.noarch python36-setuptools.noarch
sudo yum install nginx
- 安装项目依赖:
sudo pip-3.6 install django requests pillow gunicorn
- 从本地机器复制应用到 EC2 实例:
scp -R -i gamestore-keys.pem ./gamestore ec2-user@35.176.16.157:~/gamestore
- 修改 `settings.py` 文件:
ALLOWED_HOSTS=["35.176.16.157"]
ORDER_SERVICE_BASEURL = "http://35.176.194.15"
- 创建应用文件夹并复制应用:
sudo mkdir /app && sudo cp -R ./gamestore /app/
- 修改文件夹所有权:
cd / && sudo chown -R nginx:nginx ./gamestore
- 配置 nginx:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
root /app/gamestore;
}
- 重启 nginx 服务:
sudo service nginx restart
- 启动应用:
cd /app/gamestore
sudo gunicorn -u nginx gamestore.wsgi
-
部署订单服务
- 重复上述步骤直到安装 nginx,确保使用另一个 EC2 实例的弹性 IP 地址。
- 安装订单服务依赖:
sudo pip-3.6 install django djangorestframework requests
- 复制项目文件:
scp -R -i order-service-keys.pem ./order ec2-user@35.176.194.15:~/gamestore
- 修改 `settings.py` 文件:
ALLOWED_HOSTS=["35.176.194.15"]
- 创建文件夹并复制项目:
sudo mkdir /srv && sudo cp -R ./order /srv/
- 修改文件夹所有权:
cd / && sudo chown -R nginx:nginx ./order
- 配置 nginx:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- 重启 nginx 服务:
sudo service nginx restart
- 启动服务:
cd /srv/order
sudo gunicorn -u nginx order.wsgi
四、无服务器通知应用
接下来将探索 AWS Lambda 函数和 AWS API Gateway。AWS Lambda 可让我们创建无服务器函数,无服务器并不意味着没有服务器,而是这些函数不需要像在 EC2 实例上运行应用那样的 DevOps 开销。
-
设置环境
-
创建一个名为
notifier的目录并进入该目录:
-
创建一个名为
mkdir notifier && cd notifier
- 使用 `pipenv` 创建虚拟环境:
pipenv --python ~/Installs/Python3.6/bin/python3.6
- 安装 Flask 和 `requests` 包:
pipenv install flask
pipenv install requests
后续将学习如何使用 AWS Simple Email Service 从应用程序发送电子邮件。整个过程的流程图如下:
graph LR
A[完善在线游戏商店项目] --> B[测试集成]
B --> C[部署到 AWS]
C --> D[探索无服务器通知应用]
D --> E[设置环境]
E --> F[学习使用 AWS SES 发送邮件]
通过以上步骤,我们完成了在线游戏商店的功能完善、集成测试、部署到 AWS 以及开始探索无服务器通知应用的前期准备工作。
在线游戏商店集成与部署及无服务器通知应用
五、创建服务使用 Flask 框架
在完成环境设置后,我们可以开始使用 Flask 框架创建一个服务。Flask 是一个轻量级的 Web 框架,非常适合快速开发小型应用。
1.
创建 Flask 应用文件
- 在
notifier
目录下创建一个名为
app.py
的文件,内容如下:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
- 上述代码创建了一个简单的 Flask 应用,当访问根路径时,会返回 `Hello, World!`。
-
运行 Flask 应用
- 在终端中激活虚拟环境:
pipenv shell
- 运行 Flask 应用:
python app.py
- 打开浏览器,访问 `http://127.0.0.1:5000`,应该能看到 `Hello, World!` 页面。
六、安装和配置 AWS CLI
AWS CLI(Command Line Interface)是一个用于管理 AWS 服务的命令行工具,我们需要安装并配置它来与 AWS 进行交互。
1.
安装 AWS CLI
- 根据不同的操作系统,选择合适的安装方式。以 Linux 为例,可以使用以下命令安装:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
-
配置 AWS CLI
- 运行以下命令进行配置:
aws configure
- 按照提示输入 AWS Access Key ID、AWS Secret Access Key、默认区域名称和默认输出格式。这些信息可以在 AWS 控制台的 IAM(Identity and Access Management)服务中获取。
七、使用 CLI 创建 S3 桶并上传文件
S3(Simple Storage Service)是 AWS 提供的对象存储服务,我们可以使用 AWS CLI 创建 S3 桶并上传文件。
1.
创建 S3 桶
- 使用以下命令创建一个 S3 桶,桶名必须全局唯一:
aws s3 mb s3://my-unique-bucket-name
-
上传文件到 S3 桶
-
在
notifier目录下创建一个测试文件,例如test.txt,内容随意。 - 使用以下命令将文件上传到 S3 桶:
-
在
aws s3 cp test.txt s3://my-unique-bucket-name
八、安装和配置 Zappa
Zappa 是一个用于将 Python 应用部署到 AWS Lambda 和 API Gateway 的工具,非常适合部署无服务器应用。
1.
安装 Zappa
- 在虚拟环境中使用以下命令安装 Zappa:
pipenv install zappa
-
初始化 Zappa
-
在
notifier目录下运行以下命令初始化 Zappa:
-
在
zappa init
- 按照提示进行配置,选择合适的设置,例如 AWS 区域、S3 桶等。
九、使用 Zappa 部署应用
完成 Zappa 的配置后,就可以使用它将 Flask 应用部署到 AWS Lambda 和 API Gateway。
1.
部署应用
- 使用以下命令部署应用:
zappa deploy dev
- `dev` 是部署环境的名称,可以根据需要更改。部署过程可能需要一些时间,完成后会返回一个 API Gateway 的 URL。
-
测试部署
-
打开浏览器,访问 Zappa 返回的 API Gateway URL,应该能看到与本地运行时相同的
Hello, World!页面。
-
打开浏览器,访问 Zappa 返回的 API Gateway URL,应该能看到与本地运行时相同的
十、总结与展望
在本章中,我们完成了多个重要任务。首先完善了在线视频游戏商店项目,实现了订单提交和订单历史查看功能,通过一系列的代码修改和配置,让商店的功能更加完整。接着进行了集成测试,确保网站和服务能够正常协同工作。然后将游戏商店应用和订单服务部署到了 AWS 的 EC2 实例上,通过详细的步骤完成了服务器的配置和应用的启动。最后,我们开始探索无服务器应用领域,利用 Flask 框架创建服务,安装和配置了 AWS CLI、Zappa 等工具,并成功将应用部署到 AWS Lambda 和 API Gateway。
未来,我们可以进一步扩展这些功能。例如,在无服务器应用方面,可以结合 AWS Simple Email Service 实现订单状态变更的邮件通知功能,当订单成功接收或状态变为已发货时,自动发送邮件通知用户。在游戏商店项目中,可以继续优化用户界面和用户体验,增加更多的商品管理和订单管理功能。同时,还可以考虑引入更多的 AWS 服务,如 DynamoDB 用于数据存储,进一步提升系统的性能和可扩展性。
整个项目的步骤总结如下表:
|步骤|操作内容|
| ---- | ---- |
|完善在线游戏商店项目|配置设置文件、修改模型文件、模板文件、添加新 URL、修改视图文件、创建新模板文件、修改样式文件和基础模板文件|
|测试集成|启动网站和服务,验证功能|
|部署到 AWS|分别部署游戏商店应用和订单服务到 EC2 实例|
|探索无服务器通知应用|设置环境、创建 Flask 服务、安装和配置 AWS CLI、Zappa,部署应用|
通过这些步骤,我们逐步构建了一个功能丰富、可扩展的在线游戏商店系统,并开始涉足无服务器应用领域,为后续的开发和优化奠定了坚实的基础。
超级会员免费看

被折叠的 条评论
为什么被折叠?



