TL; DR: ปรับใช้ใหม่โดยรีสตาร์ทเซิร์ฟเวอร์ (ตรวจสอบการอัปเดตขั้นสุดท้าย)
นี่เป็นคำถามเก่า แต่ยังไม่พบวิธีแก้ปัญหาและ ณ จุดนี้ฉันไม่รู้ว่าจะดูที่ไหน
เรามีเว็บแอป Java (Spring, non boot) สามรายการที่ปรับใช้บนเซิร์ฟเวอร์สองเครื่อง: แอปหนึ่งอยู่บน GCP Compute Engine ที่ปรับใช้บน Tomcat9 (พอร์ต 80) และอีกสองแอปอยู่ในเซิร์ฟเวอร์ภายในของเราบน Tomcat8.5 หนึ่งอินสแตนซ์ ( เปลี่ยนเส้นทางพอร์ต 8080 จากพอร์ต 80) ทั้งสองระบบมี Mysql8 และใช้คอนฟิกูเรชันเดียวกันในการเชื่อมต่อกับมัน: DataSource สำหรับ DB หลักและ ConnectionProvider สำหรับผู้เช่า
ปัญหาคือเมื่อปรับใช้ใหม่ การเชื่อมต่อเก่าบางส่วนจากพูล (HikariCP) จะไม่ถูกหยุดทำงาน ในขณะที่การเชื่อมต่ออื่นๆ การเชื่อมต่อเหล่านี้ยังคงอยู่จากผู้ให้บริการการเชื่อมต่อแบบหลายผู้เช่าของเราผู้เช่าหลักที่จะพูดฆ่าการเชื่อมต่อที่เก่ากว่าอย่างถูกต้อง แน่นอนว่าสิ่งนี้นำไปสู่กรณีที่เรามีการเชื่อมต่อมากมายที่เราใช้งานไม่ได้ โยน SQLExceptions ฉันได้แก้ไขโดยการเพิ่มจำนวนการเชื่อมต่อ แต่นี่ไม่ใช่วิธีแก้ปัญหา
เรากำลังปรับใช้ใหม่โดยเพียงแค่อัปเดตไฟล์ war โดยไม่มี GUI ฉันมั่นใจว่านี่คือสาเหตุของปัญหา แต่ไม่ได้อธิบายจริงๆ ว่าทำไมการเชื่อมต่อบางอย่างจึงปิดอย่างถูกต้องและบางอย่างไม่ได้
สิ่งที่ฉันได้ลอง:
- ฉันเคยเห็นคำตอบที่เกี่ยวข้องเกี่ยวกับเรื่องนี้ (ส่วนใหญ่เกี่ยวข้องกับ PHP) โดยที่
การเชื่อมต่อ Mysql อยู่ในสถานะสลีปหลังจากทำงานเสร็จแล้ว
ฉันได้ลองแก้ไขที่ให้ไว้ในคำถามเหล่านั้นด้วยเพราะพวกเขา
ดูสมเหตุสมผลสำหรับสถานการณ์กรณีของฉันด้วย เช่น ลดลง
เดอะ
wait_timeout
และ interactivevive_timeout
ถึง 30 นาที
- การกำหนดค่า HikariCP ของเราไม่ได้ใช้งานการเชื่อมต่อหลังจากผ่านไป 10 นาทีและทำได้
อายุการใช้งานสูงสุด
15 นาที แม้จะผ่านไปหลายชั่วโมงแล้วก็ตาม
ปิดและพวกเขาจะได้รับการรีเฟรชจริง ๆ หลังจาก 30 นาทีนั้น โดยสิ่งนี้
ฉันหมายความว่าเวลาที่แสดงโดยแบบสอบถาม เลือก * จาก information_schema.processlist GROUP BY db;
ขึ้นไปที่ 1799 (แม้แต่น้อย) แล้วกลับไปที่ 0 ทำไม ฉันรู้ว่าระบบไม่ได้ถูกใช้งานโดยผู้ใช้ในขณะนั้น และบันทึกแสดงว่า HikariCP รับรู้การเชื่อมต่อเพียง 4 รายการ (การเชื่อมต่อที่ฉันกำหนดค่าไว้) แทนที่จะเป็น 20 รายการในบางครั้งที่ 'ทำงานอยู่'
เราใช้ Spring Data JPA ดังนั้นการจัดการการเชื่อมต่อทั้งหมดจึงจัดการโดย Hibernate/JPA การเชื่อมต่อยังได้รับการรีเฟรชอย่างถูกต้องโดย Hikari ดังนั้นฉันไม่คิดว่าเป็นสิ่งที่เปิดการเชื่อมต่อทิ้งไว้ในรหัส
จนถึงตอนนี้ ฉันแน่ใจว่านั่นไม่ใช่ปัญหากับ Hikari (และจากนี้ฉันหมายถึงการกำหนดค่าของเรา) สิ่งนี้ทำให้ฉันเชื่อว่ามีบางอย่างแปลก ๆ กับการกำหนดค่าฐานข้อมูล หรือเราไม่ได้ปรับใช้อย่างถูกต้อง
ฉันเชื่อว่าปัญหานี้จะหมดไปหากฉันสร้างเลย์เอาต์ของเซิร์ฟเวอร์ใหม่ (ขออภัยที่ฉันไม่มีคำศัพท์) โดยมีทั้งเว็บแอปในอินสแตนซ์ Tomcat ของตัวเอง และใช้ Apache หรือ Nginx เพื่อพร็อกซี ฉันได้ทำการกำหนดค่านี้ในสภาพแวดล้อมการทดสอบแล้ว และฉันต้องการจะทำมาระยะหนึ่งแล้ว แต่มันยากที่จะพิสูจน์การเปลี่ยนแปลงในตำแหน่งของฉัน (ค่อนข้างจะเป็นผู้พัฒนาแบ็กเอนด์ที่ยังไม่ใช่รุ่นน้อง เป็นผู้รับผิดชอบในเรื่องนี้) ถึงกระนั้นก็เป็นการเปลี่ยนแปลงครั้งใหญ่ ฉันจะใช้เวลาสองสามวันในขณะที่ฉันทำงานเพิ่มเติม และฉันค่อนข้าง (ถูกต้อง) แก้ไขการกำหนดค่าปัจจุบันมากกว่าสร้างเซิร์ฟเวอร์ใหม่
ตัวเลือกอื่นๆ คือการกำหนดเวลาเซิร์ฟเวอร์ + db รีสตาร์ท ระบบของเราเป็นแบบภูมิภาค และผู้ใช้จำนวนน้อยของเรายังทำงานตามเวลาปกติ ดังนั้นพวกเขาจะไม่สังเกตเห็นการรีสตาร์ททุกวันในเวลา 3:00 น. ฉันไม่ชอบสิ่งนี้และคิดว่ามันไร้ประสิทธิภาพเท่ากับการเพิ่มขึ้นแบบสุ่มสี่สุ่มห้า max_connections
IMO ทุกวัน
นอกจากนี้ยังมีตัวเลือกในการสร้างใหม่วิธีที่เราจัดการกับผู้เช่าหลายรายของเรา เรากำลังใช้ ConnectionProvider และการเชื่อมต่อเหล่านี้เป็นการเชื่อมต่อที่ "ผิดพลาด" ฉันได้เห็นตัวอย่างวิธีการอื่นๆ โดยใช้ DataSource และฉันรู้ว่า DataSource ไม่มีปัญหานี้เนื่องจากการเชื่อมต่อฐานข้อมูล "หลัก" หยุดทำงานตามที่คาดไว้ในการปรับใช้ใหม่ ถึงกระนั้น ฉันก็ยังเชื่อว่านี่เป็นปัญหาการกำหนดค่า
เนื่องจากไม่มีประสบการณ์ของฉันและฉันต้องตรวจสอบกี่สิ่ง ฉันเดาว่าฉันมองข้ามบางสิ่งในเอกสารไป หรือฉันแค่ไม่เข้าใจการกำหนดค่าที่ฉันสัมผัสอย่างแท้จริง และฉันก็หลงทางเหมือนกัน ฉันมาหาประสบการณ์อื่นในเรื่องนี้ มีอะไรอีกบ้างที่ฉันควรตรวจสอบ ฉันยังตั้งค่า slow_query_logs
แต่ไฟล์ดังกล่าวยังคงว่างเปล่าหลังจากผ่านไปหลายวัน
ใครเคยมีปัญหาประเภทนี้มาก่อนหรือไม่? หากคุณต้องการข้อมูลเพิ่มเติมเกี่ยวกับโครงสร้างหรือการปรับใช้ของเรา โปรดอย่าลังเลที่จะร้องขอ คุณอาจเดาได้ว่าเราเป็นบริษัทเล็กๆ ที่ยังคงเรียนรู้เกี่ยวกับสิ่งนี้
อัปเดต:
ฉันได้แนะนำวิธีพิเศษบางอย่างในแบ็กเอนด์ของเรา ซึ่งน่าจะช่วยในเรื่องการเชื่อมต่อเพิ่มเติมได้ เมธอดบางเมธอดที่ไม่ได้เขียนทับ และเนื่องจากเรากำลังขยายจากคลาสอื่น เมธอด super จึงอาจไม่ทำงาน วิธีการเหล่านี้กำหนดเป้าหมายเฉพาะโครงสร้างข้อมูลที่มีการเข้าถึงการเชื่อมต่อ
นอกจากนี้ หลังจากการปรับใช้ซ้ำหนึ่งครั้ง ฉันเห็นการเชื่อมต่อเปลี่ยนจาก 4 เป็น 8 (คาดว่า: 4 จากการปรับใช้ครั้งแรก และอีก 4 จากการปรับใช้ซ้ำ) แต่หลังจากนั้นไม่กี่ชั่วโมง จำนวนการเชื่อมต่อลดลงเหลือ 6 ฉันหวังว่ามันจะเป็นอย่างนั้น ก็จบ แต่วันต่อมา เราก็มีสายสัมพันธ์ทั้ง 8 นั้นอีก
ที่แย่ไปกว่านั้น วันนี้ฉันมีโอกาสเริ่มบริการบางอย่างใหม่ และทดลองรีสตาร์ทเฉพาะบริการฐานข้อมูลเท่านั้น ในตอนแรกดูเหมือนว่าจะลดการเชื่อมต่อลงเหลือ 4 ต่อผู้เช่าที่คาดไว้ แต่หลังจากนั้นไม่นานก็เพิ่มขึ้นเป็นค่าเดิมก่อนที่จะรีสตาร์ท สิ่งนี้บอกฉันว่าความสัมพันธ์ถูกจับเป็นตัวประกัน (?) โดย Tomcat ซึ่งหมายความว่าอาจมีบางอย่างในเอกสารที่กล่าวถึงพฤติกรรมนี้ ฉันไม่พบคำหลักที่เหมาะสมในการค้นหา แต่การเดิมพันของฉันอยู่ที่บริบท ขอบเขต หรือหนึ่งวาล์ว
หากฉันไม่พบสิ่งใด ฉันจะเปิดตัว ConnectionProvider ที่สร้างขึ้นเองซึ่งขยายมาจาก EntityManagerFactoryBean
. ในนี้ฉันตั้งค่า หยุด()
วิธีการทริกเกอร์ @พรีเดสทรอย
วิธีการเข้าถึงโครงสร้างข้อมูลด้วยการเชื่อมต่อของผู้เช่าและปิดระบบด้วยตนเองด้วยวิธีของ Hikari ตามทฤษฎีแล้ว นี่คือสิ่งที่ทำได้มากที่สุดจากโค้ดเพื่อปิดการเชื่อมต่อนี้ หากไม่ได้ผลและฉันไม่พบสิ่งใดในเอกสารของ Tomcat ฉันจะต้องพูดและเลือกระหว่างการรีสตาร์ทตามกำหนดเวลาหรือการสร้างเซิร์ฟเวอร์ใหม่ + "การปรับใช้ใหม่ที่เหมาะสม" (หยุด อัปเดต เริ่ม)
อัปเดต 2:
ฉันลงทุนเมื่อวานเพื่อพยายามปิดการเชื่อมต่อด้วยตนเองโดยใช้วิธีการที่อธิบายไว้ในการอัปเดตครั้งล่าสุด และด้วยวิธีอื่นที่ช่วยตัวเองด้วย a ServletContextListener
. ไม่ได้ผลและพบว่าวิธีการ ปิด()
ในผู้ให้บริการการเชื่อมต่อ HikariCPs ไม่ได้อ้างถึงการเชื่อมต่อ ดังนั้น yikes ฉันยังตัดสินใจที่จะลองและสร้าง ConnectionProviders แบบไดนามิกใน bean ด้วยวิธีการปิด/ทำลายที่เหมาะสม แต่เนื่องจากวิธีที่ฉันใช้ wan ไม่ได้มีไว้สำหรับสิ่งนั้น ฉันจะละทิ้งแนวคิดนั้นบางส่วน
ถัดไป: เปลี่ยนจาก ผู้ให้บริการการเชื่อมต่อ
ถึง แหล่งข้อมูล
. หากวิธีนี้ใช้ได้ผล เราก็สามารถปรับใช้ซ้ำได้ตามปกติ ฉันจะลองใช้สามวิธีที่ฉันพบ (ในกรณีที่การเชื่อมต่อมีปัญหาเดียวกันในการปรับใช้ซ้ำ): การตั้งค่า @พรีเดสทรอย
วิธีการวนซ้ำแผนที่แหล่งข้อมูลด้วยตนเองและปิดการเชื่อมต่อที่เกี่ยวข้องทั้งหมด สร้างและลงทะเบียนทั้งหมดแบบไดนามิก แหล่งข้อมูล
s as bean (อาจ "จัดกลุ่ม" พวกมันด้วยส่วนต่อประสานหรืออะไรทำนองนั้น MultiTenantResolver
สามารถทำงานร่วมกับมันหรือใช้แนวทางแรก แต่ปิดการเชื่อมต่อใน ServletContextListener
.
อีกสิ่งหนึ่งที่ฉันพบคือการเชื่อมต่อนั้นถูกรักษาให้อยู่ในระดับที่สูงกว่าบริบทของเว็บแอป นี่เป็นข้อมูลสำคัญ แต่จริงๆ แล้วฉันไม่เข้าใจมากพอว่าทำไมชุดการเชื่อมต่อจากแอปหนึ่งถึงไม่ปิดในขณะที่อีกชุดหนึ่งไม่ใช่เหตุใด Tomcat จึงไม่ปล่อยให้เธรด/การเชื่อมต่อเหล่านั้นตายหลังจากหมดเวลา แหล่งที่มาของข้อมูลนี้คือ คำถามนี้จาก StackOverflow.
ฉันสามารถ "ตัดชิ้นส่วนของเซิร์ฟเวอร์" ออกอย่างเงียบ ๆ และตั้งค่าสภาพแวดล้อมการทดสอบส่วนบุคคลด้วยสภาพแวดล้อมการทดสอบ เนื่องจากฉันรับผิดชอบด้านเทคนิคและสิ่งนี้ในความพยายามที่จะแก้ไขสิ่งที่เกิดขึ้นใน Production ฉันคิดว่าฉันมีเหตุผล
ฉันอาจจะลอง ถามใน SO และ HikariCPs Google Group แม้ว่าจะมีวัตถุประสงค์ที่แตกต่างกันเพื่อให้คำถามของฉันเกี่ยวข้องกับทั้งสองชุมชน
อัพเดท 3
การเปลี่ยนจาก ConnectionProvider เป็น DataSource ช่วยแก้ปัญหาได้ครึ่งหนึ่งและนำข้อผิดพลาดใหม่ที่ทำให้สับสนมากขึ้น:
- ในขณะที่พูลส่วนใหญ่เริ่มต้นอย่างเหมาะสมที่การเชื่อมต่อ 4 ครั้งในการปรับใช้ซ้ำ พูล 2 พูลนั้นยังคงอยู่ในลักษณะการทำงานแบบเก่า (4 จากการปรับใช้เดิม + 4 ของการปรับใช้ใหม่) และอีกหนึ่งกลุ่มจบลงด้วย 12 เมื่อปรับใช้ซ้ำ นั่นคือ 4 ดั้งเดิม 4 จากการปรับใช้ใหม่ และสุ่ม 4 พิเศษ
- ขณะทดสอบพฤติกรรมแปลก ๆ โดยใช้ระบบ ฉันสังเกตเห็นว่าทุกครั้งที่ฉันเปลี่ยนผู้เช่า กลุ่มใหม่จะถูกสร้างขึ้น ภายหลังฉันพบว่าอันที่จริงแล้ว มีการสร้างพูลสองพูลเมื่อเริ่มต้น และพูลอื่น ๆ ทั้งหมดถูกสร้างขึ้นเมื่อมีการร้องขอเท่านั้น ไม่เป็นไรจริงๆ แต่ฉันยังมีผู้เช่าหนึ่งรายที่มีการเชื่อมต่อแบบสุ่มเมื่อเริ่มต้นซึ่งดำเนินต่อไปเมื่อใช้ฐานข้อมูลนั้น
จากนั้นฉันลองใช้ตัวเลือกทั้งหมดของฉันและปิดการเชื่อมต่อด้วยตนเองระหว่างการปิดเครื่อง แต่ฉันไม่สามารถพูดได้ว่าวิธีนี้ใช้ได้ผล
ดูเหมือนว่าฉันต้องเปลี่ยนวิธีการทำงานของเซิร์ฟเวอร์ ฉันรู้สึกประหลาดใจเล็กน้อยที่ดูเหมือนจะหาคำตอบไม่ได้ไม่ว่าฉันจะตรวจสอบอะไร และฉันรู้สึกหงุดหงิดที่หลังจากทุ่มเทไปกับสิ่งนี้มาตลอด ทุกอย่างน่าจะแก้ไขเป็นแบตช์ไฟล์ที่ดูแล การปรับใช้ใหม่โดยการปิด แทนที่ และเริ่มต้นใหม่อีกครั้ง
ในเอกสาร Hikari มีการระบุว่าสำหรับการปรับใช้แบบด่วน (และการปรับใช้แบบร้อนโดยการขยาย) จำเป็นต้องปิดการเชื่อมต่อ แต่พูดถึงแหล่งข้อมูล ไม่ใช่ ConnectionProvider ณ จุดนี้ ฉันกำลังพิจารณาที่จะทิ้ง Hikari เพื่อหาทางออกอื่น แต่ฉันรู้สึกว่านี่ไม่จำเป็นและเป็นผลมาจากความหงุดหงิดของฉัน
อย่างไรก็ตาม ฉันจะพยายามทำสิ่งที่ฉันเดาต่อไป เหลือไม่มากให้ฉันลอง
อัปเดต 4:
ในที่สุดฉันก็ยอมแพ้ ฉันได้คุยกับคนที่ฉันต้องการคุยและได้รับกำหนดเส้นตายในการทำสิ่งอื่นๆ ให้เสร็จ รวมถึงการยกเครื่องเซิร์ฟเวอร์ของเราเล็กน้อย นั่นเป็นส่วนหนึ่งของเหตุผลที่ฉันเริ่มมองหาสิ่งนี้เช่นกันอย่างไรก็ตาม เมื่อถึงเส้นตายนี้และเนื่องจากฉันไม่พบวิธีแก้ไข ฉันจะสร้างโครงสร้างเซิร์ฟเวอร์ใหม่: ฉันจะใช้พร็อกซีเซิร์ฟเวอร์เพื่อให้แต่ละแอปมีอินสแตนซ์ Tomcat ในพอร์ตต่างๆ ที่ปลอดภัย ด้วยวิธีนี้ลูกค้าไม่จำเป็นต้องเปลี่ยนแปลงอะไรเลย ข้างใน ฉันจะให้สคริปต์การปรับใช้แก่หัวหน้าโครงการที่จะอัปเดตสาขาการปรับใช้ สร้าง WAR ที่อัปเดต หยุดบริการ Tomcat เฉพาะของพวกเขา ล้างบิลด์ก่อนหน้า เพิ่มบิลด์ใหม่ และเริ่มบริการ Tomcat agian ด้วยวิธีนี้ฉันจึงไม่ต้องกังวลเกี่ยวกับการเชื่อมต่อ ในที่สุดให้แต่ละโครงการมีความเป็นอิสระที่จำเป็นและทำให้การปรับใช้โดยอัตโนมัติเพื่อหลีกเลี่ยงข้อผิดพลาดให้มากที่สุดเท่าที่จะทำได้
ไม่ได้โกหก มันค่อนข้างแย่ที่จบลงแบบนี้ แต่เราไม่ได้ชนะเสมอไปใช่ไหม?