1. บทนำ
โปรแกรมฝั่งเซิร์ฟเวอร์จำนวนมากในปัจจุบันได้รับการพัฒนาโดยใช้ Java สำหรับโปรแกรม Socket ที่พัฒนาใน Java หากปัญหาเกิดขึ้นหลังจากที่ฝั่งเซิร์ฟเวอร์ออนไลน์ จะต้องรีสตาร์ทด้วยตนเอง หากวางสายกลางดึก มันยังเป็นปัญหาอยู่มาก
วิธีแก้ปัญหาส่วนใหญ่คือการใช้กระบวนการอื่นเพื่อป้องกันโปรแกรมเซิร์ฟเวอร์ หากโปรแกรมเซิร์ฟเวอร์ค้าง ให้เริ่มโปรแกรมเซิร์ฟเวอร์ผ่านกระบวนการ daemon
จะเกิดอะไรขึ้นถ้ากระบวนการ daemon หยุดทำงาน? ใช้ dual guards เพื่อปรับปรุงเสถียรภาพ Guard A รับผิดชอบในการตรวจสอบโปรแกรมเซิร์ฟเวอร์และ guard B Guard B รับผิดชอบในการตรวจสอบ guard A หากมีปัญหาในด้านใดด้านหนึ่ง โปรแกรมสามารถเริ่มต้นได้อย่างรวดเร็วเพื่อปรับปรุงความเสถียรของ โปรแกรมเซิร์ฟเวอร์
สภาพแวดล้อมการทำงานของ Java นั้นแตกต่างจากโปรแกรมที่พัฒนาในภาษาต่างๆ เช่น โปรแกรม C. Java ที่ทำงานบน JVM ต่างจากภาษา C ซึ่งสามารถสร้างกระบวนการได้โดยตรง การสร้างกระบวนการใน Java นั้นเทียบเท่ากับการใช้ java -jar xxx.jar เพื่อเริ่มโปรแกรม
โปรแกรมเริ่มต้น Java ไม่มีการจำกัดอินสแตนซ์เดียวที่คล้ายกับ C# คุณสามารถเริ่มหลายโปรแกรมได้ แต่คุณไม่สามารถเริ่มมากกว่าหนึ่งโปรแกรมได้
2. คำอธิบายทางเทคนิค
คำอธิบายทางเทคนิคที่นี่ค่อนข้างหยาบ โปรดตรวจสอบ Baidu เพื่อดูรายละเอียด ฉันอธิบายฟังก์ชันที่นี่เท่านั้น
1.คำสั่ง jps
เครื่องมือคำสั่งที่มาพร้อมกับ JDK ให้ใช้ jps -l เพื่อแสดงรายการโปรแกรม Java ที่กำลังรันอยู่ และแสดง pid และชื่อของโปรแกรม Java ใช้ได้กับโปรแกรม Java เท่านั้น ที่จริงแล้ว สิ่งที่คุณกำลังดูอยู่คือ JVM ที่กำลังรันอยู่
2. การใช้คลาส java.nio.channels.FileLock นี่คือคลาสใน Java IO ใหม่ คุณสามารถใช้คลาสนี้เพื่อล็อคไฟล์ขณะอ่านไฟล์ได้หรือไม่ ไฟล์ถูกล็อคโดยโปรแกรมอื่น
3. ProcessBuilder และกระบวนการ
หลักการทั้งสองมีความคล้ายคลึงกัน ทั้งสองเรียกคำสั่งระบบเพื่อรันแล้วส่งคืนข้อมูล แต่การเขียนโค้ดอย่างหนักจะทำให้โปรแกรม Java ของคุณสูญเสียความสามารถในการพกพา คุณสามารถแยกคำสั่งออกเป็นไฟล์กำหนดค่าได้
3. หลักการออกแบบ
เซิร์ฟเวอร์: โปรแกรมเซิร์ฟเวอร์
A: เดม่อน เอ
B: เดม่อน บี
A.lock: ล็อคไฟล์ของ daemon A
B.lock: ล็อคไฟล์ของ daemon B
-------------------------------------------------- -------------------------------------------------- --------------------------------
ขั้นตอนที่ 1: ขั้นแรก อย่าพิจารณาถึงเซิร์ฟเวอร์ แต่พิจารณาเฉพาะการป้องกันระหว่าง A และ B เท่านั้น
1.A เป็นตัวกำหนดว่า B ยังมีชีวิตอยู่หรือไม่ และถ้าไม่ ให้เริ่ม B
2.B กำหนดว่า A ยังมีชีวิตอยู่หรือไม่ และเริ่มต้น A ถ้าไม่ใช่
3. ในระหว่างกระบวนการทำงาน A และ B เข้าหากันเพื่อล็อคไฟล์ของกันและกัน หากได้รับ จะเป็นการพิสูจน์ว่าอีกฝ่ายล่มแล้วจึงเริ่มอีกฝ่าย
4. เมื่อ A เริ่มต้น ให้ทำการล็อคไฟล์ A.lock หากได้รับมา จะเป็นการพิสูจน์ว่า A ไม่ได้เริ่มต้น จากนั้น A จะทำงาน หากไม่ได้รับการล็อค จะเป็นการพิสูจน์ว่า A ได้เริ่มต้นแล้ว หรือ B ได้รับการล็อคเมื่อตัดสิน ถ้า A ได้เริ่มต้นแล้ว และไม่จำเป็นต้องเริ่ม A อีกครั้ง ถ้า B ได้รับการล็อคเมื่อทำการตัดสิน ก็ไม่สำคัญ B จะเริ่ม A อีกครั้ง
5. เมื่อ B เริ่มต้นขึ้น หลักการจะเหมือนกับ A
6. ถ้า A วางสายระหว่างการทำงาน B จะพิจารณาว่า A วางสายแล้วและเริ่ม A ขในทำนองเดียวกัน
ขั้นตอนที่ 2: เข้าร่วมเซิร์ฟเวอร์
1.A ใช้เพื่อปกป้อง B และเซิร์ฟเวอร์ และ B ใช้เพื่อปกป้อง A
2. หลักการเหมือนกับขั้นตอนที่ 1 ยกเว้นว่า A มีภารกิจหลายอย่างในการปกป้องเซเรอร์
3. เมื่อ A ทำงานอยู่ ให้ใช้กระบวนการ pid เพื่อตรวจสอบว่าเซิร์ฟเวอร์วางสายแล้ว จากนั้นจึงเริ่มเซิร์ฟเวอร์
4. หากทั้งเซิร์ฟเวอร์และ A ล่ม B จะเริ่ม A จากนั้น A จะเริ่มเซิร์ฟเวอร์
5. หากเซิร์ฟเวอร์และ B ล่ม A จะเริ่มเซิร์ฟเวอร์และ B
6. ถ้าทั้ง A และ B ตาย การป้องกันสิ้นสุดลง
ขั้นตอนที่ 3: ใช้การปิดระบบเพื่อยุติการป้องกัน ไม่เช่นนั้นมันจะเริ่มต้นโดยอัตโนมัติหลังจากสิ้นสุดเซิร์ฟเวอร์
4. การตระหนักรู้
1. การใช้งาน GuardA
คัดลอกรหัสรหัสดังต่อไปนี้:
GuardA ระดับสาธารณะ {
// GuardA ใช้เพื่อรักษาล็อคของตัวเอง
ไฟล์ส่วนตัว fileGuardA;
FileOutputStream ส่วนตัว fileOutputStreamGuardA;
FileChannel ส่วนตัว fileChannelGuardA;
FileLock ส่วนตัว fileLockGuardA;
// GuardB ใช้เพื่อตรวจจับการล็อคของ B
ไฟล์ส่วนตัว fileGuardB;
FileOutputStream ส่วนตัว fileOutputStreamGuardB;
FileChannel ส่วนตัว fileChannelGuardB;
FileLock ส่วนตัว fileLockGuardB;
GuardA สาธารณะ () พ่นข้อยกเว้น {
fileGuardA = ไฟล์ใหม่ (Configure.GUARD_A_LOCK);
ถ้า (!fileGuardA.exists()) {
fileGuardA.createNewFile();
-
//รับการล็อกไฟล์ และออกหากไม่สามารถพิสูจน์ได้ว่า GuardA ได้เริ่มทำงานแล้ว
fileOutputStreamGuardA = FileOutputStream ใหม่ (fileGuardA);
fileChannelGuardA = fileOutputStreamGuardA.getChannel();
fileLockGuardA = fileChannelGuardA.tryLock();
ถ้า (fileLockGuardA == null) {
System.ออก(0);
-
fileGuardB = ไฟล์ใหม่ (Configure.GUARD_B_LOCK);
ถ้า (!fileGuardB.exists()) {
fileGuardB.createNewFile();
-
fileOutputStreamGuardB = FileOutputStream ใหม่ (fileGuardB);
fileChannelGuardB = fileOutputStreamGuardB.getChannel();
-
-
* ตรวจสอบว่ามี B อยู่หรือไม่
-
* @return true B มีอยู่แล้ว
-
checkGuardB บูลีนสาธารณะ () {
พยายาม {
fileLockGuardB = fileChannelGuardB.tryLock();
ถ้า (fileLockGuardB == null) {
กลับเป็นจริง;
} อื่น {
fileLockGuardB.release();
กลับเท็จ;
-
} จับ (IOException จ) {
System.ออก(0);
//อย่าแตะ.
กลับเป็นจริง;
-
-
-
2. การใช้งาน GuardServer
คัดลอกรหัสรหัสดังต่อไปนี้:
GuardServer ระดับสาธารณะ {
ชื่อเซิร์ฟเวอร์สตริงส่วนตัว
GuardServer สาธารณะ (ชื่อเซิร์ฟเวอร์สตริง) {
this.servername = ชื่อเซิร์ฟเวอร์;
-
โมฆะสาธารณะ startServer (สตริง cmd) พ่นข้อยกเว้น {
System.out.println("เริ่มเซิร์ฟเวอร์ : " + cmd);
//แยกคำสั่ง
// String[] cmds = cmd.split(" ");
// ตัวสร้าง ProcessBuilder = ProcessBuilder ใหม่ (cmds);
-
ProcessBuilder builder=new ProcessBuilder(สตริงใหม่[]{"/bin/sh","-c",cmd});
//ค้นหาผลลัพธ์ของโปรแกรมเซิร์ฟเวอร์ไปที่ /dev/tty
builder.redirectOutput(ไฟล์ใหม่("/dev/tty"));
builder.redirectError(ไฟล์ใหม่("/dev/tty"));
builder.start(); // พ่น IOException
เธรด.สลีป(10,000);
-
-
* ตรวจสอบว่ามีบริการอยู่หรือไม่
-
* @return ส่งคืนค่า pid ของโปรแกรมจาวาที่กำหนดค่าไว้
* @return pid >0 ส่งคืน pid <=0 ซึ่งหมายความว่าโปรแกรม Java ที่ระบุไม่ได้ทำงานอยู่
-
int checkServer () สาธารณะพ่นข้อยกเว้น {
intpid = -1;
กระบวนการกระบวนการ = null;
เครื่องอ่าน BufferedReader = null;
กระบวนการ = Runtime.getRuntime().exec("jps -l");
reader = ใหม่ BufferedReader (InputStreamReader ใหม่ (process.getInputStream ()));
เส้นสาย;
ในขณะที่ ((line = reader.readLine()) != null) {
สตริง[] strings = line.split("//s{1,}");
ถ้า (strings.length < 2)
ดำเนินการต่อ;
ถ้า (สตริง [1] .contains (ชื่อเซิร์ฟเวอร์)) {
pid = Integer.parseInt(สตริง[0]);
หยุดพัก;
-
-
reader.ปิด();
กระบวนการทำลาย();
ส่งคืน PID;
-
-
3. การใช้งาน GuardAMain
คัดลอกรหัสรหัสดังต่อไปนี้:
GuardAMain ระดับสาธารณะ {
โมฆะคงที่สาธารณะ main (String [] args) พ่นข้อยกเว้น {
GuardA guardA = GuardA ใหม่();
กำหนดค่า กำหนดค่า = กำหนดค่าใหม่ ();
เซิร์ฟเวอร์ GuardServer = GuardServer ใหม่ (configure.getServername());
ในขณะที่ (จริง) {
// เรียกใช้ GuardB หาก GuardB ไม่ทำงาน
ถ้า (!guardA.checkGuardB()) {
System.out.println("เริ่ม GuardB...");
Runtime.getRuntime().exec(configure.getStartguardb());
-
// ตรวจสอบความอยู่รอดของเซิร์ฟเวอร์
ถ้า (server.checkServer() <= 0) {
บูลีน isServerDown = จริง;
//เช็คการเดินทาง
สำหรับ (int i = 0; i <3; i++) {
// หากบริการยังมีชีวิตอยู่
ถ้า (server.checkServer() > 0) {
isServerDown = เท็จ;
หยุดพัก;
-
-
ถ้า(isServerDown)
server.startServer(configure.getStartserver());
-
Thread.sleep(configure.getInterval());
-
-
-
4. การดำเนินการปิดระบบ
คัดลอกรหัสรหัสดังต่อไปนี้:
การปิดระบบคลาสสาธารณะ {
โมฆะคงที่สาธารณะ main (String [] args) พ่นข้อยกเว้น {
กำหนดค่า กำหนดค่า = กำหนดค่าใหม่ ();
System.out.println("ตัวป้องกันการปิดเครื่อง..");
สำหรับ (int i = 0; i <3; i++) {
กระบวนการ p = Runtime.getRuntime().exec("jps -l");
เครื่องอ่าน BufferedReader = BufferedReader ใหม่ (InputStreamReader ใหม่ (p.getInputStream()));
เส้นสาย;
ในขณะที่ ((line = reader.readLine()) != null) {
ถ้า (line.toLowerCase().contains("Guard".toLowerCase())) {
สตริง[] strings = line.split("//s{1,}");
int pid = Integer.parseInt(สตริง[0]);
Runtime.getRuntime().exec(configure.getKillcmd() + " " + pid);
-
-
p.waitFor();
reader.ปิด();
p.ทำลาย();
Thread.sleep (2000);
-
System.out.println("การ์ดปิดตัวลง");
-
-
5. GuardB คล้ายกับ GuardA
5. ดาวน์โหลดและใช้งาน
โฟลเดอร์โครงการ: guard_demo
ดาวน์โหลดที่อยู่: http://pan.baidu.com/s/1bn1Y6BX
หากคุณมีคำถามหรือข้อเสนอแนะโปรดติดต่อฉัน