1. 소개
오늘날의 많은 서버측 프로그램은 Java를 기반으로 개발됩니다. Java로 개발된 소켓 프로그램의 경우 이러한 서버측이 온라인 상태가 된 후 문제가 발생하면 한밤중에 중단되면 수동으로 다시 시작해야 합니다. , 여전히 매우 번거롭습니다.
대부분의 솔루션은 서버 프로그램을 보호하기 위해 다른 프로세스를 사용하는 것입니다. 서버 프로그램이 중단되면 데몬 프로세스를 통해 서버 프로그램을 시작하십시오.
데몬 프로세스가 중단되면 어떻게 되나요? 안정성을 높이기 위해 듀얼 가드를 사용합니다. 가드 A는 서버 프로그램을 모니터링하고 가드 B는 가드 A를 모니터링합니다. 양쪽에 문제가 있으면 프로그램을 빠르게 시작하여 안정성을 향상시킬 수 있습니다. 서버 프로그램.
Java의 실행 환경은 C와 같은 언어로 개발된 프로그램과 다릅니다. Java 프로그램은 JVM에서 실행됩니다. 프로세스를 직접 생성할 수 있는 C 언어와 달리 Java에서 프로세스를 생성하는 것은 java -jar xxx.jar을 사용하여 프로그램을 시작하는 것과 같습니다.
Java 시작 프로그램에는 C#과 마찬가지로 단일 인스턴스 제한이 없습니다. 여러 개를 시작할 수는 있지만 여러 개를 시작할 수는 없습니다. 서버 프로그램을 보호하기 위해 여러 개의 서버 프로그램이 시작되면 어떻게 될까요?
2. 기술적 설명
여기의 기술적인 설명은 비교적 대략적입니다. 자세한 내용은 Baidu를 확인하세요. 여기서는 기능만 설명하겠습니다.
1. jps 명령.
JDK와 함께 제공되는 명령 도구는 jps -l을 사용하여 실행 중인 Java 프로그램을 나열하고 Java 프로그램의 pid와 이름을 표시합니다. 실제로 Java 프로그램에만 유효합니다. 실제로 보고 있는 것은 실행 중인 JVM입니다.
2. java.nio.channels.FileLock 클래스를 사용합니다. 이는 Java new IO의 클래스로 파일을 읽는 동안 파일을 잠그는 데 사용할 수 있습니다. 파일이 다른 프로그램을 사용하여 잠겨 있습니다.
3. ProcessBuilder 및 프로세스
두 가지 원칙은 모두 시스템 명령을 호출하여 실행한 다음 정보를 반환하는 것과 비슷합니다. 그러나 하드 코딩하면 Java 프로그램의 이식성이 저하됩니다. 명령을 구성 파일로 분리할 수 있습니다.
3. 디자인 원칙
서버: 서버 프로그램
A: 데몬 A
B: 데몬 B
A.lock: 데몬 A의 파일 잠금
B.lock: 데몬 B의 파일 잠금
------------------------------------- --------------------------------
1단계: 먼저 서버를 고려하지 않고 A와 B 사이의 보호만 고려합니다.
1.A는 B가 살아 있는지 확인하고 그렇지 않으면 B를 시작합니다.
2.B는 A가 살아 있는지 확인하고 그렇지 않으면 A를 시작합니다.
3. 실행 과정에서 A와 B는 서로의 파일 잠금을 얻기 위해 서로 이동합니다. 이를 얻으면 상대방이 다운되었음을 증명하고 상대방을 시작합니다.
4. A가 시작되면 A.lock 파일의 잠금을 획득합니다. 잠금을 획득하면 A가 시작되지 않았음을 증명하고, 잠금을 획득하지 못하면 A가 이미 시작되었음을 증명합니다. B가 판단할 때 잠금을 얻었습니다. A가 이미 시작했고 A를 다시 시작할 필요가 없다면 B가 판단할 때 잠금을 얻었더라도 상관없습니다. 어쨌든 B는 A를 다시 시작할 것입니다.
5. B가 시작되면 원리는 A와 동일합니다.
6. A가 작동 중에 전화를 끊으면 B는 A가 전화를 끊은 것으로 판단하고 A를 시작합니다. B마찬가지로.
2단계: 서버 가입
1.A는 B와 서버를 보호하는 데 사용되고 B는 A를 보호하는 데 사용됩니다.
2. A가 Serer를 보호하는 여러 임무를 수행한다는 점을 제외하면 원칙은 1단계와 동일합니다.
3. A가 실행 중일 때 프로세스 pid를 사용하여 서버가 중단되었음을 감지한 후 서버를 시작합니다.
4. 서버와 A가 모두 다운되면 B가 A를 시작한 다음 A가 서버를 시작합니다.
5. 서버와 B가 다운되면 A는 서버와 B를 시작합니다.
6. A와 B가 모두 죽으면 가드가 종료됩니다.
3단계: Shutdown을 사용하여 가드를 종료합니다. 그렇지 않으면 서버 종료 후 자동으로 시작됩니다.
4. 실현
1. GuardA 구현
다음과 같이 코드 코드를 복사합니다.
공개 클래스 GuardA {
// GuardA는 자체 잠금을 유지하는 데 사용됩니다.
개인 파일 fileGuardA;
개인 FileOutputStream fileOutputStreamGuardA;
개인 FileChannel fileChannelGuardA;
개인 FileLock fileLockGuardA;
// GuardB는 B의 잠금을 감지하는 데 사용됩니다.
개인 파일 fileGuardB;
개인 FileOutputStream fileOutputStreamGuardB;
개인 FileChannel fileChannelGuardB;
개인 FileLock fileLockGuardB;
public GuardA()가 예외를 발생시킵니다. {
fileGuardA = 새 파일(Configure.GUARD_A_LOCK);
if (!fileGuardA.exists()) {
fileGuardA.createNewFile();
}
//파일 잠금을 획득하고 GuardA가 시작되었음을 증명할 수 없으면 종료합니다.
fileOutputStreamGuardA = new FileOutputStream(fileGuardA);
fileChannelGuardA = fileOutputStreamGuardA.getChannel();
fileLockGuardA = fileChannelGuardA.tryLock();
if (fileLockGuardA == null) {
시스템.exit(0);
}
fileGuardB = 새 파일(Configure.GUARD_B_LOCK);
if (!fileGuardB.exists()) {
fileGuardB.createNewFile();
}
fileOutputStreamGuardB = new FileOutputStream(fileGuardB);
fileChannelGuardB = fileOutputStreamGuardB.getChannel();
}
/**
* B가 존재하는지 확인
*
* @return true B가 이미 존재합니다.
*/
공개 부울 checkGuardB() {
노력하다 {
fileLockGuardB = fileChannelGuardB.tryLock();
if (fileLockGuardB == null) {
사실을 반환;
} 또 다른 {
fileLockGuardB.release();
거짓을 반환;
}
} 잡기(IOException e) {
시스템.exit(0);
// 절대 건드리지 마세요
사실을 반환;
}
}
}
2. GuardServer 구현
다음과 같이 코드 코드를 복사합니다.
공개 클래스 GuardServer {
개인 문자열 서버 이름;
public GuardServer(문자열 서버 이름) {
this.서버이름 = 서버이름;
}
public void startServer(String cmd)에서 예외가 발생합니다.
System.out.println("서버 시작 : " + cmd);
//별도의 명령
// String[] cmds = cmd.split(" ");
// ProcessBuilder 빌더 = new ProcessBuilder(cmds);
//
ProcessBuilder builder=new ProcessBuilder(new String[]{"/bin/sh","-c",cmd});
///dev/tty에서 서버 프로그램의 출력을 찾습니다.
builder.redirectOutput(new File("/dev/tty"));
builder.redirectError(새 파일("/dev/tty"));
builder.start(); // IOException 발생
Thread.sleep(10000);
}
/**
* 서비스가 존재하는지 확인하세요
*
* @return 구성된 Java 프로그램의 pid를 반환합니다.
* @return pid >0은 pid <=0을 반환합니다. 이는 지정된 Java 프로그램이 실행되고 있지 않음을 의미합니다.
* **/
public int checkServer()에서 예외가 발생합니다.
int pid = -1;
프로세스 프로세스 = null;
BufferedReader 리더 = null;
process = Runtime.getRuntime().exec("jps -l");
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
스트링라인;
while ((line = reader.readLine()) != null) {
String[] strings = line.split("//s{1,}");
if (문자열. 길이 < 2)
계속하다;
if (문자열[1].contains(서버 이름)) {
pid = Integer.parseInt(strings[0]);
부서지다;
}
}
reader.close();
프로세스.파괴();
pid를 반환;
}
}
3. GuardAMin 구현
다음과 같이 코드 코드를 복사합니다.
공개 클래스 GuardAMin {
public static void main(String[] args)에서 예외가 발생합니다.
GuardA GuardA = 새로운 GuardA();
구성 구성 = 새로운 구성();
GuardServer 서버 = new GuardServer(configure.getServername());
동안 (참) {
// GuardB가 실행되고 있지 않으면 GuardB를 실행합니다.
if (!guardA.checkGuardB()) {
System.out.println("GuardB 시작....");
Runtime.getRuntime().exec(configure.getStartguardb());
}
// 서버 생존 확인
if (server.checkServer() <= 0) {
부울 isServerDown = true;
// 여행 확인
for (int i = 0; i < 3; i++) {
// 서비스가 살아있다면
if (server.checkServer() > 0) {
isServerDown = 거짓;
부서지다;
}
}
if(isServerDown)
server.startServer(configure.getStartserver());
}
Thread.sleep(configure.getInterval());
}
}
}
4. 셧다운 구현
다음과 같이 코드 코드를 복사합니다.
공개 클래스 종료 {
public static void main(String[] args)에서 예외가 발생합니다.
구성 구성 = 새로운 구성();
System.out.println("셧다운 가드..");
for (int i = 0; i < 3; i++) {
프로세스 p = Runtime.getRuntime().exec("jps -l");
BufferedReader 리더 = new BufferedReader(new InputStreamReader(p.getInputStream()));
스트링라인;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains("Guard".toLowerCase())) {
String[] strings = line.split("//s{1,}");
int pid = Integer.parseInt(strings[0]);
Runtime.getRuntime().exec(configure.getKillcmd() + " " + pid);
}
}
p.waitFor();
reader.close();
p.destroy();
Thread.sleep(2000);
}
System.out.println("Guards가 종료되었습니다.");
}
}
5. GuardB는 GuardA와 유사합니다.
5. 다운로드 및 사용
프로젝트 폴더: Guard_demo
다운로드 주소: http://pan.baidu.com/s/1bn1Y6BX
질문이나 제안 사항이 있으면 저에게 연락해주세요.