开源服务器设计总计(plain framework2020年总计)
2020年注定会被历史铭记,世界遭受着一场前所未有的灾难,这种灾难到现在还在持续。还记得19年末的时候,那时候听到一点点消息,哪里想得到年关难过,灾难来的让人猝不及防。由于疫情防控,2020年感觉转瞬即逝,仿佛晃眼的功夫。本来做些自身职业上的改变,去年因为自身的原因因此搁浅。想想这几年自己维护的这个框架并未有太大的起色,甚者一度遗忘了它,所规划的很多内容并未得到实现。自己的懒惰搁浅,未免是对生命的严重浪费。今年的虽然暂时没有太多的计划,但是改变已经走在路上,希望这对自己的人生有所帮助。plain framework原本是面向游戏服务器设计的,不过在设计上尽量兼容所有的网络应用,从14年开始到现在已经六七年了,经过多次的修改,也扩充了不少的接口,在使用上更加的便捷。在这篇总结中,我主要讲述的是最近加入的控制台(console)模块,其实这个模块在许多框架中是很常见的。未来的技术日新月异,如果大家对编程上面有所兴趣,不妨可以做一定的参考,如有不足的地方也请指正,我将认真的思考其中遗留的问题。在这里我也祝愿大家在2021年,在风雨之后迎来希望的彩虹!
总结
在以前的文章中,我已经总结过plain framework的成长过程,从C++98到C++11经历了不小的变化。如今的目录结构和当初很不同,从参考到自己的一些设计,让整个框架越来越好用越来越高效是最终的目的。但是这种改变是漫长的,而且总觉得力不从心,许多的设计虽然有过临时的想法始终没能得到执行。当前这个框架现阶段如果直接用于项目中问题不大,却不能保证有所隐藏的一些BUG。今年我要做出一些改变,不单单做游戏方面的设计,我对于未来的技术也有过兴趣,如在之前的项目中使用过VUE框架来做后台的相应设计。不过相对于未来,算法始终是大方向以及技术上需要突破的,比如AI人工智能,其中的算法需要许多的数学知识,但这些相应的知识我自己要么遗忘要么还没认真接触,作为自己的兴趣后续会向这些方向研究。
人生总要有些改变,支持迟来和早来!因此我需要有段时间来放空脑袋,想一想未来的方向了。随着年龄的增长就产生了一种莫名的危机感,在职业上也有些乏味缺少新鲜,但是生活总是要继续的。一个人如果有了计划而不执行,到了没有精力和时间去做得时候,那时候再后悔未免可笑。因此我觉得一个人就得坚定地向着自己的目标走去,就算目标看来那样遥不可及,但我们可以不断改变策略,毕竟没有人随随便便就成功,但如果不去行动那么连成功的机会都没有。
希望大家都能慢慢接近自己的理想,也希望这场人类的灾难早点过去!
控制台(console)
控制台是在应用中提供调试的工具,一般情况下可以来分析应用运行过程中一些数据。在经典的操作系统中,控制台实在是太常见了,如windows中的命令行控制器,可以使用相应的命令对系统或者应用进行运行、调试和分析。
plain framework加入控制台的目的,也是为了调试以及在应用运行中做一些调试和处理,提供了比较丰富的命令接口使得外部注册命令比较容易。在服务器的设计中,很多时候想要看看应用的线程内存情况,还有网络的链接和数据的收发情况。增加控制台,有助于我们在测试的时候,对不同情况下特别是压力测试时分析出重要的数据,这有助于帮助我们对程序进行优化。目前PF 中的命令不多,主要几个常见的命令,后续会继续完善。
下图为PF中控制台的调试(包括在LINUX下的编译部分),目前仅支持网络调试(直接输入的方式很快集成,由于感觉用处不大暂时没实现):
部分代码
由于控制台需要使用网络命令行,因此在框架中增加了standard的网络协议(protocol),这个网络协议是遇到换行便将内容读出并调用注册的执行接口:
#include "pf/basic/string.h"
#include "pf/net/stream/input.h"
#include "pf/net/stream/output.h"
#include "pf/net/connection/basic.h"
#include "pf/net/connection/manager/listener.h"
#include "pf/net/protocol/standard.h" using namespace pf_basic::string;
using namespace pf_net::protocol; bool Standard::command(connection::Basic *connection, uint16_t count) {
if (connection->is_disconnect()) return false; //Leave this when not connected.
stream::Input *istream = &connection->istream();
auto line = istream->readline();
if (!line.empty()) {
if (!connection->check_safe_encrypt()) return false;
auto listener = connection->get_listener();
if (!is_null(listener)) {
rtrim(line); // Remove '\n' '\r' or other words on last.
auto callback = listener->get_standard_callback();
if (callback) callback(line, connection);
}
}
return true;
} bool Standard::send(connection::Basic *connection, packet::Interface *packet) {
return true;
}
在处理命令的时候注册的回调函数如下:
void console_net_handle(
const std::string &cmd, pf_net::connection::Basic *connection) {
using namespace pf_console;
using namespace pf_basic::string;
if (is_null(ENGINE_POINTER)) return;
auto console = ENGINE_POINTER->get_console();
if (is_null(console)) return;
if ("quit" == cmd) {
connection->exit();
return;
}
StringInput input(cmd);
NetOutput output(connection);
console->run(&input, &output);
}
整个控制台的实现目录结构如下:
控制台应用代码:
#include "pf/basic/string.h"
#include "pf/console/argv_input.h"
#include "pf/console/array_input.h"
#include "pf/console/commands/app.h"
#include "pf/console/commands/help.h"
#include "pf/console/commands/list.h"
#include "pf/basic/logger.h"
#include "pf/console/application.h" using namespace pf_console;
using namespace pf_interfaces::console;
using namespace pf_basic::string; uint8_t Application::run(Input *input, Output *output) {
std::unique_ptr<Input> input_temp;
std::unique_ptr<Output> output_temp;
if (is_null(input)) {
unique_move(Input, new ArgvInput(), input_temp);
input = input_temp.get();
}
if (is_null(output)) {
unique_move(Output, new Output(), output_temp);
output = output_temp.get();
}
configure_IO(input, output);
uint8_t exit_code{0};
try {
exit_code = do_run(input, output);
} catch (std::exception &e) {
std::cout << "Application::run get error!!!: " << e.what() << std::endl;
}
return exit_code;
} uint8_t Application::do_run(Input *input, Output *output) {
if (input->has_parameter_option({"--version", "-V"})) {
output->write_ln(get_long_version());
return 0;
}
try {
input->bind(get_definition());
} catch(...) { }
auto name = get_command_name(input);
std::unique_ptr<Input> input_temp;
if (input->has_parameter_option({"--help", "-h"}, true)) {
if (name == "") {
name = "help";
unique_move(Input,
new ArrayInput({{"command_name", default_command_name_}}),
input_temp);
input = input_temp.get();
} else {
want_helps_ = false;
}
}
if (name == "") {
name = default_command_name_;
auto definition = get_definition();
definition->set_argument(InputArgument("command",
InputArgument::kModeOptional,
definition->get_argument("command").get_description(), name));
}
Command *command{nullptr};
try {
running_command_ = nullptr;
command = find(name);
} catch (...) { }
if (is_null(command)) {
FAST_ERRORLOG(CONSOLE_MODULENAME,
"[console] (Application::run)"
" can't find the command: %s",
name.c_str());
return 1;
}
running_command_ = command;
auto exit_code = do_runcommand(command, input, output);
return exit_code;
} InputDefinition *Application::get_definition() {
if (is_null(definition_)) {
std::unique_ptr<InputDefinition> temp(new InputDefinition());
*temp = get_default_input_definition();
definition_ = std::move(temp);
if (single_command_) {
if (is_null(definition_temp_)) {
unique_move(InputDefinition, new InputDefinition(), definition_temp_);
*definition_temp_ = *definition_;
}
definition_temp_->set_arguments({});
return definition_temp_.get();
}
}
return definition_.get();
} std::string Application::get_long_version() const {
std::string r{"Console Tool"};
if (name_ != "") {
if (version_ != "") {
r = name_ + " " + version_;
}
r = name_;
}
return r;
} Command *Application::add(Command *command) {
// std::cout << "Add command: " << command->name() << std::endl;
if (!command->is_enabled()) {
return nullptr;
}
init();
command->set_application(this);
// Will throw if the command is not correctly initialized.
command->get_definition();
command->configure();
auto name = command->name();
if (name == "") {
throw std::logic_error("command cannot have an empty name.");
}
if (commands_.find(name) != commands_.end()) {
return commands_[name].get();
}
std::unique_ptr<Command> temp;
unique_move(Command, command, temp);
commands_[name] = std::move(temp);
for (auto const &alias : command->get_aliases()) {
command_aliases_[alias] = name;
}
return command;
} Command *Application::get(const std::string &_name) {
auto name = get_command_real_name(_name);
if ("" == name) {
std::string e = "The command \"" + _name + "\" does not exist.";
throw std::invalid_argument(e);
}
if (commands_.find(name) == commands_.end()) return nullptr;
auto command = commands_[name].get();
if (want_helps_) {
want_helps_ = false;
auto help_command = get("help");
help_command->set_command(command);
return help_command;
}
return command;
} std::vector<std::string> Application::get_namespaces() {
std::vector<std::string> r;
auto commands = all();
for (auto it = commands.begin(); it != commands.end(); ++it) {
if (it->second->is_hidden()) continue;
auto temp = extract_all_namespace(it->first);
for (const auto &one : temp) r.emplace_back(one);
for (const auto &alias : it->second->get_aliases()) {
auto temp1 = extract_all_namespace(alias);
for (const auto &one : temp1) r.emplace_back(one);
}
}
// * The result maybe need use array_unique to remove the same values.
return r;
} std::string Application::find_namespace(const std::string &_namespace) {
return "";
} Command *Application::find(const std::string &name) {
init();
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
if (!is_null(it->second)) {
for (const auto &alias : it->second->get_aliases()) {
if ("" == command_aliases_[alias])
command_aliases_[alias] = it->second->name();
}
} else {
std::cout << "find no command: " << it->first << std::endl;
}
}
return get(name);
} std::map<std::string, Command *>
Application::all(const std::string &_namespace) {
std::map<std::string, Command *> r;
init();
if ("" == _namespace) {
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
r[it->first] = it->second.get();
}
return r;
}
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
if (_namespace == extract_namespace(it->first)) {
r[it->first] = it->second.get();
}
}
return r;
} Application &Application::set_default_command(
const std::string &name, bool is_single_command) {
default_command_name_ = name;
if (is_single_command) {
// Ensure the command exist
find(name);
single_command_ = true;
}
return *this;
} uint8_t Application::do_runcommand(
Command *command, Input *input, Output *output) {
// std::cout << "do_runcommand: " << command->name() << std::endl;
return command->run(input, output);
} std::string Application::get_command_name(Input *input) const {
return single_command_ ? default_command_name_ : input->get_first_argument();
} InputDefinition Application::get_default_input_definition() const {
std::vector<InputParameter *> p;
std::unique_ptr<InputParameter> p1(new InputArgument(
"command", InputParameter::kModeRequired,
"The command to execute", ""
));
p.emplace_back(p1.get());
std::unique_ptr<InputParameter> p2(new InputOption(
"--help", "-h", InputParameter::kModeNone,
"Display help for the given command. When no command"
" is given display help for the" + default_command_name_ + "command", ""
));
p.emplace_back(p2.get());
std::unique_ptr<InputParameter> p3(new InputOption(
"--quiet", "-q", InputParameter::kModeNone,
"Do not output any message", ""
));
p.emplace_back(p3.get());
std::unique_ptr<InputParameter> p4(new InputOption(
"--verbose", "-v|vv|vvv", InputParameter::kModeNone,
"Increase the verbosity of messages: 1 for normal output, "
"2 for more verbose output and 3 for debug", ""
));
p.emplace_back(p4.get());
std::unique_ptr<InputParameter> p5(new InputOption(
"--version", "-V", InputParameter::kModeNone,
"Display this application version", ""
));
p.emplace_back(p5.get());
std::unique_ptr<InputParameter> p6(new InputOption(
"--ansi", "", InputParameter::kModeNone,
"Force ANSI output", ""
));
p.emplace_back(p6.get());
std::unique_ptr<InputParameter> p7(new InputOption(
"--no-ansi", "", InputParameter::kModeNone,
"Disable ANSI output", ""
));
p.emplace_back(p7.get());
std::unique_ptr<InputParameter> p8(new InputOption(
"--no-interaction", "-n", InputParameter::kModeNone,
"Do not ask any interactive question", ""
));
p.emplace_back(p8.get()); return InputDefinition(p);
} std::vector<Command *> Application::get_default_commands() const {
std::vector<Command *> r;
return r;
} std::string Application::get_abbreviation_suggestions(
const std::vector<std::string> &abbrevs) const {
return "";
} std::vector<std::string> Application::find_alternatives(
const std::string &name, const std::vector<std::string> &collection) const {
return {};
} void Application::configure_IO(Input *input, Output *output) {
if (input->has_parameter_option({"--ansi"}, true)) {
output->set_decorated(true);
} else if (input->has_parameter_option({"'--no-ansi'"}, true)) {
output->set_decorated(false);
}
} std::vector<std::string> Application::extract_all_namespace(
const std::string &name) {
std::vector<std::string> r;
std::vector<std::string> parts;
explode(name.c_str(), parts, ":", true, true);
for (const auto &part : parts) {
if (r.size() > 0) {
std::string temp = r[r.size() - 1] + ":" + part;
r.emplace_back(temp);
} else {
r.emplace_back(part);
}
}
return r;
} std::string Application::extract_namespace(
const std::string &name, int32_t limit) const {
return "";
} void Application::init() {
if (initialized_) return;
initialized_ = true;
// std::cout << "Application::init" << std::endl;
add(new commands::Help());
add(new commands::List());
add(new commands::App());
}
命令实现代码:
#include "pf/basic/string.h"
#include "pf/console/argv_input.h"
#include "pf/console/array_input.h"
#include "pf/console/commands/app.h"
#include "pf/console/commands/help.h"
#include "pf/console/commands/list.h"
#include "pf/basic/logger.h"
#include "pf/console/application.h" using namespace pf_console;
using namespace pf_interfaces::console;
using namespace pf_basic::string; uint8_t Application::run(Input *input, Output *output) {
std::unique_ptr<Input> input_temp;
std::unique_ptr<Output> output_temp;
if (is_null(input)) {
unique_move(Input, new ArgvInput(), input_temp);
input = input_temp.get();
}
if (is_null(output)) {
unique_move(Output, new Output(), output_temp);
output = output_temp.get();
}
configure_IO(input, output);
uint8_t exit_code{0};
try {
exit_code = do_run(input, output);
} catch (std::exception &e) {
std::cout << "Application::run get error!!!: " << e.what() << std::endl;
}
return exit_code;
} uint8_t Application::do_run(Input *input, Output *output) {
if (input->has_parameter_option({"--version", "-V"})) {
output->write_ln(get_long_version());
return 0;
}
try {
input->bind(get_definition());
} catch(...) { }
auto name = get_command_name(input);
std::unique_ptr<Input> input_temp;
if (input->has_parameter_option({"--help", "-h"}, true)) {
if (name == "") {
name = "help";
unique_move(Input,
new ArrayInput({{"command_name", default_command_name_}}),
input_temp);
input = input_temp.get();
} else {
want_helps_ = false;
}
}
if (name == "") {
name = default_command_name_;
auto definition = get_definition();
definition->set_argument(InputArgument("command",
InputArgument::kModeOptional,
definition->get_argument("command").get_description(), name));
}
Command *command{nullptr};
try {
running_command_ = nullptr;
command = find(name);
} catch (...) { }
if (is_null(command)) {
FAST_ERRORLOG(CONSOLE_MODULENAME,
"[console] (Application::run)"
" can't find the command: %s",
name.c_str());
return 1;
}
running_command_ = command;
auto exit_code = do_runcommand(command, input, output);
return exit_code;
} InputDefinition *Application::get_definition() {
if (is_null(definition_)) {
std::unique_ptr<InputDefinition> temp(new InputDefinition());
*temp = get_default_input_definition();
definition_ = std::move(temp);
if (single_command_) {
if (is_null(definition_temp_)) {
unique_move(InputDefinition, new InputDefinition(), definition_temp_);
*definition_temp_ = *definition_;
}
definition_temp_->set_arguments({});
return definition_temp_.get();
}
}
return definition_.get();
} std::string Application::get_long_version() const {
std::string r{"Console Tool"};
if (name_ != "") {
if (version_ != "") {
r = name_ + " " + version_;
}
r = name_;
}
return r;
} Command *Application::add(Command *command) {
// std::cout << "Add command: " << command->name() << std::endl;
if (!command->is_enabled()) {
return nullptr;
}
init();
command->set_application(this);
// Will throw if the command is not correctly initialized.
command->get_definition();
command->configure();
auto name = command->name();
if (name == "") {
throw std::logic_error("command cannot have an empty name.");
}
if (commands_.find(name) != commands_.end()) {
return commands_[name].get();
}
std::unique_ptr<Command> temp;
unique_move(Command, command, temp);
commands_[name] = std::move(temp);
for (auto const &alias : command->get_aliases()) {
command_aliases_[alias] = name;
}
return command;
} Command *Application::get(const std::string &_name) {
auto name = get_command_real_name(_name);
if ("" == name) {
std::string e = "The command \"" + _name + "\" does not exist.";
throw std::invalid_argument(e);
}
if (commands_.find(name) == commands_.end()) return nullptr;
auto command = commands_[name].get();
if (want_helps_) {
want_helps_ = false;
auto help_command = get("help");
help_command->set_command(command);
return help_command;
}
return command;
} std::vector<std::string> Application::get_namespaces() {
std::vector<std::string> r;
auto commands = all();
for (auto it = commands.begin(); it != commands.end(); ++it) {
if (it->second->is_hidden()) continue;
auto temp = extract_all_namespace(it->first);
for (const auto &one : temp) r.emplace_back(one);
for (const auto &alias : it->second->get_aliases()) {
auto temp1 = extract_all_namespace(alias);
for (const auto &one : temp1) r.emplace_back(one);
}
}
// * The result maybe need use array_unique to remove the same values.
return r;
} std::string Application::find_namespace(const std::string &_namespace) {
return "";
} Command *Application::find(const std::string &name) {
init();
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
if (!is_null(it->second)) {
for (const auto &alias : it->second->get_aliases()) {
if ("" == command_aliases_[alias])
command_aliases_[alias] = it->second->name();
}
} else {
std::cout << "find no command: " << it->first << std::endl;
}
}
return get(name);
} std::map<std::string, Command *>
Application::all(const std::string &_namespace) {
std::map<std::string, Command *> r;
init();
if ("" == _namespace) {
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
r[it->first] = it->second.get();
}
return r;
}
for (auto it = commands_.begin(); it != commands_.end(); ++it) {
if (_namespace == extract_namespace(it->first)) {
r[it->first] = it->second.get();
}
}
return r;
} Application &Application::set_default_command(
const std::string &name, bool is_single_command) {
default_command_name_ = name;
if (is_single_command) {
// Ensure the command exist
find(name);
single_command_ = true;
}
return *this;
} uint8_t Application::do_runcommand(
Command *command, Input *input, Output *output) {
// std::cout << "do_runcommand: " << command->name() << std::endl;
return command->run(input, output);
} std::string Application::get_command_name(Input *input) const {
return single_command_ ? default_command_name_ : input->get_first_argument();
} InputDefinition Application::get_default_input_definition() const {
std::vector<InputParameter *> p;
std::unique_ptr<InputParameter> p1(new InputArgument(
"command", InputParameter::kModeRequired,
"The command to execute", ""
));
p.emplace_back(p1.get());
std::unique_ptr<InputParameter> p2(new InputOption(
"--help", "-h", InputParameter::kModeNone,
"Display help for the given command. When no command"
" is given display help for the" + default_command_name_ + "command", ""
));
p.emplace_back(p2.get());
std::unique_ptr<InputParameter> p3(new InputOption(
"--quiet", "-q", InputParameter::kModeNone,
"Do not output any message", ""
));
p.emplace_back(p3.get());
std::unique_ptr<InputParameter> p4(new InputOption(
"--verbose", "-v|vv|vvv", InputParameter::kModeNone,
"Increase the verbosity of messages: 1 for normal output, "
"2 for more verbose output and 3 for debug", ""
));
p.emplace_back(p4.get());
std::unique_ptr<InputParameter> p5(new InputOption(
"--version", "-V", InputParameter::kModeNone,
"Display this application version", ""
));
p.emplace_back(p5.get());
std::unique_ptr<InputParameter> p6(new InputOption(
"--ansi", "", InputParameter::kModeNone,
"Force ANSI output", ""
));
p.emplace_back(p6.get());
std::unique_ptr<InputParameter> p7(new InputOption(
"--no-ansi", "", InputParameter::kModeNone,
"Disable ANSI output", ""
));
p.emplace_back(p7.get());
std::unique_ptr<InputParameter> p8(new InputOption(
"--no-interaction", "-n", InputParameter::kModeNone,
"Do not ask any interactive question", ""
));
p.emplace_back(p8.get()); return InputDefinition(p);
} std::vector<Command *> Application::get_default_commands() const {
std::vector<Command *> r;
return r;
} std::string Application::get_abbreviation_suggestions(
const std::vector<std::string> &abbrevs) const {
return "";
} std::vector<std::string> Application::find_alternatives(
const std::string &name, const std::vector<std::string> &collection) const {
return {};
} void Application::configure_IO(Input *input, Output *output) {
if (input->has_parameter_option({"--ansi"}, true)) {
output->set_decorated(true);
} else if (input->has_parameter_option({"'--no-ansi'"}, true)) {
output->set_decorated(false);
}
} std::vector<std::string> Application::extract_all_namespace(
const std::string &name) {
std::vector<std::string> r;
std::vector<std::string> parts;
explode(name.c_str(), parts, ":", true, true);
for (const auto &part : parts) {
if (r.size() > 0) {
std::string temp = r[r.size() - 1] + ":" + part;
r.emplace_back(temp);
} else {
r.emplace_back(part);
}
}
return r;
} std::string Application::extract_namespace(
const std::string &name, int32_t limit) const {
return "";
} void Application::init() {
if (initialized_) return;
initialized_ = true;
// std::cout << "Application::init" << std::endl;
add(new commands::Help());
add(new commands::List());
add(new commands::App());
}
[viticm@izuf633l0ge76tbdctmljaz core]$ cat /home/viticm/develop/github/plain/framework/core/src/console/command.cc
#include "pf/support/helpers.h"
#include "pf/console/application.h"
#include "pf/console/command.h" using namespace pf_support;
using namespace pf_console; // Static member must be initialized.
std::string Command::default_name_{"unknown"}; void Command::merge_application_definition(bool merge_args) {
if (is_null(app_) or !is_null(full_definition_)) return;
unique_move(InputDefinition, new InputDefinition(), full_definition_);
full_definition_->set_options(array_values(definition_->get_options()));
full_definition_->add_options(
array_values(app_->get_definition()->get_options()));
if (merge_args) {
full_definition_->set_arguments(
array_values(app_->get_definition()->get_arguments()));
full_definition_->add_arguments(array_values(definition_->get_arguments()));
} else {
full_definition_->set_arguments(array_values(definition_->get_arguments()));
}
} uint8_t Command::run(Input *input, Output *output) {
uint8_t r{0};
merge_application_definition();
// bind the input against the command specific arguments/options
try {
input->bind(get_definition(), is_parse_input());
} catch (std::exception &e) {
if (!ignore_validation_errors_)
throw std::runtime_error(e.what());
}
initialize(input, output); // Set process title.
if (!process_title_.empty()) { }
if (input->is_interactive()) {
interact(input, output);
} // The command name argument is often omitted when a command is executed
// directly with its run() method.
// It would fail the validation if we didn't make sure the command argument
// is present, since it's required by the application.
if (input->has_argument("command") && input->get_argument("command") == "") {
input->set_argument("command", name_);
} input->validate(); if (code_) {
r = code_(input, output);
} else {
r = execute(input, output);
} return r;
}
更多
可以在github上找到完整的项目:https://github.com/viticm/plain
开源服务器设计总计(plain framework2020年总计)的更多相关文章
- 转:Nginx+ffmpeg的HLS开源服务器搭建配置及开发详解
转:http://itindex.net/detail/51186-nginx-ffmpeg-hls 本文概述: 至目前为止,HLS 是移动平台上非常重要并十分流行的流媒体传输协议.做移动平台的流媒体 ...
- DNS开源服务器BIND最小配置详解<转>
一,简介 相对于存储和大数据领域,CDN是一个相对小的领域,但行行出状元,BIND就是CDN领域的蝉联N届的状元郎.BIND是一款非常常用的DNS开源服务器,全球有90%的DNS用BIND实现.值得一 ...
- DNS开源服务器BIND最小配置详解
一,简介 相对于存储和大数据领域,CDN是一个相对小的领域,但行行出状元,BIND就是CDN领域的蝉联N届的状元郎.BIND是一款非常常用的DNS开源服务器,全球有90%的DNS用BIND实现.值得一 ...
- Nginx+ffmpeg的HLS开源服务器搭建配置及开发详
本文概述: 至目前为止,HLS 是移动平台上非常重要并十分流行的流媒体传输协议.做移动平台的流媒体开发,不知道它不掌握它 ,真是一大遗憾.而HLS的平台搭建有一定的难度,本文针对对该方向有一定了解的朋 ...
- 基于内存,redis,mysql的高速游戏数据服务器设计架构
转载请注明出处,欢迎大家批评指正 1.数据服务器详细设计 数据服务器在设计上采用三个层次的数据同步,实现玩家数据的高速获取和修改. 数据层次上分为:内存数据,redis数据,mysql数据 设计目的: ...
- H2Engine游戏服务器设计之属性管理器
游戏服务器设计之属性管理器 游戏中角色拥有的属性值很多,运营多年的游戏,往往会有很多个成长线,每个属性都有可能被N个成长线模块增减数值.举例当角色戴上武器时候hp+100点,卸下武器时HP-100点, ...
- 游戏服务器设计之NPC系统
游戏服务器设计之NPC系统 简介 NPC系统是游戏中非常重要的系统,设计的好坏很大程度上影响游戏的体验.NPC在游戏中有如下作用: 引导玩家体验游戏内容,一般游戏内有很多主线.支线任务,而任务的介绍. ...
- h2engine游戏服务器设计之聊天室示例
游戏服务器设计之聊天室示例 简介 h2engine引擎建群以后,有热心网友向我反馈,想尝试h2engine但是没有服务器开发经验觉得无从入手,希望我能提供一个简单明了的示例.由于前一段时间工作实在忙碌 ...
- 基于内存,redis,mysql的高速游戏数据服务器设计架构 ZT
zt http://www.cnblogs.com/captainl1993/p/4788236.html 1.数据服务器详细设计 数据服务器在设计上采用三个层次的数据同步,实现玩家数据的高速获取和 ...
随机推荐
- .NET微服务最佳实践 eShopOnContainers
本文翻译自微软Docs, 内嵌译者多年使用的参悟,如理解有误,请不吝赐教. 微软与社区专家合作,开发了功能齐全的云原生微服务示例应用eShopOnContainers. 该应用旨在展示使用.NET.D ...
- .net实现filestream类复制文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...
- HashMap扩容后是否需要rehash?
需要,因为要重新计算旧数组元素在新数组地址.HashMap在JDK1.8中的rehash算法(也就是扩容后重新为里面的键值对寻址的算法)进行优化.hash寻址算法是 index =(n - 1) &a ...
- yum install valgrind.x86_64
Reference: https://cloudlinux.zendesk.com/hc/en-us/articles/115004075294-Fix-rpmdb-Thread-died-in-Be ...
- MySQL like查询使用索引
在使用msyql进行模糊查询的时候,很自然的会用到like语句,通常情况下,在数据量小的时候,不容易看出查询的效率,但在数据量达到百万级,千万级的时候,查询的效率就很容易显现出来.这个时候查询的效率就 ...
- 后端程序员之路 15、Matplotlib
Matplotlib: Python plotting - Matplotlib 2.0.0 documentationhttp://matplotlib.org/ matplotlib-绘制精美的图 ...
- SSRF攻击原理
目录 什么是SSRF 原理 防护 什么是SSRF 一个对外的Web接口,改接口能让用户控制curl命令,去访问别的web服务. 简图如下 想象一下当用户请求的baidu.com/x.php?image ...
- 【pytest官方文档】解读fixtures - 1.什么是fixtures
在深入了解fixture之前,让我们先看看什么是测试. 一.测试的构成 其实说白了,测试就是在特定的环境.特定的场景下.执行特定的行为,然后确认结果与期望的是否一致. 就拿最常见的登录来说,完成一次正 ...
- PTA甲级—常用技巧与算法
散列 1078 Hashing (25 分) Quadratic probing (with positive increments only) is used to solve the collis ...
- C#的常见集合接口提供的功能
C#的常见集合接口提供的功能 这里的功能都是泛型版本的常见功能,列出来,也许后面用得上吧,没有放非泛型版本,因为觉得用得不多,也就没有整理 IEnumerable<T> ICollecti ...