[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user Техника

In order to coordinate rabbitmq with alice, I’m setting the cookie for both on the command line with ‘-setcookie 123’. This seems to work fine.

Why is it looking for the cookie there?

Only root or rabbitmq should run rabbitmq-multi

But if you just want to drop privileges, that happens by default.

—Simon MacMullenStaff Engineer, RabbitMQSpringSource, a division of VMware

Пытаюсь запустить мой сервер rabbitmq, но я получаю эту ошибку. Я следовал всем инструкциям по его установке. rabbitmq-server файл живет внутри /usr/local/sbin, Поэтому я меняю каталог на sbin, и писать rabbitmq-server в моем терминале, но это показывает ошибку -bash: rabbitmq-server: command not found, Я уже добавил следующую строку: export PATH=/usr/local/sbin:$PATH в нижней части моего bash_profileсогласно инструкции. Есть идеи, почему это не работает?

Этот вопрос был задан ранее ( RabbitMQ не удалось инициализировать распределение erlang), но я не смог выяснить, как это исправить из существующих ответов. После https://www.rabbitmq.com/man/rabbitmqctl.1.man.html я хотел бы запустить rabbitmqctl status command to display information about the RabbitMQ broker. However, I get an error message:

Only root or rabbitmq should run rabbitmqctl

Если я запускаю команду с sudo, I still get an error message:

Как я могу это исправить?

Overview

RabbitMQ ships with multiple command line tools, each with a set of related commands:

they can be found under the directory in installation root.

On Windows, the above tool names will end with , e.g. in a Windows installation will
be named .

Additional tools are optional and can be obtained from GitHub:

Even though CLI tools ship with the server, most commands can be used to operate on remote nodes.
Plugins can provide CLI commands that will be discovered by CLI tools for explicitly enabled plugins.

August 3 2015, 17:03

Дано: нода с RabbitMQ, которую крайне неаккуратно потушили. Нода не стартует, на любые попытки стартовать выдаёт нечто невразумительное вроде:

И тому подобное. В логах в лучшем случае будет что-то типа:

Crash dump was written to: erl_crash.dump
init terminating in do_boot (timeout_waiting_for_tables)

При этом никакой rabbitmqctl force_boot и тому подобное не поможет. Делаем следующее. Идём в /var/lib/rabbitmq/mnesia и копируем базу оттуда в сухое, тёплое место. Старую базу в mnesia/ удаляем. Далее, выполняем: service rabbitmq-server restart. Именно перезапуск, а не старт и не останов. Иначе не поднимается демон. Далее, проверяем ps-ом, что кролик стартанул. После чего делаем: systemctl rabbitmq-server stop. Далее, возвращаем базу на место, удалив то, что RabbitMQ сгенерил в каталоге mnesia/ при повторном перезапуске. Далее, как обычно: service rabbitmq-server start. Если всё сделано верно, то нода в рабочем состоянии появится в management-консоли.

Matt Pietrekmpietrek at skytap.com
Tue Dec 20 20:06:14 GMT 2011

NAME

— tool
for managing RabbitMQ nodes

SYNOPSIS

RabbitMQ is an open source multi-protocol messaging broker.

is the main command line tool
for managing a RabbitMQ server node, together with
,
, and others.

Diagnostic information is displayed if connection failed, the
target node was not running, or could
not authenticate to the target node successfully.

OPTIONS

Quiet output mode is selected. Informational messages are reduced when
quiet mode is in effect.

Silent output mode is selected. Informational messages are reduced and
table headers are suppressed when silent mode is in effect.Do not output headers for tabular data.Do not run the command. Only print information message.

timeout,
timeout

Operation timeout in seconds. Not all commands support timeouts. Default
is .

PLUGIN
COMMANDS

Prints a list of configured Shovels

Federation
plugin

Prints a list of federation links.

Only list federation links which are not running.

AMQP
1. 0 plugin

Это вторая часть руководства по созданию инструмента для скрапинга веб-страниц с помощью Python. Мы будем использовать интеграцию Celery и системы управления задачами.

В части 1 «Создание скрапера RSS-каналов с помощью Python» показано, как можно использовать Requests и Beautiful Soup.

В части 3 этой серии статей «Создание приложения для скрапинга веб-страниц с помощью Python, Celery и Django я продемонстрирую, как интегрировать инструмент для скрапинга веб-страниц в приложения.

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

В предыдущей статье я создал простую программу скрапинга RSS-каналов, которая извлекает информацию с помощью и (смотрите код на GitHub). Теперь мы будем использовать этот код как основу для создания системы управления задачами и запланированного скрапинга.

Следующим логическим шагом в скрапинге данных с веб-сайтов, которые часто меняются (то есть RSS-канала, отображающего X элементов за раз), является регулярный скрапинг. В предыдущем примере парсинга мы использовали командную строку для выполнения кода по команде. Однако это не масштабируемое решение. Чтобы автоматизировать его, мы добавим для создания системы очереди задач с периодом выполнения.

Я буду использовать следующие инструменты:

Примечание. Все зависимости библиотеки перечислены в файлах requirements.txtи Pipfile / Pipfile.lock.

Celery — это система управления задачами, она работает совместно с для выполнения .

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Выше показано, что инициатор задач (наше приложение для скрапинга веб-страниц) передает информацию о задаче в очередь (Celery) для выполнения. Планировщик (Celery beat) выполняет их как задачи cron, без какой-либо дополнительной обработки или взаимодействий вне запуска приложения Celery.

Вот схема шагов, которые мы предпримем для создания окончательного проекта:

Мы начнем с открытия каталога предыдущего проекта, в данном случае это web_scraping_example из предыдущей статьи. Если хотите, его можно клонировать с GitHub.

Кроме того, требования к проекту могут задавать для этой установки использование pip, как в примере, приведенном ниже.

Мы используем Celery и RabbitMQ, потому что они довольно просты в настройке, тестировании и масштабировании в производственной среде. Хотя мы могли бы выполнять периодические задачи с помощью других библиотек, или просто заданий cron, в целом, я хотел, чтобы в следующей статье этой серии мы основывались на этом.

Дополнительно:  Не работает тачпад на ноутбуке Асер

В долгосрочной перспективе нам будет намного проще, если мы будем использовать что-то, что сможем масштабировать в следующем проекте, а также изучим некоторые ключевые команды и инструменты по мере постепенного увеличения сложности.

Настроить и запустить сервер RabbitMQ в Ubuntu значительно проще, чем в операционной системе Windows. Я буду следовать официальному руководству по установке.

Ниже приведены команды установки для Debian, Ubuntu.

Когда я впервые установил RabbitMQ в виртуальной среде, он запустился автоматически. Чтобы проверить, что команда rabbitmq-server работает (ее мы будем использовать при работе с Celery), мне пришлось закрыть службу.

Я также заметил, что в разрешениях по умолчанию для установки указано Only root or rabbitmq should run rabbitmqctl shutdown, что мне показалось странным. Вместо того, чтобы решить эту проблему, я решил просто запустить sudo.

$ sudo rabbitmqctl shutdown

Затем я смог протестировать сервер, используя rabbitmq-server, я привожу команду и вывод ниже.

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

К вашему сведению — вы можете завершить команду rabbitmq-server, используя клавиатурную комбинацию Ctrl + C.

Для настройки RabbitMQ в операционной системе Windows требуются дополнительные действия. В официальной документации есть руководство по ручной установке.

В данном случае работа с Celery будет сопровождаться их собственным подтверждением концепции «Hello World» в виде задачи, выполняющей базовое добавление. Оно доступно в официальной документации Celery. Я собираюсь вкратце проиллюстрировать его. Однако, если вам нужны пояснения или подробный обзор, пожалуйста, ознакомьтесь с официальной документаций.

Теперь, когда у нас установлен и проверен брокер RabbitMQ, можно приступить к созданию файла tasks.py. Он будет содержать задачи, которые мы будем выполнять, будь то добавление, скрапинг веб-страниц или сохранение пользователей в базе данных. Теперь я внесу изменения в каталог проекта.

Используя add этой задачи, мы можем начать тестирование исполнения. Здесь все может немного запутаться, так как на следующем шаге у меня будут одновременно открыты три терминала.

Я начну с краткого объяснения, затем углублюсь в код и предоставлю снимки экрана.

Чтобы завершить тест, мы будем выполнять задачу Celery с помощью командной строки, импортировав файл tasks.py и вызвав его. Чтобы задачи были в очередь, нам нужно, чтобы Celery worker и сервисы RabbitMQ были активными. Сервер RabbitMQ будет действовать как брокер сообщений, в то время как Celery worker будет выполнять задачи.

Я буду обозначать каждый шаг номерами терминалов:

Мы начнем с запуска сервера RabbitMQ в терминале №1.

# RabbitMQ
$ sudo rabbitmq-server

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Запуск сервера RabbitMQ

Впоследствии мы можем начать процесс Celery worker в терминале №2. Я добавил подробные настройки для worker, чтобы проиллюстрировать, как будет выглядеть результат.

Примечание: это необходимо выполнить из каталога проекта.

# Celery worker
$ celery worker -A tasks -l INFO

Разберем приведенную выше команду:

Чтобы проверить, правильно ли загружается worker, найдите в терминале строку concurrency: 4 (prefork).

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Запуск Celery worker с подробной информацией

Затем мы можем начать выполнение теста в терминале №3. Я буду выполнять цикл, чтобы проиллюстрировать, что служба worker перехватывает несколько задач. Мы добьемся этого, введя add из файла tasks.py, а затем выполнив цикл for. После строки add.delay(i, i) вам нужно будет использовать клавиатурную комбинацию Ctrl + Enter длявыполнения команды.

Теперь вы должны увидеть большой блок вывода в терминале № 3 (выполнение задачи Celery). Это продемонстрирует, что worker результат задачи от терминала №2.

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Запуск выполнения задачи Celery

Если мы проверим Celery worker в терминале № 2, процесс, выполняющий задачу add, мы увидим, что он перехватывает каждое из выполнений задачи.

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

, получающий и выполняющий задачи

Теперь мы доказали, что Celery и RabbitMQ установлены правильно. Это помогает заложить основу для других задач, которые мы будем реализовывать (например, скрапинг веб-страниц), демонстрируя, как взаимодействуют Celery, Celery worker и RabbitMQ.

Теперь, когда мы рассмотрели установку и основы, мы перейдем к файлу tasks.py, чтобы создать задачи скрапинга веб-страниц.

Приведенный выше пример помог проверить процесс, который мы будем использовать для выполнения задач с помощью Celery, а также продемонстрировал, как задачи регистрируются с помощью Celery worker.

Основываясь на приведенном выше примере, мы начнем с создания задач скрапинга. Сейчас я собираюсь отказаться от файла scraping.py, так как он будет просто скопирован в файл tasks.py для простоты.

Я начну с удаления из примера функции def add(x, y) и копирования зависимостей (Requests и BeautifulSoup) вместе с самими функциями.

Примечание: я буду использовать функции, но в файле tasks.py.

Хотя функции скрапинга, которые мы определили, доказали свою эффективность для извлечения данных из RSS-канала, у нас все еще есть возможности для улучшения. Ниже я в общих чертах обрисовал, что мы будем изменять в наборе инструментов для скрапинга, прежде чем автоматизируем его.

Упомянутые выше изменения невелики, так как мы уже выполнили основную часть работы в рамках первой статьи о реализации. Хотя это несущественное изменение, .json будет читать немного удобнее, чем .txt. Два дополнительных столбца также помогут сделать его более «масштабируемым» при добавлении других каналов, а также при последующем анализе данных.

Давайте начнем с save_function: обновим ее для вывода файла .json и добавим временную метку, чтобы улучшить качество при обращении к ранее извлеченным данным.

Переходя к двум изменениям в функции hackernews_rss() — мы добавим некоторую информацию об источнике и отметку времени created_at. Это два простых изменения, которые помогут нам оставаться в курсе, если мы добавим дополнительные функции скрапинга.

Указанные выше изменения иллюстрируют добавление столбцов created_at и source. К вашему сведению — если вы опустите переход str(), вы не сможете его выполнить из-за Object of type datetime is not JSON serializable.

Теперь мы будем использовать возможности Celery по планированию задач, опираясь на то, что beat_schedule поставляется из коробки. Это позволяет нам регистрировать задачи на определенное время с помощью агента планирования.

Отличное описание примеров планирования можно найти в официальной документации. Я также включил несколько дополнительных примеров расписания в файле tasks.py в.

Я собираюсь выполнять задачу скрапинга каждую минуту, так как это продемонстрирует, как Celery worker взаимодействует с запланированными задачами. Это не приведет к разнице в данных, поскольку используемый нами RSS-канал не обновляется ежеминутно.

Моя цель в этой демонстрации — показать выходные файлы статей и простое расписание задач.

Приведенная выше конфигурация будет регистрировать расписание задач в самом приложении Celery. После запуска мы сможем вызвать расписание Celery, чтобы дважды проверить, что он поставил в очередь.

Теперь, когда расписание создано, пришло время включить сервер RabbitMQ и запустить процессы Celery worker.

В этом примере мы будем использовать две вкладки терминала:

Чтобы запустить сервер RabbitMQ (наш брокер сообщений), мы будем использовать ту же команду, что и раньше.

Дополнительно:  Не запускается безопасный режим Windows 7: что делать

Примечание: мне также нравится дважды проверять, что узел, созданный при запуске, выключен, поскольку он не запускается с журналом и выдает ошибку, если мы сначала не отключим его.

$ sudo rabbitmqctl shutdown
$ sudo rabbitmq-server

Теперь вы должны видеть результат, аналогичный предыдущему (смотрите скриншот экрана, приведенный ниже).

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

После запуска сервера RabbitMQ мы можем начать с терминала №2.

Мы немного изменим команду, так как теперь она будет включать в себя обозначение -Bдля того, какие вызовы worker выполняет расписание.

$ celery -A tasks worker -B -l INFO

Вывод консоли будет иллюстрировать запуск приложения и (в зависимости от того, какое у вас расписание) будет выводить информацию о выполнении задачи.

На скриншоте ниже:

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Выполнение запланированной задачи

К вашему сведению — мы можем остановить выполнение запланированной задачи с помощью клавиатурной комбинации Ctrl + C, так как это будет работать бесконечно.

Теперь, когда мы успешно выполнили задачу save_function(), созданный ранее файл вывел файл .json.

[rabbitmq-discuss] Unable RabbitMQ Server as no-root user

Вывод .json файла задачи

Мы успешно расширили наш простой инструмент для скрапинга веб-страниц, чтобы создать расписание. Это гарантирует, что нам больше не нужно вручную выполнять задачи скрапинга, и мы можем просто «включить и оставить». После планировки задач, проект сможет скрапить сайты на предмет наличия данных, которые изменяются по заданному расписанию (скажем, каждые 15 минут), и каждый раз возвращать новые данные.

В третьей части этой серии статей я продемонстрирую приложение Django с интеграцией Celery и скрапингом веб-страниц. Это будет отличный пример веб-приложения, которое извлекает данные на сайт и заполняет его информацией. Нашим конечным продуктом будет агрегатор новостей, который будет извлекать информацию сразу из нескольких RSS-каналов.

Options and Positional Arguments

A specific example:

The explicit positional argument separator must be used when positional arguments begin with a hyphen or a double
hyphen (such as generated passwords), to make sure they are not parsed as options:

Option values can be passed as or . The latter variant must be used
when the value begins with a hyphen (), otherwise it would be treated as an option:

, , , and support command aliases.

Пожалуйста, попробуйте это,

Перейдите к пути «Сервер RabbitMQ
abbitmq_server-VERSIONsbin», затем выполните следующие шаги,

Я бы предпочел эту команду:

brew services run rabbitmq

Я чувствую, что это больше похоже на systemd.

A RabbitMQ plugin can provide CLI commands that will be discovered by tools such as ,
, , and others. For plugin commands to be discoverable, the plugin
must be explicitly enabled.

When performing command discovery, CLI tools will consult the Enabled Plugins File to determine
what plugins to scan for commands. If a plugin is not included into that file, e.g. because it was enabled implicitly as
a dependency, it won’t be listed in the enabled plugins file and thus its CLI commands will not be discovered
and will not be available.

Use the command to see what commands are available, both core and provided by plugins.

System and Environment Requirements

RabbitMQ CLI tools require a compatible Erlang/OTP version to be installed.

The tools assume that system locale is a UTF-8 one (e.g. or ). If that’s
not the case, the tools may still function correctly but it cannot be guaranteed.
A warning will be emitted in non-UTF-8 locales.

Command Aliases

, and support command aliases. Aliases provide
a way to define abbreviated versions of certain commands and their arguments. For example,
instead of typing it may be more convenient to define an alias,
, that would expand to .

Aliases are loaded from a file specified via the environment
variable:

The aliases file uses a very minimalistic ini-style alias = command format, for
example:

With this alias file in place it will be possible to use

which would expand to

rabbitmqctl list_queues —quiet

The last alias in the example above configures a command:

rabbitmq-diagnostics cipher_suites —openssl-format —quiet

All tools process aliases the same way. As long as the expanded command is recognized,
aliases can be used with any tool or even more than one. For example,
both and provide the command
so the alias works for both of them exactly the same way:

The file will be consulted only if the command invoked is not provided by the tool.

Help Us Improve the Docs <3

If you’d like to contribute an improvement to the site,
its source is available on GitHub.
Simply fork the repository and submit a pull request. Thank you!

Getting Help and Providing Feedback

If you have questions about the contents of this guide or
any other topic related to RabbitMQ, don’t hesitate to ask them
using GitHub Discussions
or our community Discord server.

Rabbitmqctl

If interaction from a remote node is required, download and extract the generic UNIX package
or use the Windows installer.

Besides authentication, all configuration for core CLI tools is optional.

Commands that require specific arguments list them in the usage section and will report
any missing arguments when executed.

Я также столкнулся с похожей ошибкой при попытке включить плагин управления rabbitmq,
$rabbitmq-plugins enable rabbitmq_management

Это было решено, когда я побежал с ** sudo. **

На самом деле я получил ту же ошибку:

Только root или rabbitmq должны запускать rabbitmqctl

когда я хотел выполнить следующую команду:

rabbitmq-plugins enable rabbitmq_mqtt

Я просто запустил его с sudo и это было решено!

Попробуйте перезапустить rabbitmq-server затем попробуйте снова с sudo:

sudo service rabbitmq-server restart

Discovering Commands Using the Help Command

To find out what commands are available, use the command:

The command can display usage information for a particular command:

rabbitmq-diagnostics help status

Alternatively, the option can be used:

including for individual commands:

rabbitmq-diagnostics status —help

Managing Nodes

To retrieve node status, use or
with an optional target:

rabbitmq-diagnostics status

rabbitmq-diagnostics .bat status

Starting a node

How RabbitMQ nodes are started depends on the package type used:

Stopping a node

To stop a node, consider using the same service management tooling used when starting
the node, which depends on the package typed used when RabbitMQ was installed.

To stop a node using RabbitMQ CLI tools, use
or with an optional target:

How CLI Tools Authenticate to Nodes (and Nodes to Each Other)

If the file does not exist, Erlang VM will automatically create
one with a randomly generated value when the RabbitMQ server
starts up.

Дополнительно:  Как получить root для телефона lenovo

Erlang cookie generation should be done at cluster deployment stage, ideally using automation
and orchestration tools.

Docker community RabbitMQ image uses environment variable value
to populate the cookie file.

Configuration management and container orchestration tools that use this image
must make sure that every RabbitMQ node container in a cluster uses the same value.

In the context of Kubernetes, the value must be specified in the
deployment file.
For instance, this can be seen in the RabbitMQ on Kubernetes examples repository.

Windows

On Windows, the cookie location depends on a few factors:

Erlang 20. 2 or later

With Erlang versions starting with 20.2, the cookie file locations are:

Overriding Using CLI and Runtime Command Line Arguments

As an alternative, the option «» can be added
to environment variable value
to override the cookie value used by a RabbitMQ node:

CLI tools can take a cookie value using a command line flag:

rabbitmq-diagnostics status —erlang-cookie «cookie-value»

Both are the least secure options and generally not recommended.

Starting with version , includes a command
that provides relevant information on the Erlang cookie file used by CLI tools:

Server Nodes

Unless any server directories were overridden, that’s the directory where
the cookie file will be looked for, and created by the node on first boot if it does not already exist.

In the example above, the cookie file location will be .

Hostname Resolution

Starting with RabbitMQ , CLI tools provide two commands that help verify
that hostname resolution on a node works as expected. The commands are not meant to replace
and other specialised DNS tools but rather
provide a way to perform most basic checks while taking Erlang runtime hostname resolver features
into account.

The commands are covered in the Networking guide.

Authentication Failures

When the cookie is misconfigured (for example, not identical), RabbitMQ nodes will log errors
such as «Connection attempt from disallowed node», «», «Could not auto-cluster».

For example, when a CLI tool connects and tries to authenticate using a mismatching secret value:

When a CLI tool such as fails to authenticate with RabbitMQ,
the message usually says

* epmd reports node ‘rabbit’ running on port 25672
* TCP connection succeeded but Erlang distribution failed
* suggestion: hostname mismatch?
* suggestion: is the cookie set correctly?
* suggestion: is the Erlang distribution using TLS?

This means that TCP connection from a CLI tool to a RabbitMQ node
succeeded but authentication attempt was rejected by the server. The
message also mentions several most common reasons for that, which are
covered next.

An incorrectly placed cookie file or cookie value mismatch are most
common scenarios for such failures.

RabbitMQ node logs its cookie hash on start. CLI tools print their
cookie hash value when they fail to authenticate with the target node.

When a recent Erlang/OTP version is used, authentication failures contain
more information and cookie mismatches can be identified better:

Node Name Type Mismatch

If RabbitMQ nodes are configured to use long node names ( is exported to ),
so should CLI tools via the same environment variable or the command line flag introduced in 3.7.0.

Inter-node Connections Require TLS

If RabbitMQ is set up to encrypt inter-node and CLI connections using TLS,
CLI tools also must use TLS and therefore require additional options.
Non-TLS connections from other nodes and CLI tools will fail.

Hostname Mismatch

then even if and resolve to the same IP address,
the server will reject ‘s authentication attempt. This scenario is relatively
rare.

When a recent Erlang/OTP version is used, authentication failures contain
more information and hostname mismatches can be identified better:

Other Possible Reasons

Just like with any network connection, CLI-to-node connections can fail due to

and so on.

RabbitMQ Networking guide contains a section on troubleshooting of networking-related issues.

Rabbitmq-plugins

rabbitmq-plugins is a tool that manages plugins:
lists, enables and disables them. It ships with RabbitMQ.

It supports both online (when target node is running) and offline mode (changes
take effect on node restart).

Offline Mode

is a flag supported by commands. When provided, the tool will avoid
contacting the target node and instead operate on plugin files directly.

When the flag is used, the command will rely on environment variables
to determine where to find the plugins directory of the local node.

Using CLI Tools against Remote Server Nodes

Some commands, such as

can be used against any node. Others, such as

can only be run on the same host or in the same container as their target node. These commands typically
rely on or modify something in the local environment, e.g. the local enabled plugins file.

Node Names

Some commands accept both a target node and another node name. For example,
accepts both a target node (that will perform the action)
and a name of the node to be removed.

In a cluster, nodes identify and contact each other using node names. See Clustering guide
for details.

When a node starts up, it checks whether it has been assigned a node name. This is done
via the environment variable.
If no value was explicitly configured, the node resolves its hostname and prepends to it to compute its node name.

If a system uses fully qualified domain names (FQDNs) for hostnames, RabbitMQ nodes
and CLI tools must be configured to use so called long node names.
For server nodes this is done by setting the environment variable
to .

For CLI tools, either must be set or the option
must be specified:

Rabbitmqadmin

The tool requires Python 2.7.9 or a more recent version.

«Node-local» and «Clusterwide» Commands

Client connections, channels and queues will be distributed across cluster nodes.
Operators need to be able to inspect and monitor such resources
across all cluster nodes.

Assuming a non-changing
state of the cluster (e.g. no connections are closed or
opened), two CLI commands executed against two different
nodes one after another will produce identical or
semantically identical results. «Node-local» commands, however, likely will not produce
identical results since two nodes rarely have entirely identical state.

Оцените статью
Master Hi-technology
Добавить комментарий