runit 学习笔记

概要

通过这篇教程,我们可以快速了解runit并搭建起相应服务。

runit是一个用于服务监控的UNIX软件,它提供以下两种服务:

当服务器启动的时候启动定义好的服务。
监控运行的服务,当服务发生意外中断的时候,自动重启服务。
这篇教程将通过一个简单的示例来讲解如何创建新的runit服务。如果你想了解更多信息,可以查看runit的官方文档

本笔记包括以下三部分:

  1. 创建一个runit模板
  2. 创建第一个服务
  3. 如何自动管理服务

搭建环境

首先确保系统安装了runit,大多数Linux版本的软件仓库里都包哈了runit包。例如,如果你的系统是基于Debian的,则可以使用下面的命令进行安装:

apt-get install runit

如果是centos,则可以使用yum进行安装,但是默认情况下centos软件仓库里并没有runit,所以需要先配置相应的仓库:

 curl -s https://packagecloud.io/install/repositories/imeyer/runit/script.rpm.sh | sudo bash
sudo yum install runit-2.1.1-7.el7.centos.x86_64

装完之后执行以下命令,它的意思是runsvdir会监控/etc/service/目录下的文件,这些文件用于配置被监控的服务

runsvdir -P /etc/service log:.......

runsvdir其实是一套组件,这些组件可以满足用户的各种需求,核心组件包括了runsvdir,runsv, chpst,svlogd以及sv。

注意输出结果中的runsvdir -P /etc/service log:......., 它的意思是runsvdir会监控/etc/service/目录下的文件,这些文件用于配置被监控的服务。

被监控的服务是通过在/etc/service目录下创建子目录,并添加可执行脚本run来实现的。

当runsvdir发现新的配置文件时,它就会自动启动一个runsv进程来管理这个配置的服务。

runit的设计思想就是每个组件的功能是完全独立的,以便管理。可以使用man命令查看具体组件的

man runsvdir

在/etc/service,如果不存在,则使用mkdir创建相应目录

mkdir /etc/service

验证启动是否成功

[root@fabric-cli logs]# ps -ef | grep runsvdir
root      2023  1915  0 16:27 pts/1    00:00:00 runsvdir -P /etc/service
root      2638  1519  0 16:37 pts/0    00:00:00 grep --color=auto runsvdir

创建模板

为了便于开发与测试,这里我们不直接在/etc/service/目录中添加配置文件,而是创建一个暂存的目录来放置配置文件。
当我们满意自己的测试之后,再使用软链接,将暂存目录链接到/etc/service

创建/etc/runit作为暂存目录。

mkdir  /etc/runit

在创建一个真正的runit服务之前,我们先创建一个模板。这个模板将展示runit的基本用法,以后可以直接使用这个模板创建新的服务。
首先创建template目录:

mkdir /etc/runit/template

接下来,创建run脚本:

#!/bin/sh -e
exec 2>&1
exec chpst -u USER COMMAND

给run添加可执行权限:

chmod +x /etc/runit/template/run

这个脚本首先将标准错误输出流输出到标准输出流,然后执行chpst命令。chpst命令用来指定使用哪个用户执行命令。由于run脚本默认被root用户执行,通过chpst可以将run配置为普通用户来执行。通过man命令可以查看chpst的更多信息。

man chpst

当runsvdir检查到/etc/service目录下包含一个新的目录时,runsvdir会启动一个runsv进程来执行和监控run脚本。通过man命令查看runsv的更多信息:

man runsv

你可能注意runsv还可以监控日志服务,日志在应用中非常的重要。下面创建一个日志模板。
首先创建log目录:

mkdir /etc/runit/template/log

然后创建run脚本:

#!/bin/sh
exec chpst -u USER svlogd -tt LOGDIR

给run脚本添加可执行权限:

# chmod +x /etc/runit/template/log/run

上面的脚本使用chpst启动一个svlogd守护进程,该进程将日志信息写到LOGDIR目录中。
使用man命令获取更多关于svlodg的信息:

man svlogd

当runsvdir在/etc/service/目录中发现新的配置时,它会继续查找子目录log,如果找到了则启动runsv进程来执行和监控log目录下的run脚本。

服务示例

接下来,我们使用上面创建的模板来创建一个简单的服务配置。首先创建需要再run脚本中被chpst使用的用户:

 adduser foo

接下来创建放置服务配置的目录,并将它的所属用户与用户组设置为foo。

mkdir /opt/example
chown foo:foo /opt/example

接下来切换到foo用户:

su foo

在/opt/example目录中添加脚本foo-service.sh:

#!/bin/bash

echo "Started service..."

for i in {1..30}
do
    echo "Doing stuff..."
    sleep 1
done

echo "Oh no I crashed..." >&2
exit 1

给脚本赋予执行权限:

chmod +x /opt/example/foo-service.sh

上面的脚本模拟了一个真实的应用,每隔1秒打印一次日志信息,最后打印一次错误信息。可以尝试执行这个脚本:

/opt/example/foo-service.sh

接下来创建包含日志服务的目录:

mkdir /opt/example/logs

再切换回root用户。

exit

接下来使用之前创建的模板来监控这个示例服务,将暂存目录中的模板文件拷贝至新的目录example:

cp -R /etc/runit/template /etc/runit/example

更新/etc/runit/example/run脚本的内容,使用foo用户来执行foo-service.sh脚本。

#!/bin/sh -e
exec 2>&1
exec chpst -u foo /opt/example/foo-service.sh

同样的,更新/etc/runit/example/log/run脚本内容:

#!/bin/sh
exec chpst -u foo svlogd -tt /opt/example/logs

在将服务部署到/etc/service前,首先测试下配置是否正确:

/etc/runit/example/run

如果脚本运行正常,就可以部署服务了,创建一个软链接:

ln -s /etc/runit/example /etc/service/example

不出意外的话,runsvdir会检测到我们配置的两个服务,然后启动两个runsv进程来执行并监控服务。通过下面的命令可以检测服务是否正常运行。

sv status example

输出结果如下所示:

[root@fabric-cli logs]# sv status example
run: example: (pid 3325) 2s; run: log: (pid 2025) 1248s

使用tail命令可以查看日志信息:

[root@fabric-cli logs]#  tail -f /opt/example/logs/current
2018-05-23_08:49:10.80197 Doing stuff...
2018-05-23_08:49:11.80376 Doing stuff...
2018-05-23_08:49:12.80574 Doing stuff...
2018-05-23_08:49:13.80805 Doing stuff...
2018-05-23_08:49:14.80964 Doing stuff...
2018-05-23_08:49:15.81138 Doing stuff...
...

管理服务

最后,我们再来学习下sv用法,sv用来手动管理我们的服务。

检查服务的状态

sv status example

停止服务

sv stop example

停止服务之后不会再输出日志信息,也不会再自动重启。

重启服务

sv restart example

更多用法

man sv

参考
runit 快速入门

### C++ gRPC 学习教程和示例代码 #### 一、环境搭建 为了能够顺利运行C++中的gRPC程序,需要先完成开发环境的配置。这通常涉及到安装必要的依赖库以及设置编译工具链。具体来说,可以按照官方文档指导来准备所需的软件包,比如Protocol Buffers编译器`protoc`及其对应的C++插件,还有gRPC核心库等[^1]。 ```bash sudo apt-get install build-essential autoconf libtool pkg-config git clone https://github.com/protocolbuffers/protobuf.git cd protobuf ./autogen.sh && ./configure && make -j$(nproc) && sudo make install ``` 对于gRPC本身,则可以通过如下命令获取并构建: ```bash git clone --recurse-submodules -b v1.48.x https://github.com/grpc/grpc cd grpc mkdir -p cmake/build && cd cmake/build cmake ../.. make -j$(nproc) sudo make install ``` #### 二、创建Protobuf文件 接下来就是定义服务接口,在`.proto`文件里描述消息结构和服务方法。这里给出一个简单的例子——HelloWorld服务,其中包含了一个名为SayHello的方法用于接收请求并向客户端返回响应信息。 ```protobuf syntax = "proto3"; option cc_enable_arenas = true; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ``` 保存上述内容到`helloworld.proto`之后,利用之前提到过的`protoc`命令行工具将其转换成相应的头文件与源码文件以便后续使用。 #### 三、编写服务器端逻辑 基于前面所生成的服务类模板,现在可以在项目中实现具体的业务处理函数了。下面展示的是如何继承自动生成出来的基类,并重写虚函数以提供实际功能的部分代码片段。 ```cpp #include <iostream> #include "helloworld.grpc.pb.h" using namespace std; using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using helloworld::Greeter; using helloworld::HelloReply; using helloworld::HelloRequest; class GreeterServiceImpl final : public Greeter::Service { public: Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override { string prefix("Hello "); reply->set_message(prefix + request->name()); return Status::OK; } }; ``` 这段代码实现了当接收到客户端发起的调用时会执行的操作:拼接字符串形成回复文本并通过参数传递给对方。 #### 四、启动监听进程 有了完整的协议声明加上对应的功能模块后就可以着手建立网络连接等待远端访问啦! ```cpp void RunServer() { string server_address("0.0.0.0:50051"); GreeterServiceImpl service; ServerBuilder builder; // Listen on the given address without any authentication mechanism. builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); // Register "service" as the instance through which we'll communicate with // clients. In this case it corresponds to an *synchronous* service. builder.RegisterService(&service); // Finally assemble the server. unique_ptr<Server> server(builder.BuildAndStart()); cout << "Server listening on " << server_address << endl; // Wait for the server to shutdown. Note that some other thread must be // responsible for shutting down the server for this call to ever return. server->Wait(); } ``` 此部分负责初始化HTTP/2传输层设施并将指定地址开放出去供外界联系;同时注册好先前定义好的处理器对象使得每次有新链接进来都能找到合适的地方去解析数据流进而触发相应动作。 #### 五、客户端编程指南 最后一步自然是要让应用程序具备主动出击的能力咯~即构造出能向远程主机发出请求的消息体格式化为wire format再经由socket发送过去得到回应为止的过程。 ```cpp void RunClient() { string target_str("localhost:50051"); // Instantiate the client. It requires a channel, out of which the actual RPCs // are created. This channel models a connection to an endpoint specified by // the argument; you may provide extra arguments to indicate credentials, // compression Level etc. shared_ptr<Channel> channel = CreateChannel(target_str, InsecureChannelCredentials()); // Stub acts like a proxy object representing remote side entity. unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel)); // Data we are sending to the server. HelloRequest request; request.set_name("you"); // Container for the data we expect from the server. HelloReply reply; // Context for the client. It could be used to convey extra information to // the server and/or tweak certain RPC behaviors. ClientContext context; // The actual RPC. Status status = stub->SayHello(&context, request, &reply); // Act upon its status. if (status.ok()) { cout << "Greeter received: " << reply.message() << endl; } else { cerr << status.error_code() << ": " << status.error_message() << endl; } } ``` 以上便是整个流程的大致介绍,当然这只是冰山一角而已,更多高级特性和最佳实践还需要读者朋友们自行探索学习哦~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值