すべての外部製品の動作を保証するために、人気のあるnginxを使用しています。 高速で信頼性があります。 それにほとんど問題はありません。 当社の製品も絶えず進化しており、新しいサービスが登場し、新しい機能が追加され、古いものが拡張されています。 視聴者と負荷は増え続けています。 ここで、影響を受けるアプリケーションの可用性とフォールトトレランスを維持しながら、開発を加速し、生産性を大幅に向上させ、サービスにこの新しい機能を簡単に追加できるようにする方法について説明します。 「nginx as web application」の概念についてです。
すなわち、
サードパーティのモジュール(主にLUA)について、完全に魔法のようなことを迅速かつ確実に行うことができます。

問題と解決策
基本的な考え方は非常に簡単です。 次の要因を考慮してください。
-アプリケーションロジックの複雑さ、
-アプリケーションコンポーネントの数、
-オーディエンスのサイズ。
ある時点から、アプリケーションの応答性と高速性、時には機能性を維持することは非常に困難になります。 製品はマルチコンポーネントになり、地理的に分散されます。 そして、ますます多くの人々がそれを使用しています。 同時に、応答性とフォールトトレランスに関するビジネス要件があり、最初にそれを遵守する必要があります。
この問題を解決するにはいくつかの方法があります。 あなたはすべてを壊し、他の技術でそれを作り直すことができます。 もちろん、このオプションは機能しますが、あまり気に入らなかったため、徐々にやり直すことにしました。 基本は、アセンブリの
オープンレスト (nginx + LUA)です。 なぜLUA。 cgi、fastcgi、およびその他のcgiの助けがなくても、強力で美しく、高速な機能をnginx構成ファイルに直接スクリプト化できます。 すべてが非同期に動作します。 そして、顧客だけでなく、バックエンドにも。 同時に、Webサーバーのイベントループに干渉することなく、コールバックなしで、既存のnginx機能を完全に使用します。
現在、次のバックエンドが利用可能です。
-redis
-memcache
-MySQL
-PostgreSQL
さらに、
RabbitMQや
ZeroMQなど、使用するモジュールをさらに接続できます。
非常に高速に動作します。 とにかく、php-fpmより高速))
論理的な質問は、Cですべてをすべて書き換えてみませんか? LUAでの書き込みは、はるかに簡単で高速です。 そして、非同期およびnginxイベントループに関連する問題をすぐに回避できます。
例。 アイデア
通常どおり、完全なコードは提供せず、主要部分のみを提供します。 これらはすべて、以前はphpで行われていました。
1.この部分は、同僚の
AotDによって発明され作成され
ました 。 画像のリポジトリがあります。 ユーザーに表示する必要があり、サイズ変更などのいくつかの操作を実行することをお勧めします。 写真をcephに保存します。これはAmazon S3の類似物です。 ImageMagickは画像処理に使用されます。 リサイザーにキャッシュディレクトリがあり、処理された画像がそこに追加されます。
ユーザーのリクエストを解析し、写真、ユーザーが必要とする解像度を決定してcephに進み、その場で処理して表示します。
serve_image.lua
require "config" local function return_not_found(msg) ngx.status = ngx.HTTP_NOT_FOUND if msg then ngx.header["X-Message"] = msg end ngx.exit(0) end local name, size, ext = ngx.var.name, ngx.var.size, ngx.var.ext if not size or size == '' then return_not_found() end if not image_scales[size] then return_not_found('Unexpected image scale') end local cache_dir = static_storage_path .. '/' .. ngx.var.first .. '/' .. ngx.var.second .. '/' local original_fname = cache_dir .. name .. ext local dest_fname = cache_dir .. name .. size .. ext
imagic.lua
バインディングを接続します。
LuaJITにアクセスできる必要があります。
nginx_partial_resizer.conf.template
2. APIのファイアウォール。 検証、顧客識別、rps制御、および不要なものに対する障壁を要求します。
Firewall.lua
module(..., package.seeall); local function ban(type, element) CStorage.banPermanent:set(type .. '__' .. element, 1); ngx.location.capture('/postgres_ban', { ['vars'] = { ['type'] = type, ['value'] = element} }); end local function checkBanned(apiKey)
Validator.lua
module(..., package.seeall); local function checkApiVersion() local apiVersion = ''; if not (ngx.ctx.REQUEST['version']) then local nginx_request = tostring(ngx.var.uri); local version = nginx_request:sub(2,4); if tonumber(version:sub(1,1)) and tonumber(version:sub(3,3)) then apiVersion = version; else return CApiException.throw('versionIsRequired'); end; else apiVersion = ngx.ctx.REQUEST['version']; end; local isSupported = false; for i, version in pairs(config.app_params['supported_api_version']) do if apiVersion == version then isSupported = true; end; end; if not (isSupported) then CApiException.throw('unsupportedVersion'); end; ngx.ctx.GLOBAL['api_version'] = apiVersion; end; local function checkKey() if not (ngx.ctx.REQUEST['key']) then CApiException.throw('unauthorized'); end; end; function run() checkApiVersion(); checkKey(); end;
Apikey.lua
module ( ..., package.seeall ) function init() if not(ngx.ctx.GLOBAL['CApiKey']) then ngx.ctx.GLOBAL['CApiKey'] = {}; end end; function flush() CStorage.apiKey:flush_all(); CStorage.apiKey:flush_expired(); end; function load() local dbError = nil; local dbData = ngx.location.capture('/postgres_get_keys'); dbData = dbData.body; dbData, dbError = rdsParser.parse(dbData); if dbData ~= nil then local rows = dbData.resultset if rows then for i, row in ipairs(rows) do local cacheKeyData = {}; for col, val in pairs(row) do if val ~= rdsParser.null then cacheKeyData[col] = val; else cacheKeyData[col] = nil; end end CStorage.apiKey:set(tostring(cacheKeyData['key']),JSON:encode(cacheKeyData)); end; end; end; end; function checkNotEmpty() if not(ngx.ctx.GLOBAL['CApiKey']['loaded']) then local cnt = CHelper.tablelength(CStorage.apiKey:get_keys(1)); if cnt == 0 then load(); end; ngx.ctx.GLOBAL['CApiKey']['loaded'] = 1; end; end; function getData(key) checkNotEmpty(); return CStorage.apiKey:get(key); end; function getStatus(key) key = getData(key); local result = ''; if key ~= nil then key = JSON:decode(key); if key['is_active'] ~= nil and key['is_active'] == true then result = 'allowed'; else result = 'blocked'; end; else result = 'forbidden'; end; return result; end; function blockTemporary(apiKey) apiKey = tostring(apiKey); local isset = getData(apiKey); if isset then CStorage.tmpBlockedDemoKeys:set(apiKey, 1, config.app_params['ban_params']['time_demo_apikey_block_tmp']); end; end; function block(apiKey) apiKey = tostring(apiKey); local keyData = getData(apiKey); if keyData then ngx.location.capture('/redis_get', { ['vars'] = { ['key'] = apiKey } }); keyData['is_active'] = false; CStorage.apiKey:set(apiKey,JSON:encode(cacheKeyData)); end; end;
Storages.lua
module ( ..., package.seeall ) apiKey = ngx.shared.apiKey; RPS = ngx.shared.RPS; RPP = ngx.shared.RPP; banPermanent = ngx.shared.banPermanent; banTmp = ngx.shared.banTmp; tmpBlockedDemoKeys = ngx.shared.tmpBlockedDemoKeys;
3. AMQPプロトコルを介したコンポーネント間通信などの追加サービス。 例は
こちらです。
4.
すでに書いたように 。 バックエンドを介してリクエストを渡すためのルートを「スマートに」制御する機能を備えたアプリケーション自己診断モジュール。 まだ開発中です。
5. API用アダプター。 場合によっては、既存の方法を微調整、補足、または拡張する必要があります。 すべてを書き換えないために、
LUAが役立ちます 。 たとえば、オンザフライでのjson <-> xml変換。
6 ... さらに多くのアイデアがあります。
そのようなベンチマークはありません。 製品は複雑すぎるため、ベンチマーク後のrpは多くの要因に大きく依存しています。 ただし、当社の製品では、影響を受ける機能の生産性が20倍に向上し、場合によってはすべてが最大200倍高速になりました。
長所と短所
有形のプラス。 phpで5メガバイトのコードであったものはすべて、luaで100kbファイルに変わります。
-開発速度、
-アプリケーションの速度、
-信頼性
-イベントループnginxを中断せずに、クライアントとバックエンドとの非同期動作、
-LUAシュガーは気持ちいい! コルーチン、すべてのnginxフォークの共有辞書、サブクエスト、たくさんのバインダー。
目に見えない短所。
-すべてを注意深く行い、非同期とイベントループnginxについて覚えておく必要があります。
-フロントエンドは非常に高速であるため、バックエンドは気に入らないかもしれません。 レイヤーなしで、それらの間には直接接続があります。 たとえば、フロントエンドで1秒あたり10,000リクエストのLUAが簡単に機能すると確信しています。 ただし、同時に基地に行きたい場合は、問題が発生する可能性があります。
-問題が発生した場合、デバッグするのはかなり困難です。
ちなみに、この記事の執筆中に、その瞬間、プログラマー
はこのすべてについて高負荷について詳しく
語っています。
コメントで質問にお答えします。
最後に、
ここでトピックに関する情報の小さな選択を
見つけることができます 。