Symfoware

Symfowareについての考察blog

nginx + luaで簡易ファイルストレージ

画像ファイル等を、別サーバーへ受け渡すため一時的に保存するツールが欲しい。
良い案を模索中です。


nginx + luaによる実装



とりあえず、nginxのluaモジュールで簡単なファイルストレージを作ってみました。


nginx.confに、ファイルの保存先とluaプログラムのパスを指定。
合わせて、アップロードサイズの上限「client_max_body_size」を増やしておきます。


    client_max_body_size 4G;
    
    server {

        location /strage {
                set $path '/var/www/data/';
                content_by_lua_file /var/www/strage.lua;
        }





luaプログラム。

・strage.lua


  1. local do_get = function(filepath)
  2.     local file = io.open(filepath, 'rb')
  3.     ngx.print(file:read('*a'))
  4.     file.close()
  5. end
  6. local do_post = function(filepath)
  7.     -- body_dataを取得してみる
  8.     local req_body = ngx.req.get_body_data()
  9.     
  10.     -- 取得できたら、req_body自身が保存すべき内容
  11.     if req_body then
  12.         local f = io.open(filepath, "wb")
  13.         f:write(req_body)
  14.         f:close()
  15.         
  16.         ngx.say('saved')
  17.         return
  18.     end
  19.     
  20.     -- もし取得できていなかったら、データはファイルに行っている
  21.     -- テンポラリのファイル名を取得
  22.     local req_body_file_name = ngx.req.get_body_file()
  23.     os.rename(req_body_file_name, filepath)
  24.     ngx.say('moved')
  25. end
  26. local do_delete = function(filepath)
  27.     os.remove(filepath)
  28. end
  29. local strage_path = ngx.var.path
  30. if strage_path == "" or string.sub(strage_path, -1) ~= "/" then
  31.     strage_path = strage_path .. '/'
  32. end
  33. -- bodyの解析
  34. ngx.req.read_body()
  35. -- ヘッダーから保存するときのファイル名を取得
  36. local filename = ngx.req.get_headers()["filename"]
  37. -- ファイルの保存先
  38. local filepath = strage_path .. filename
  39. local method = ngx.req.get_method()
  40. if method == 'GET' then
  41.     do_get(filepath)
  42.     
  43. elseif method == 'POST' then
  44.     do_post(filepath)
  45.     
  46. elseif method == 'DELETE' then
  47.     do_delete(filepath)
  48.     
  49. end





Pythonのサンプル


  1. # -*- coding:utf-8 -*-
  2. import urllib2
  3. url = "http://192.168.1.103/strage"
  4. # --- ファイルのアップロード ---
  5. with open('cc2.jpg', 'rb') as f:
  6.     image = f.read()
  7. req = urllib2.Request(url, data=image)
  8. # ヘッダーに保存するファイル名を指定する
  9. req.add_header('filename', 'cc2.jpg')
  10. res = urllib2.urlopen(req)
  11. # --- ファイルのダウンロード ---
  12. req = urllib2.Request(url)
  13. req.add_header('filename', 'cc2.jpg')
  14. res = urllib2.urlopen(req)
  15. f = open('test.jpg', 'wb')
  16. f.write(res.read())
  17. f.close()
  18. # --- ファイルのダ削除 ---
  19. req = urllib2.Request(url)
  20. req.add_header('filename', 'cc2.jpg')
  21. req.get_method = lambda: 'DELETE'
  22. res = urllib2.urlopen(req)








MariaDB blobとの比較



同じサーバーにMariaDBをインストール。
longblobフィールドを持つテーブルを作成し、画像テンポラリとして使ってみます。


  1. CREATE TABLE imagetmp (
  2.     name varchar(50) primary key,
  3.     image longblob not null
  4. )




luaとどちらが速いかテスト。
14Mの画像を登録、復元、削除というオペレーションを100回繰り返し、必要な時間を計測してみます。

まず、今回作ったluaのファイルストレージ。


  1. # -*- coding:utf-8 -*-
  2. import urllib2
  3. import time
  4. url = "http://192.168.1.103/strage"
  5. # 登録用のデータを準備
  6. with open('04.jpg', 'rb') as f:
  7.     image = f.read()
  8. start = time.time()
  9. for i in xrange(100):
  10.     # --- ファイルのアップロード ---
  11.     req = urllib2.Request(url, data=image)
  12.     # ヘッダーに保存するファイル名を指定する
  13.     req.add_header('filename', '04.jpg')
  14.     res = urllib2.urlopen(req)
  15.     # --- ファイルのダウンロード ---
  16.     req = urllib2.Request(url)
  17.     req.add_header('filename', '04.jpg')
  18.     res = urllib2.urlopen(req)
  19.     f = open('test.jpg', 'wb')
  20.     f.write(res.read())
  21.     f.close()
  22.     # --- ファイルのダ削除 ---
  23.     req = urllib2.Request(url)
  24.     req.add_header('filename', '04.jpg')
  25.     req.get_method = lambda: 'DELETE'
  26.     res = urllib2.urlopen(req)
  27. elapsed_time = time.time() - start
  28. print ("elapsed_time:{0}".format(elapsed_time)) + "[sec]"




24.5秒ぐらい



続いて、MariaDBのテンポラリ


  1. # -*- coding:utf-8 -*-
  2. import MySQLdb
  3. import time
  4. con = MySQLdb.connect(
  5.     host='192.168.1.103',
  6.     user='admin',
  7.     db='sample',
  8.     passwd='P@ssw0rd',
  9.     charset='utf8')
  10. cur = con.cursor()
  11. # 登録用のデータを準備
  12. with open('cc2.jpg', 'rb') as f:
  13.     image = f.read()
  14. start = time.time()
  15. for i in xrange(100):
  16.     cur.execute("INSERT INTO imagetmp (name, image) VALUES ('04.jpg', %s)", [image,])
  17.     con.commit()
  18.     cur.execute("SELECT image FROM imagetmp where name = '04.jpg'")
  19.     row = cur.fetchone()
  20.     f = open('test.jpg', 'wb')
  21.     f.write(row[0])
  22.     f.close()
  23.     cur.execute("DELETE FROM imagetmp where name = '04.jpg'")
  24.     con.commit()
  25. elapsed_time = time.time() - start
  26. print ("elapsed_time:{0}".format(elapsed_time)) + "[sec]"
  27. cur.close()
  28. con.close()



8秒ぐらい


・・・遅すぎて使い物にならない。
良い案だと思ったけど、ちゃんとメモリにキャッシュするようにしないとダメか。
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2016/02/21(日) 18:06:36|
  2. Lua
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Debian 8にjenkinsをapt-getでインストール | ホーム | Alpine Linux nginx-luaをインストール>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1854-fc8e9923
この記事にトラックバックする(FC2ブログユーザー)