エントリー
phpでプログラムを作成すればするほど、サーバー上のデーモンが必要なタスクに出くわします。 はい、もちろんphpDaemon、cron、またはcrutchesがあり、スクリプトをn回実行するたびに特定の操作セットが発生します。 しかし、通常のサイトよりも負荷の大きいプロジェクトについて話すと、動揺し始めます。
プロジェクトの1つで、この問題を解決するために、多数のphp + RabbitMQ + erlangを使用することにしました。 必要な機能は既にphpで記述されており、時間内に異なるマシンに呼び出しを分散するだけで済みました。 具体的には、タスクは次のとおりでした。外部データストアからユーザーパーサーを記述し、最も重要なこととして、データを最新の状態に保ち、変更があった場合は通知を送信します。
ソースデータ
-ユーザーをお気に入りに追加するphpのレスト機能。 将来的にはこのリストから、最新の情報を保持するユーザーが作成され、変更があった場合はクライアントに通知が送信されます。
-アーランジのアプリケーション。現在処理しているユーザーなどを調整する必要があります。 外部ソースからの情報処理は2つの方法で実行されます-これはhtmlページを解析するか、可能であればAPIリクエストを解析することです。 応答を処理するには、phpで記述されたrest関数が使用されます。
-多数のサーバーに簡単に拡張できる機能をサポートする必要があります。 解析するユーザーのリストは、RabbitMQによって形成されたキューにあります。
タスク番号1-RabbitMQで動作するようにPHP拡張を構成する
まず、追加のソフトウェアパッケージをインストールします。
apt-get install gcc apt-get install php5-dev
さらに、インターネット上にあるインストール自体:
#download and install the rabbitmq c amqp lib wget https://github.com/alanxz/rabbitmq-c/releases/download/v0.5.1/rabbitmq-c-0.5.1.tar.gz tar -zxvf rabbitmq-c-0.5.1.tar.gz cd rabbitmq-c-0.5.1/ ./configure make sudo make install cd .. #download and compile the amqp wget http://pecl.php.net/get/amqp-1.4.0.tgz tar -zxvf amqp-1.4.0.tgz cd amqp-1.4.0/ phpize && ./configure --with-amqp && make && sudo make install #Add amqp extension to php mods-availabile directory echo "extension=amqp.so" > /etc/php5/mods-available/amqp.ini #Enabled it in cli cd /etc/php5/cli/conf.d/ ln -s ../../mods-available/amqp.ini 20-amqp.ini php -m | grep amqp #Enabled it in cli cd /etc/php5/apache2/conf.d/ ln -s ../../mods-available/amqp.ini 20-amqp.ini #restart Apache and than check phpinfo on web service apache2 restart
運がよければ、すべてが正しく設定されています。
タスク番号2-RabbitMQとそのWebベースのコントロールパネルをインストールする
sudo apt-get install rabbitmq-server rabbitmq-plugins enable rabbitmq_management rabbitmqctl stop rabbitmq-server -detached
次に、このアドレスで、デフォルトでログイン(ゲスト)とパスワード(ゲスト)でキュー管理にアクセスできます。
ip.addres :15672 /
タスク番号3-PHPを介したRabbitMQキューへの影響
// $rabbit = new AMQPConnection(array('host' => '127.0.0.1', 'port' => '5672', 'login' => 'guest', 'password' => 'guest')); $rabbit->connect(); $testChannel = new AMQPChannel($rabbit); $exchange = new AMQPExchange($testChannel); $exchange->setName('logoooooo'); $exchange->setType(AMQP_EX_TYPE_DIRECT); $exchange->declare(); // $testChannel = new AMQPChannel($rabbit); $queue = new AMQPQueue($testChannel); $queue->setName("yyyyyyy2"); $queue->declare(); // $testChannel = new AMQPChannel($rabbit); $queue = new AMQPQueue($testChannel); $queue->setName("yyyyyyy2"); $queue->bind('logoooooo'); $queue->declare(); // $testChannel = new AMQPChannel($rabbit); $exchange = new AMQPExchange($testChannel); $exchange->setName('logoooooo'); $exchange->publish('pooooooooooooooooooooooooooooooo');
タスク番号4-鉄筋を使用してアーランでアプリケーションを作成する
鉄筋をインストールする
apt-get install rebar mkdir 1 rebar create template=simpleapp srvid=my_server46 rebar create template=simplesrv srvid=my_server46
タスク番号5-アーランでアプリケーションを実行する
最初にCMakeをインストールします。
apt-get install make
Makefileに次のように書きます:
all: rebar compile run: ERL_LIBS=deps erl +K true -name myapp_app@127.0.0.1 -boot start_sasl -pa ebin -s myapp_app -sasl errlog_type error
行-pa ebin -s myapp_appは、ebin / myapp_app.erlを実行し、関数myapp_app:start()が含まれていることを意味します。
ERL_LIBS = depsは、depsフォルダーにあるすべてのライブラリーをロードすることを意味します。
タスク番号6-RabbitMQとErlangの通信に必要なライブラリを接続します
rebar.configに以下を追加します。
{deps, [ {rabbit_common, ".*", {git, "git://github.com/jbrisbin/rabbit_common.git", {tag, "rabbitmq-3.0.2"}}} ]}. {erl_opts, [ debug_info, compressed, report, warn_export_all, warn_export_vars, warn_shadow_vars, warn_unused_function, warn_deprecated_function, warn_obsolete_guard, warn_unused_import % warnings_as_errors ]}.
rebar get-depsを実行して、依存関係を引き出します。 さらに、残りのライブラリで問題が発生したため、公式のRabbitMQ Webサイトに書かれたものを使用する必要がありました。 ただし、これらの前に必要なパッケージをインストールします。
apt-get install xsltproc apt-get install zip
鉄筋が作成したdepsフォルダーに移動し、gitを使用してすべてをダウンロードしてからインストールします。
cd deps git clone https://github.com/rabbitmq/rabbitmq-erlang-client.git git clone https://github.com/rabbitmq/rabbitmq-server.git git clone https://github.com/rabbitmq/rabbitmq-codegen.git cd rabbitmq-erlang-client make
タスク番号7-ErlangからRabbitMQキューからメッセージを受信する
myapp_app.erlファイルは、編集可能なメイクファイルから実行できるように、少し編集可能なままにしておきます。
-module(myapp_app). -behaviour(application). -export([start/0,start/2, stop/1]). start() -> myapp_sup:start_link(). start(_StartType, _StartArgs) -> myapp_sup:start_link(). stop(_State) -> ok.
プロセスの監視を担当するファイルmyapp_sup.erlは、initからモジュールへの呼び出しを追加します。
-module(myapp_sup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> {ok, { {one_for_one, 5, 10}, [{my_server46_0, {my_server46, start_link, []},permanent, brutal_kill, worker, [my_server46]}]} }.
RabbitMQとの通信を担当するモジュール:
-module(my_server46). -behaviour(gen_server). -include("deps/rabbitmq-erlang-client/include/amqp_client.hrl"). -define(SERVER, ?MODULE). -export([start_link/0,main/0,loop/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). init(Args) -> main(), {ok, Args}. main() -> {ok, Connection} = amqp_connection:start(#amqp_params_network{host = "localhost"}), {ok, Channel} = amqp_connection:open_channel(Connection), io:format(" [*] Waiting for messages. To exit press CTRL+C~n"), amqp_channel:call(Channel, #'basic.qos'{prefetch_count = 1}), amqp_channel:subscribe(Channel, #'basic.consume'{queue = <<"yyyyyyy2">>},self()), receive #'basic.consume_ok'{} -> io:fwrite(" _rec_main_ok_ "),ok end, loop(Channel), io:fwrite("begin~n", []). loop(Channel)-> receive {#'basic.deliver'{delivery_tag = Tag}, #amqp_msg{payload = Body}} -> Dots = length([C || C <- binary_to_list(Body), C == $.]), io:format(" [x] Received Body ~p~n", [Body]), receive after Dots*1000 -> io:format(" _loop_rec_after_ ~p",[0]), ok end, timer:sleep(3500), amqp_channel:cast(Channel, #'basic.ack'{delivery_tag = Tag}), loop(Channel), io:format(" [x] Done 3~n") end. handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}.
ここではすべてが非常に簡単です。キュー「yyyyyyy2」にサブスクライブします。
amqp_channel:subscribe(Channel, #'basic.consume'{queue = <<"yyyyyyy2">>},self())
次に、メッセージが正常に処理されたことをRabbitMQに通知します。
amqp_channel:cast(Channel, #'basic.ack'{delivery_tag = Tag})