centos的服务化---上篇、java程序在centos下的后台进程及管理

16 篇文章 0 订阅
16 篇文章 0 订阅

前言

要做这个,大家要先知道systemctl和chkconfig是什么东西来的,然后再写脚本。

具体参考:

linux下java程序在centos的部署上篇—jar程序服务化、nohup用法及管理、nohup输出日志定时切割(草稿篇)

实践

在下列目录添加service脚本,
vim /usr/lib/systemd/system/

当然,以一个实际模块作为例子,我们将服务名称定义为:
dev@MicroBase.service

ps:假如将脚本放到system的同级—user下面,那就会触发unit not found的错误的。压根识别不了。

然后–假如有看之前文章的话肯定会知道jar文件是存在的,譬如:

这里写图片描述
下面开始。

systemctl自定义服务

引用参考:

CentOS 7的服务systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户(user)之分,像须要开机不登陆就能执行的程序,还是存在系统服务里吧,即:/usr/lib/systemd/system文件夹下

每个服务以.service结尾,通常会分为3部分:[Unit]、[Service]和[Install],我写的这个服务用于开机执行Node.js项目,详细内容例如以下:

[Unit]
Description=xiyoulibapi
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/node.js/pid
ExecStart=/usr/local/bin/forever start /node.js/xiyoulib/bin/www
ExecReload=/usr/local/bin/forever restart /node.js/xiyoulib/bin/www
ExecStop=/usr/local/bin/forever stop /node.js/xiyoulib/bin/www
PrivateTmp=true

[Install]
WantedBy=multi-user.target

好了,可以先自定义一个simple的unit服务玩一玩。

首先打开:

vim /usr/lib/systemd/system/dev@MicroBase.service

然后复制下面内容

[Unit]
Description=base micro service

[Service]
Type=simple
PIDFile=/usr/local/pid-files/MicroBaseApp-dev.pid
#ExecStart=/usr/micro-service/apps/Service4MicroBase.sh -e dev -o start
ExecStart=/usr/bin/nohup /usr/bin/java -jar  /usr/micro-service/apps/dev/MicroBase/MicroBaseApp/MicroBaseApp.jar >/usr/local/logs/dev-MicroBase-2.log 2>&1 &

ExecStop=/usr/bin/kill  -9 $MAINPID
Restart=always
#取消启动频率限制吧。
StartLimitInterval=0
PrivateTmp=true
[Install]
WantedBy=multi-user.target

接下来,

chmod 754 /usr/lib/systemd/system/dev@MicroBase.service

ps:
不要多手在添加一个x上去,譬如:

chmod +x /usr/lib/systemd/user/dev@MicroBase.service

会报错的,到时直接提示不要设置execute的权限的。

然后:

#注意,确保之前没有后台程序
echo ""> /usr/local/logs/dev-MicroBase-2.log
systemctl daemon-reload
systemctl stop dev@MicroBase.service
systemctl start dev@MicroBase.service
systemctl status dev@MicroBase.service
journalctl -xe
vim /usr/local/logs/dev-MicroBase-2.log

执行状态时候的结果:

systemctl status dev@MicroBase.service

这里写图片描述

终于终于终于没有203错误了!

好了,看看服务的日志:

journalctl -xe

这里写图片描述

这次也没有报错—-还有:

服务已经启动这句话明明是程序system.out出来的,怎么会在服务日志里面?不应该在nohup的输出文件吗?

看看nohup日志:

vim /usr/local/logs/dev-MicroBase-2.log

这里写图片描述
。。。。还是空文件。。。

好了测试通过

坑1、启动太频繁 repeated too quickly

systemctl status dev@MicroBase.service

这里写图片描述

下面解决一下。
按照字面意思是太频繁了,那么我们先重启再尝试一下。

这里写图片描述

完全不行,方向错误了。。。

参考:

[CentOS 7之Systemd详解之服务单元设置system.service(https://blog.csdn.net/yuesichiu/article/details/51485147)

StartLimitInterval=, StartLimitBurst=
限制该服务的启动频率。默认值是每10秒内不得超过5次(StartLimitInterval=10s StartLimitBurst=5)。
StartLimitInterval= 的默认值等于systemd配置文件中 DefaultStartLimitInterval= 的值,”0”表示取消启动频率限制。
StartLimitBurst= 的默认值等于systemd配置文件中 DefaultStartLimitBurst= 的值。
虽然这两个选项经常与 Restart= 一起使用,但是它们不只限制 Restart= 罗辑所导致的重启,而是限制所有类型的启动(包括手动启动)。 注意,当 Restart=逻辑所导致的重启超出了启动频率限制之后,Restart= 逻辑将会被禁用(也就是不会在下一个时间段内再次尝试重启), 然而,如果该单元随后又被手动重启,那么 Restart= 罗辑将被再次激活。 注意,”systemctl reset-failed …”命令会清除该服务的重启次数计数器,这通常用于在手动启动之前清除启动限制。

StartLimitInterval=0 加上这一句。。

这里写图片描述

加上以后变成:

这里写图片描述

然后重试:

这里写图片描述

坑2、system.in.read引发的血案

假设我们的java文件代码如下:

package net.w2p.MicroBase;


import com.alibaba.fastjson.JSONObject;
import net.w2p.MicroBase.searcher.account.MemberCondition;
import net.w2p.MicroBase.service.account.MemberRoleService;
import net.w2p.MicroBase.service.account.MemberService;
import net.w2p.MicroBase.vo.account.Member;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.util.ArrayList;

public class Provider {

    public static void main(String[] args) throws IOException {


        System.out.println("服务已经启动...");

        System.in.read();
    }
}

日志及bad file descriptor问题

就一个服务已经启动的代码,我们看看日志如何获得。
这里写图片描述

正常情况是这样的。那么用nohup,直接使用命令:

这里写图片描述

看看日志文件:

这里写图片描述

ps:这段代码可是参考了网上主流文章而而写出来的,这样也错就只能说大家都错了或者大家都忽略了一些东西了。

所有,bad file descriptor是什么鬼?

直接执行正常,加了nohup就不行了?

查阅资料:
execute nohup command with playframework get Bad file descriptor error

这里抄录一下:
问:

I use playframework2.2 and sbt 0.13.1, I can run the sbt and start the server on command line

sbt start

it works ok. but when I run:

nohup sbt start

It run a while and then stop with log error:

(Starting server. Type Ctrl+D to exit logs, the server will remain in background)  java.io.IOException: Bad file descriptor
at java.io.FileInputStream.read0(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:210)
at jline.internal.NonBlockingInputStream.read(NonBlockingInputStream.java:248)
at jline.internal.InputStreamReader.read(InputStreamReader.java:261)
at jline.internal.InputStreamReader.read(InputStreamReader.java:198)
at jline.console.ConsoleReader.readCharacter(ConsoleReader.java:2038)
at  play.PlayConsoleInteractionMode$$anonfun$waitForKey$1.play$PlayConsoleInteractionMode$$anonfun$$waitEOF$1(PlayInteractionMode.scala:36)
at play.PlayConsoleInteractionMode$$anonfun$waitForKey$1$$anonfun$apply$1.apply$mcV$sp(PlayInteractionMode.scala:45)
at play.PlayConsoleInteractionMode$$anonfun$doWithoutEcho$1.apply(PlayInteractionMode.scala:52)
at play.PlayConsoleInteractionMode$$anonfun$doWithoutEcho$1.apply(PlayInteractionMode.scala:49)
at play.PlayConsoleInteractionMode$.withConsoleReader(PlayInteractionMode.scala:31)
at play.PlayConsoleInteractionMode$.doWithoutEcho(PlayInteractionMode.scala:49)
at play.PlayConsoleInteractionMode$$anonfun$waitForKey$1.apply(PlayInteractionMode.scala:45)
at play.PlayConsoleInteractionMode$$anonfun$waitForKey$1.apply(PlayInteractionMode.scala:34)
at play.PlayConsoleInteractionMode$.withConsoleReader(PlayInteractionMode.scala:31)
at play.PlayConsoleInteractionMode$.waitForKey(PlayInteractionMode.scala:34)
at play.PlayConsoleInteractionMode$.waitForCancel(PlayInteractionMode.scala:55)
at play.PlayRun$$anonfun$24$$anonfun$apply$9.apply(PlayRun.scala:373)
at play.PlayRun$$anonfun$24$$anonfun$apply$9.apply(PlayRun.scala:352)
at scala.util.Either$RightProjection.map(Either.scala:536)
at play.PlayRun$$anonfun$24.apply(PlayRun.scala:352)
at play.PlayRun$$anonfun$24.apply(PlayRun.scala:334)
at sbt.Command$$anonfun$sbt$Command$$apply1$1$$anonfun$apply$6.apply(Command.scala:72)
at sbt.Command$.process(Command.scala:95)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:100)
at sbt.MainLoop$$anonfun$1$$anonfun$apply$1.apply(MainLoop.scala:100)
at sbt.State$$anon$1.process(State.scala:179)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:100)
at sbt.MainLoop$$anonfun$1.apply(MainLoop.scala:100)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.MainLoop$.next(MainLoop.scala:100)
at sbt.MainLoop$.run(MainLoop.scala:93)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:71)
at sbt.MainLoop$$anonfun$runWithNewLog$1.apply(MainLoop.scala:66)
at sbt.Using.apply(Using.scala:25)
at sbt.MainLoop$.runWithNewLog(MainLoop.scala:66)
at sbt.MainLoop$.runAndClearLast(MainLoop.scala:49)
at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:33)
at sbt.MainLoop$.runLogged(MainLoop.scala:25)
at sbt.StandardMain$.runManaged(Main.scala:57)
at sbt.xMain.run(Main.scala:29)
at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:57)
at xsbt.boot.Launch$.withContextLoader(Launch.scala:77)
at xsbt.boot.Launch$.run(Launch.scala:57)
at xsbt.boot.Launch$$anonfun$explicit$1.apply(Launch.scala:45)
at xsbt.boot.Launch$.launch(Launch.scala:65)
at xsbt.boot.Launch$.apply(Launch.scala:16)
at xsbt.boot.Boot$.runImpl(Boot.scala:32)
at xsbt.boot.Boot$.main(Boot.scala:21)
at xsbt.boot.Boot.main(Boot.scala)
error[0m] [0mjava.io.IOException: Bad file descriptor[0m
error[0m] [0mUse 'last' for the full log.[0m

回答:

The error happens because standard input get redirected from /dev/null by nohup - you get the same error if you do play start < /dev/null. The sbt process starts the actual server in a separate process, the sets itself up to display logs and wait for you to type Ctrl-D or Ctrl-C. It uses JLine to wait for user input, which attempts to attach to the standard input as a terminal. /dev/null can't be used in this way, so it dies complaining of a bad file descriptor. However, the background server process continues running.

If you want to start Play non-interactively, you need to use the stage task. See Using the stage task in the Play documentation.

。。。。这里的意思似乎是,交互类型的程序才出这个问题—本身nohup就是放后台运行的。。。。看到这里我灵机一动。。。改改代码编译然后再试试。
ps:为什么这里要执着于这个问题,system.in.read的问题?因为这个方式是阻塞了主线程的方法,在此期间,微服务会一直开启一直保持处理状态,没有这个的话估计微服务运行不起来。也就是说,所有用nohup的后台程序都要考虑如何保持java程序一直运行而不是一闪而过
解决方案:
这里写链接内容
大家可以参考一下:

这里写图片描述

代码修改,本地测试通过:
这里写图片描述
好了,上传到jenkins,然后编译,然后部署到目标目录上面去:
这里写图片描述

第一,原始方法执行:
这里写图片描述

java -jar  /usr/micro-service/apps/dev/MicroBase/MicroBaseApp/MicroBaseApp.jar

这里写图片描述

只能用ctrl c断掉,程序测试通过。

好了,清空一下日志,然后用nohup来执行。

echo ""> /usr/local/logs/dev-MicroBase.log

/usr/bin/nohup /usr/bin/java -jar  /usr/micro-service/apps/dev/MicroBase/MicroBaseApp/MicroBaseApp.jar >/usr/local/logs/dev-MicroBase.log 2>&1 /usr/local/pids/dev-MicroBase.pid &

vim /usr/local/logs/dev-MicroBase.log

这里写图片描述

这里写图片描述

好了,没有刚才的问题了,那么,退出来看看nohup是不是真的有这个后台程序在执行。

jobs -l

这里写图片描述

有了这个程序了,我们可以切换到前台然后ctrl c直接杀死进程。

fg 1

这里写图片描述

这里写图片描述

好了,用了信号量以后,既可以阻塞主程序,性能也低,也可以避开人机交互。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值