1 つの重要な質問: なぜこの方法でこれを実装したいのですか? 基本的に「疎」に結合されたコンポーネントを「密」に結合しています (共有ライブラリにはあらゆる種類のクラッシュ関連の問題があるため、マネージャーが停止します)。必要に応じて 2 つ以上の子プロセスを起動および再起動する (できる) マネージャー プログラムを用意してみませんか。
ある種のプロトコルを使用して、子プロセスが Manager と通信するか、相互に通信するようにします。ZeroMQをお勧めするのは、それが素晴らしいことと、プロセス間通信を完全に隠しているためです。つまり、(異なるマシン間の) ソケット、スレッド間のプロセス間通信、または単一のマシン上の名前付きパイプであり、非常に高速です。これは、クライアントを実装した後、クライアントをどのようにデプロイするかを決定できることを意味します。マネージャーにロードされた共有ライブラリとして、同じボックスで実行される個別のプロセスとして、または個別のマシンで実行される分散プロセスとして、ほとんど必要ありません。何かを変えるために。つまり、非常にスケーラブルです。
ただし、共有ライブラリのアプローチに専念している場合は、実装が少し難しいかもしれませんが、これに絶対にお勧めする「設計パターン」が 1 つあります。しかし、それはその重さの価値があります。
マネージャーは、モジュール間でメッセージを渡す前にタイムスタンプをチェックし、何か変更があった場合は再コンパイルしてリロードする必要があります。これは、コードの変更が「ホット」であることを意味します。変更を確認するためにマネージャーを停止し、再コンパイルし、マネージャーを再起動する必要はありません。したがって、js で開発するように C でプログラミングできます。何時間も節約できます。
少し前に、C++ と APR を使用して同様のことを行いました (ライブラリ間通信ではありません)。コードは少し「ハッキー」ですが、とにかくここにあります;-)
Makefile
独自のディレクトリに for each サブモジュールがあることに依存していることに注意してください。依存関係のために、タイムスタンプをチェックせず、リクエストごとに再コンパイルするだけです。これはあなたにとって理想的ではないかもしれないので、その部分を再考する必要があるかもしれません.
それを機能させる上で最も困難な部分は、ディレクトリに対する適切なアクセス許可を取得することでしたが、考えてみると、これはこれを fcgi プロセスとして実行していたため、実際に実行すると Web サーバーとして実行されたためです。ほとんどの場合、これらの問題は発生しません。
#ifndef _CMJ_RUN_HPP
#define _CMJ_RUN_HPP
#include <fcgio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <apr.h>
#include <apr_dso.h>
#include <apr_pools.h>
#include <apr_thread_proc.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
#include <stdexcept>
#include <cstdarg>
class Line {
protected:
std::stringstream line_;
bool isError_;
public:
Line(const char* line, bool isError) : line_(line), isError_(isError) {}
Line(const Line& rhs) : line_(rhs.line()), isError_(rhs.error()) {}
bool error() const { return isError_; }
const char* line() const { return line_.str().c_str(); }
const Line& operator = (const Line& rhs) {
line_.str() = rhs.line();
isError_ = rhs.error();
return rhs;
}
};
class Run {
protected:
int exitCode_;
std::vector<Line> out_;
bool errors_;
protected:
void run(const char* dir, const char* cmd, std::vector<const char*> &args, apr_pool_t* parentPool) ;
public:
Run(const char* dir, const char* cmd, std::vector<const char*> &args, apr_pool_t* parentPool);
Run(const char* dir, const char* cmd, apr_pool_t* parentPool);
int exitCode() { return exitCode_; }
bool errors() { return errors_; }
bool errors(std::ostream& out);
int size() { return out_.size(); }
Line& line(int i) { return out_[i]; }
};
class dso_error: public std::runtime_error {
public:
dso_error(const char* c) : std::runtime_error(c) {};
dso_error(std::string err) : std::runtime_error(err) {};
static dso_error instance(const char* format, ...) {
char errbuf[8192];
va_list va;
va_start(va, format);
vsnprintf(errbuf, 8192, format, va);
va_end(va);
return dso_error(errbuf);
}
};
/**
* Provides a building and loading framework for Dynamic libraries, with the full power
* of make behind it.
* Usage:
* <code>
* DsoLib so("/var/www/frontier/echo","/var/www/frontier/echo/libecho.so",pool);
* if (!so.errors(outStream)) {
* void (*pFn)(void) = sym("initialize");
* (*pFn)();
* }
* </code>
*/
class DsoLib : public Run {
protected:
apr_pool_t* pool_;
apr_dso_handle_t* dso_;
std::string dirname_;
std::string libname_;
public:
/** dir is the directory where make should be executed, libname is full path to the library
* from current working directory.
*/
DsoLib(const char* dir, const char* libname, apr_pool_t* parentPool) throw(dso_error);
~DsoLib();
void* sym(const char* symbol) throw (dso_error);
void* sym(std::string symbol) throw (dso_error) { return sym(symbol.c_str()); }
};
#endif
そしてRun.cpp
#include "Run.hpp"
#include <string>
#include <sstream>
#include <boost/filesystem.hpp>
#include <cassert>
#define DBGENDL " (" << __FILE__ << ":" << __LINE__ << ")" << endl
using namespace std;
Run::Run(const char* dir, const char* cmd, apr_pool_t* pool) : errors_(false) {
vector<const char *> args;
run(dir, cmd, args, pool);
}
Run::Run(const char* dir, const char* cmd, vector<const char*> &args, apr_pool_t* pool) : errors_(false) {
run(dir, cmd, args, pool);
}
void
Run::run(const char* dir, const char* cmd, vector<const char*> &args, apr_pool_t* parentPool) {
cout << "Run::run(dir=" << ", cmd=" << cmd << ", args...)" << endl;
apr_status_t status;
char aprError[1024];
struct aprPool_s {
apr_pool_t* pool_;
aprPool_s(apr_pool_t* parent) {
apr_pool_create(&pool_, parent);
}
~aprPool_s() {
apr_pool_destroy(pool_);
}
operator apr_pool_t* () { return pool_; }
} pool (parentPool);
apr_procattr_t* attr;
if (APR_SUCCESS != (status = apr_procattr_create(&attr, pool))) {
cerr << "apr_procattr_create error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
if (APR_SUCCESS != (status = apr_procattr_dir_set(attr, dir))) {
cerr << "apr_procattr_dir_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
if (APR_SUCCESS != (status = apr_procattr_cmdtype_set(attr, APR_PROGRAM_ENV))) {
cerr << "apr_procattr_cmdtype_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
if (APR_SUCCESS != (status = apr_procattr_io_set(attr, APR_NO_PIPE, APR_FULL_NONBLOCK, APR_FULL_NONBLOCK))) {
cerr << "apr_procattr_io_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
if (APR_SUCCESS != (status = apr_procattr_user_set(attr, "craig", "lateral"))) {
cerr << "apr_procattr_user_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
if (APR_SUCCESS != (status = apr_procattr_group_set(attr, "craig"))) {
cerr << "apr_procattr_group_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
apr_proc_t proc;
const char **argv = (const char**) new char*[ 2 + args.size() ];
argv[0] = cmd;
size_t i=0;
size_t argc=args.size();
for (i=0; i<argc; i++) {
argv[i+1] = args[i];
cerr << "arg " << i << " = " << args[i];
}
argv[i+1] = NULL;
argc++;
cerr << "About to execute " << cmd << " in dir " << dir << endl;
cerr << "ARGS:" << endl;
for (i=0; i<argc; i++) {
cerr << "[" << i << "]: " << argv[i] << endl;
}
if (APR_SUCCESS != (status = apr_proc_create(&proc, cmd, argv, NULL, attr, pool))) {
cerr << "apr_proc_create error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
apr_exit_why_e exitWhy;
cerr << "--- " << cmd << " ---" << endl;
while (APR_CHILD_NOTDONE == (status = apr_proc_wait(&proc, &exitCode_, &exitWhy, APR_NOWAIT))) {
char line[1024];
status = apr_file_gets(line, sizeof(line), proc.out);
if (APR_SUCCESS==status) {
out_.push_back(Line(line, false));
cerr << line << endl;
}
status = apr_file_gets(line, sizeof(line), proc.err);
if (APR_SUCCESS==status) {
out_.push_back(Line(line, true));
errors_ = true;
cerr << "E:" << line ;
}
}
cerr << " -----" << endl;
delete[] argv;
if ( (APR_CHILD_DONE != status) && (APR_PROC_EXIT != status) ) {
cerr << "apr_proc_wait error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
}
cerr << cmd << " exited " << ((APR_PROC_EXIT==exitWhy) ? "PROC_EXIT" :
((APR_PROC_SIGNAL==exitWhy) ? "PROC_SIGNAL" :
((APR_PROC_SIGNAL_CORE==exitWhy) ? "PROC_SIGNAL_CORE" : "Unknown"))) << endl;
}
bool
Run::errors(std::ostream& os) {
cerr << "Run::errors(ostream) : errors()=" << errors() << endl;
if (errors()) {
cerr << "Writing errors to ostream" << endl;
os << "Content-type: text/html\r\n\r\n";
os << "<html><head><title>Errors</title>"
<< "<link rel=\"stylesheet\" type=\"text/css\" href=\"css/frontier.css\"></link>"
<< "</head>"
<< "<body>";
for (int i=0; i<size(); i++) {
Line& errline = line(i);
os << "<div class=\"" << ( (errline.error() ? "error" : "out" ) ) << "\">"
<< errline.line()
<< "</div>";
}
os
<< "</body>"
<< "</html>";
}
return errors();
}
DsoLib::DsoLib(const char* dir, const char* libname, apr_pool_t* parentPool) throw (dso_error) :
Run(dir, "/usr/bin/make", parentPool), pool_(NULL), dso_(NULL), dirname_(dir), libname_(libname)
{
if (errors()) {
cerr << "Run encountered errors, quitting DsoLib::DsoLib()" << DBGENDL;
//throw dso_error::instance("Build failed for dir %s, library %s", dir, libname);
return;
} else {
cerr << "No errors encountered with Run in DsoLib::DsoLib" << DBGENDL;
}
apr_status_t status;
if (APR_SUCCESS != apr_pool_create(&pool_, parentPool)) {
cerr << "Failed to allocate pool" << DBGENDL;
throw dso_error("Failed to allocate apr_pool");
}
cerr << "Created pool ok" << DBGENDL; //(" << __FILE__ << ":" << __LINE__ << ")" << endl;
if (APR_SUCCESS != (status = apr_dso_load(&dso_, libname, pool_))) {
cerr << "apr_dso_load(" << libname << ") failed" << DBGENDL;
char aprError[1024];
throw dso_error::instance("dso_load failed, path=%s, error=%s",
libname, apr_strerror(status, aprError, sizeof(aprError)));
}
cerr << "Loaded dso ok" << DBGENDL;
#if 0
void (*initialize)(apr_pool_t*) = reinterpret_cast< void(*)(apr_pool_t*) > (sym("initialize"));
if (initialize) {
cerr << "found initialize sym: about to call initialize" << DBGENDL;
initialize(pool_);
cerr << "initialize(pool) returned ok" << DBGENDL;
} else {
cerr << "initialize sym not found" << DBGENDL;
}
#endif
cerr << "Exiting DsoLib::DsoLib(" << dir << ", " << libname << ") with success." << endl;
}
DsoLib::~DsoLib() {
cerr << "Entering DsoLib::~DsoLib(dir=" << dirname_ <<", " << "lib=" << libname_ << ")" << endl;
if (NULL!=dso_) {
void (*terminate)(void) = reinterpret_cast<void(*)()>(sym("terminate"));
if (terminate) terminate();
apr_status_t status = apr_dso_unload(dso_);
if (APR_SUCCESS != status) {
char err[8192];
cerr << "ERR apr_dso_unload failed: " << apr_dso_error(dso_, err, sizeof(err)) << endl;
} else {
cerr << "Unloaded " << libname_ << endl;
}
} else {
cerr << "ERR dso_ handle is NULL" << endl;
}
if (NULL!=pool_) apr_pool_destroy(pool_);
}
void *
DsoLib::sym(const char* symbol) throw (dso_error) {
cerr << "sym('" << symbol << "')" << DBGENDL;
cerr << "dso_ == NULL ? " << ((NULL==dso_)?"true":"false") << DBGENDL;
cerr << "dso_ = " << dso_ << DBGENDL;
assert(NULL!=symbol);
assert(NULL!=dso_);
apr_status_t status;
void* p = NULL;
if (APR_SUCCESS != (status = apr_dso_sym((apr_dso_handle_sym_t*)&p, dso_, symbol))) {
cerr << "apr_dso_sym() DID NOT RETURN APR_SUCCESS" << DBGENDL;
char aprError[1024];
stringstream err;
err << "dso_sym failed, symbol=" << symbol << ": " << apr_strerror(status, aprError, sizeof(aprError));
cerr << err.str() << DBGENDL;
} else {
cerr << "sym succeeded for " << symbol << " in " << libname_ << DBGENDL;
}
return p;
}