一个关键问题:你为什么要以这种方式实现它?您正在“紧密”耦合本质上“松散”耦合的组件(因为共享库存在各种与崩溃相关的问题:它们会使管理器崩溃)。为什么不拥有一个(可以)在必要时启动和重新启动 2 个或更多子进程的管理器程序。
让子进程使用某种协议与 Manager 或彼此进行通信。我推荐ZeroMQ既因为它很棒,又因为它完全隐藏了进程间通信,所以它可以是套接字(不同机器之间),或者线程之间的进程间,或者单个机器上的命名管道,这非常快。这意味着在实现客户端之后,您可以决定如何部署它们:作为加载到管理器中的共享库、作为运行在同一机器上的单独进程或作为运行在不同机器上的分布式进程,您几乎不需要改变任何东西。这意味着非常可扩展。
但是,如果您致力于共享库方法,那么我绝对会推荐一种“设计模式”,尽管实现起来可能有点棘手。但这将是值得的。
你的经理,在模块之间传递消息之前,应该检查它们的时间戳,如果有任何改变,重新编译并重新加载它们。这意味着您的代码更改是“热的”:您不必停止管理器、重新编译并重新启动管理器即可查看更改。所以你可以用 C 编程更像用 js 开发!它将为您节省数小时。
不久前,我使用 C++ 和 APR 做了类似的事情(不是图书馆间通信)。代码有点“hacky”,但无论如何都在这里;-)
请注意,它取决于Makefile
每个子模块在其自己的目录中都有一个,并且由于依赖关系,我不检查时间戳,我只是在每个请求上重新编译。这可能不适合您,因此您可能需要重新考虑该部分。
让它工作最困难的部分是获得对目录的正确权限,但仔细想想,那是因为我将它作为 fcgi 进程运行,所以当它实际运行时,它是作为网络服务器运行的。您很可能不会遇到这些问题。
#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
并运行.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;
}