在本机上构建AWS Memcached的实施环境

首先

Amazon ElastiCache for Memcachedを利用した値の取得について記載します。
実行環境は以下となります。

001.PNG

另外,每个安全组的入站权限如下所示。

AWSサービス利用用途EC2User側から5000番ポート(アプリがflaskのため)の許可, Cloud9から22番ポートの許可(アプリ配置のため)RDSEC2,Cloud9の割り当てたセキュリティグループから3306番ポートの許可MemcachedEC2,Cloud9から11211番ポートの許可Cloud9デフォルト

创建各种AWS资源

我将介绍各个AWS资源的创建和配置。
针对EC2、RDS和Cloud9,我将提供常规的创建方法并记录其配置内容;而对于Memcached,我将记录其创建方法。

1. RDS的设置

关于RDS的构建本身我们就不讨论了(因为它适用于广泛的一般创建方法)。
一系列的数据输入工作是通过Cloud9实施的。
以下是在Cloud9中通过执行mysql –version确认安装有mysql,然后连接到XXXXX.ap-northeast-1.rds.amazonaws.com并输入数据的过程。

$ mysql --version
mysql  Ver 15.1 Distrib 10.2.38-MariaDB, for Linux (x86_64) using  EditLine wrapper
$ mysql -h XXXXX.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.23 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

> create database food;
> connect food;
> create table fruit (ID varchar(32) UNIQUE,Name varchar(128));
> insert into fruit (ID, Name) values('001' , 'Peach');
> insert into fruit (ID, Name) values('002' , 'Orange');
> select * from fruit;
+------+--------+
| ID   | Name   |
+------+--------+
| 001  | Peach  |
| 002  | Orange |
+------+--------+
2 rows in set (0.01 sec)

2. EC2与Cloud9配置

关于EC2和Cloud9的构建,由于广泛普遍的构建方法就不再赘述。登录后,在构建完成后,我们将执行以下的yum命令来安装flask和emcached模块。

$ pip3 install Flask
$ pip3 install PyMySQL
$ pip3 install python-memcached

また、今回はCloud9を開発環境としているため、Cloud9で作成したアプリケーションを配置するにはscpコマンドを利用し、EC2へのログインに関してはsshコマンドでCloud9経由でEC2へのログインを行っています。

$ scp -i ./AWS_TEST.pem -r ./flask_app ec2-user@10.1.10.110:/home/ec2-user 
$ ssh -i ./AWS_TEST.pem ec2-user@10.1.10.110

3. 创建Memcached

Amazon ElastiCache Memcachedの作成の作成についてですが、これは以下のように1画面入力するだけで作成できます。「キャッシュ環境の作成なんて難しいんだろうな」とかまるで思う必要ないと思います。作る分にはRDSより簡単なはずです。ポイントという事も特にないのですが、サブネットグループがあったりや、プライベートサブネットに配置しパブリックサブネットのEC2からアクセスしてきたデータを返せるようにするなどRDSの構築を行うように作成できます。

003.PNG

关于每个程序

这次使用了Python的Flask框架。
首先,目录结构如下所示。

002.PNG
flask_app
├── app
│   ├── app.py
│   ├── static
│   └── templates
│       ├── hit.html
│       ├── index.html
│       └── Nothit.html
└── run.py

每个Python和HTML文件的内容如下。

1. 运行.py

通过在Flask的执行或启动(开始)文件中运行以下命令来启动服务。执行命令后,可以通过http://x.x.x.x:5000进行网络访问。
$ python3 run.py #执行命令

from app.app import app

if __name__ == "__main__":
    app.run(host='0.0.0.0', port='5000')

2. 应用程序.py

アプリケーションプログラムファイルです。
それぞれポイントになりそうな点はプログラム内にコメントを入れております。
(except~~については適当に例外処理を記載しただけなのでコメント入れてないです。。。)

# import
import pymysql.cursors
import memcache
from flask import Flask, request, render_template

app = Flask(__name__)


#Memcachedの定義
cache = memcache.Client(['mytest.xxxxxx.cfg.apne1.cache.amazonaws.com:11211'])

#デフォルトページの指定
@app.route('/')
def index():
    return render_template("index.html")


#index.htmlから/kobetsuに対してデータが飛ばされたときの実行プログラム
@app.route('/kobetsu', methods=['GET'])
def kobetsu():
    ID = request.args.get('ID')
    cachevalue = cache.get(str(ID)) #キャッシュサーバに対してデータが格納されているかを確認

    if cachevalue: # キャッシュサーバにデータが格納されておりtrue状態であればhit.htmlを返す
        return render_template('hit.html', cachevalueID=cachevalue['ID'],cachevalueName=cachevalue['Name'])
    else: #キャッシュサーバにデータが格納されておらずfalse状態であればRDSにデータを取得しに行く
        connection = pymysql.connect(host='xxxxxx.xxxxxx.ap-northeast-1.rds.amazonaws.com',
        user='admin',
        password='1qaz2wsx',# 脆弱なパスワード←?
        db='food',
        charset='utf8',
        cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = 'SELECT * FROM fruit where ID="%s"' %(ID)
            cursor.execute(sql)
            results = cursor.fetchall()
            if results:
                for r in results:
                    cache.set(str(ID) ,r ,time=600) # index.htmlから飛ばされたIDに対して出力された結果をkey:ID value:r としてキャッシュサーバに格納。timeはキャッシュサーバのデータ保有時間
                    return render_template('Nothit.html', resultsID=r['ID'], resultsName=r['Name']) # Nothit.htmlを返す
            else:
                raise TypeError()
    except TypeError: #エラー処理は結構適当です。
        connection.rollback()
        return 'No ID!!!'
    except Exception as error:
        connection.rollback()
        return 'Please call jimbot 080-XXXX-XXXX', error.args[0]
    finally:
        connection.close()

#index.htmlから/tourokuに対してデータが飛ばされたときの実行プログラム
@app.route('/touroku', methods=['POST'])
def touroku():
    ID = request.form["ID"]
    Name = request.form["Name"]

    cache.flush_all() #キャッシュの削除を行う。これは、登録した後もキャッシュが残り続けていたらユーザが登録されていないと誤認するのを防ぐため。

    connection = pymysql.connect(host='xxxxxx.xxxxxx.ap-northeast-1.rds.amazonaws.com',
    user='admin',
    password='1qaz2wsx',# 脆弱なパスワード←?
    db='food',
    charset='utf8',
    cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = "INSERT INTO fruit (ID, Name) VALUES (%s, %s)"
            cursor.execute(sql, (ID,Name))
        connection.commit()
        return 'Welcome ID :  '  + str(ID) +'!!!'
    except connection.IntegrityError: #エラー処理は結構適当です。
        connection.rollback()
        return 'MemberID already exists!!! Please try update command and put option.'
    except NameError:
        connection.rollback()
        return 'There is a mistake in the value you entered. Please enter \'ID\',Name '
    except Exception as error:
        connection.rollback()
        return 'Please call jimbot 080-XXXX-XXXX', error.args[0]
    finally:
        connection.close()

#index.htmlから/taberaretaに対してデータが飛ばされたときの実行プログラム
@app.route('/taberareta', methods=['POST'])
def taberareta():
    ID = request.form["ID"]

    cache.flush_all()#キャッシュの削除を行う。これは、削除した後もキャッシュが残り続けていたらユーザが削除されていないと誤認するのを防ぐため。

    connection = pymysql.connect(host='xxxxxx.xxxxxx.ap-northeast-1.rds.amazonaws.com',
    user='admin',
    password='1qaz2wsx',# 脆弱なパスワード←?
    db='food',
    charset='utf8',
    cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = 'SELECT * FROM fruit where ID="%s"' %(ID)
            cursor.execute(sql)
            results = cursor.fetchall()
            if results:
                sql = "DELETE FROM fruit WHERE ID = '%s'" %(ID)
                cursor.execute(sql)
                connection.commit()
                return 'Delete ID :  '  + str(ID) +' (T_T)'
            else:
                raise TypeError()
    except TypeError:  #エラー処理は結構適当です。
        connection.rollback()
        return 'No ID!!!'
    except Exception as error:
        connection.rollback()
        return 'Please call jimbot 080-XXXX-XXXX', error.args[0]
    finally:
        connection.close()

3.索引.html

这是顶层界面。相当随意。不,非常随意。
在每个表单内输入值并按下“发送数据”按钮,会将数据传递到app.py内的”/touroku”,”/kobetsu”,”/taberareta”。

<html>
<head> </head>
<table border="0">
  <body>
    <h1>Welcome to Fruit Data</h1>
    <hr>
    <form action="/touroku" method="POST">
      <table border="2">
        <h3>Add Fruit Information Data</h3>
        <tr>
          <td align="right"><b> ID:</b></td>
          <td><input type="text" name="ID" size="3" maxlength="3"></td>
        </tr>
        <tr>
          <td align="right"><b> Name:</b></td>
          <td><input type="text" name="Name" size="30" maxlength="30"></td>
        </tr>
      </table>
      <td> <input type="submit" value="Send Data"> <input type="reset" value="Reset"> </td>
    </form> <br> <br>
    <form action="/kobetsu" method="Get">
      <table border="2">
        <h3>Get a Fruit Information Data</h3>
        <tr>
          <td align="right"><b> ID:</b></td>
          <td><input type="text" name="ID" size="3" maxlength="3"></td>
        </tr>
      </table>
      <td> <input type="submit" value="Send Data"> <input type="reset" value="Reset"> </td>
    </form> <br>
    <form action="/taberareta" method="POST">
      <table border="2">
        <h3>Delete Fruit Data (T_T) </h3>
        <tr>
          <td align="right"><b> ID:</b></td>
          <td><input type="text" name="ID" size="3" maxlength="3"></td>
        </tr>
      </table>
      <td> <input type="submit" value="Send Data"> <input type="reset" value="Reset"> </td>
    </form>
  </body>
</html>

4.点击.html

这是返回当缓存命中时显示的页面。
这是在app.py中使用【return render_template(’hit.html’,cachevalueID = cachevalue [‘ID’],cachevalueName = cachevalue [‘Name’])】返回的页面。

<head>
</head>
<body>
  <h1>hit!!!  ID {{ cachevalueID }} は {{ cachevalueName }} です。</h1>
</body>

5. 没有被击中的HTML网页

当缓存未命中时,返回的页面是由app.py中的【return render_template(‘Nothit.html’, resultsID=r[‘ID’], resultsName=r[‘Name’])】所返回的页面。

<head>
</head>
<body>
  <h1>Nothit...  ID {{ resultsID }} は {{ resultsName }} です。 </h1>
</body>

由于以下URL中所提供的说明非常易懂,包括构成和每个文件的内容,请点击链接查看。
https://qiita.com/kiyokiyo_kzsby/items/0184973e9de0ea9011ed

缓存功能测试

我们将对缓存的运作进行测试,以及其他功能进行测试。
测试将按照以下顺序进行。

1.フルーツ個別検索(hitなし)
2.フルーツ個別検索(hitあり)
3.フルーツ追加
4.フルーツ個別検索(3の追加時にキャッシュクリアされたためhitなし)
5.フルーツ個別検索(hitあり)
6.3で追加したフルーツの個別検索
7.3で追加したフルーツの削除
8.3で追加したフルーツを検索しデータなし
9.フルーツ個別検索(6の削除時にキャッシュクリアされたためhitなし)

0. 顶部的画面是这样的。

top.PNG

1. 水果单独搜索(无结果)

kekka_001.PNG

2. 查询水果(有结果)。

kekka_002.PNG

3. 添加水果

kekka_003_1.PNG
kekka_003_2.PNG

4. 在添加第三个水果时,由于缓存被清除,所以无法进行水果的单独搜索。

kekka_004.PNG

5. 水果个别搜索(有结果)

kekka_005.PNG

6.3版本新增了水果的个别搜索功能。

kekka_006_1.PNG
kekka_006_2.PNG

删除了在7.3版本中添加的水果。

kekka_007_1.PNG
kekka_007_2.PNG

在8月3日新增的水果中搜索并找不到数据。

kekka_008_1.PNG

确认不存在已删除的ID。我们正在捕获并返回到屏幕上的错误,以便在app.py中的RDS没有数据时返回。

    except TypeError: #エラー処理は結構適当です。
        connection.rollback()
        return 'No ID!!!'
kekka_008_2.PNG

9. 水果个别搜索(由于删除6时清除缓存,因此没有命中)

kekka_009_1.PNG
kekka_009_2.PNG

最后

這是我們輕鬆地創建緩存伺服器並使用簡單的程式進行緩存測試的結果。在此過程中,我們學會了使用緩存和鍵值對的概念。非常感謝您的閱讀。

广告
将在 10 秒后关闭
bannerAds