TL; DR: Triển khai lại bằng cách khởi động lại máy chủ (Kiểm tra bản cập nhật cuối cùng).
Đây là một câu hỏi cũ, nhưng vẫn chưa tìm ra giải pháp và tại thời điểm này tôi không biết tìm ở đâu.
Chúng tôi có ba ứng dụng web Java (Mùa xuân, không khởi động) được triển khai trên hai máy chủ: một ứng dụng nằm trên Công cụ điện toán GCP được triển khai trên Tomcat9 (cổng 80) và hai ứng dụng còn lại nằm trong máy chủ nội bộ của chúng tôi trên một phiên bản Tomcat8.5 ( cổng 8080 được chuyển hướng từ cổng 80). Cả hai hệ thống đều có Mysql8 và sử dụng khá nhiều cấu hình giống nhau để kết nối với nó: DataSource cho DB chính và ConnectionProvider cho người thuê.
Vấn đề là, khi được triển khai lại, một số kết nối cũ hơn từ nhóm (HikariCP) sẽ không bị hủy, trong khi những kết nối khác thì bị hủy. Các kết nối còn lại này là từ nhà cung cấp kết nối nhiều bên thuê của chúng tôi.Có thể nói, người thuê chính sẽ giết các kết nối cũ hơn một cách chính xác. Tất nhiên, điều này dẫn đến trường hợp khi chúng tôi có quá nhiều kết nối, chúng tôi sử dụng hết chúng, ném SQLExceptions. Tôi đã khắc phục điều đó bằng cách tăng số lượng kết nối, nhưng đây không phải là giải pháp.
Chúng tôi đang triển khai lại bằng cách chỉ cập nhật tệp chiến tranh mà không có GUI. Tôi khẳng định đây là nguyên nhân gây ra sự cố, nhưng không thực sự giải thích tại sao một số kết nối được đóng đúng cách và những kết nối khác thì không.
Những gì tôi đã thử:
- Tôi đã thấy các câu trả lời liên quan về vấn đề này (chủ yếu là xử lý PHP), trong đó
Các kết nối Mysql vẫn ở trạng thái Ngủ tốt sau khi hoàn thành công việc.
Tôi cũng đã thử các bản sửa lỗi được cung cấp trong các câu hỏi đó vì chúng
dường như cũng hợp lý cho trường hợp của tôi. Những thứ như giảm
các
chờ_thời gian chờ
và Interactivevive_timeout
đến 30 phút.
- Cấu hình HikariCP của chúng tôi tạm dừng các kết nối sau 10 phút và chúng có
thời gian sống tối đa
trong 15 phút. Ngay cả sau nhiều giờ, các conections không
đóng lại và chúng thực sự được làm mới sau 30 phút đó. Bằng cái này
Ý tôi là thời gian được hiển thị bởi truy vấn CHỌN * TỪ NHÓM information_schema. processlist THEO db;
lên tới 1799 (thậm chí ít hơn) và sau đó quay về 0. Tại sao? Tôi biết hệ thống không được người dùng sử dụng vào thời điểm đó và nhật ký cho thấy HikariCP chỉ biết 4 kết nối (Những kết nối tôi đã định cấu hình) thay vì lên đến 20 đôi khi 'hoạt động'.
Chúng tôi đang sử dụng Spring Data JPA, vì vậy tất cả việc quản lý kết nối được xử lý bởi Hibernate/JPA. Các kết nối cũng đang được Hikari làm mới đúng cách, vì vậy tôi không nghĩ có điều gì đó khiến các kết nối bị bỏ ngỏ trong mã.
Cho đến nay, tôi khẳng định rằng đó không phải là vấn đề với Hikari (và ý tôi là cấu hình của chúng tôi). Điều này khiến tôi tin rằng có điều gì đó kỳ lạ với cấu hình Cơ sở dữ liệu hoặc đơn giản là chúng tôi không triển khai lại đúng cách.
Tôi tin rằng vấn đề này sẽ biến mất nếu tôi xây dựng lại bố cục máy chủ (xin lỗi vì tôi thiếu từ vựng) bằng cách đặt cả hai ứng dụng web trong phiên bản Tomcat của riêng chúng và sử dụng Apache hoặc Nginx để ủy quyền cho chúng. Tôi đã thực hiện cấu hình này trong môi trường thử nghiệm của mình và tôi đã muốn thực hiện nó được một thời gian rồi, nhưng thật khó để biện minh cho sự thay đổi như vậy ở vị trí của tôi (Hầu như là một nhà phát triển phụ trợ chưa có nhiều kinh nghiệm mà bằng cách nào đó phụ trách việc này). Mặc dù vậy, đó là một thay đổi lớn, tôi sẽ mất vài ngày để làm việc với nhiều thứ hơn và tôi thực sự thà (đúng cách) sửa cấu hình hiện tại hơn là xây dựng lại máy chủ.
Các tùy chọn khác là lên lịch khởi động lại máy chủ + db. Hệ thống của chúng tôi là khu vực và vẫn còn ít người dùng của chúng tôi làm việc theo giờ thông thường, vì vậy họ sẽ không bao giờ nhận thấy hoạt động khởi động lại hàng ngày vào lúc, chẳng hạn như 3 giờ sáng. Tôi chỉ không thích điều này và nghĩ rằng nó không hiệu quả như tăng một cách mù quáng max_connections
IMO mỗi ngày.
Ngoài ra còn có tùy chọn xây dựng lại cách chúng tôi xử lý nhiều người thuê của mình. Chúng tôi đang sử dụng ConnectionProvider và các kết nối này là kết nối "bị lỗi". Tôi đã xem một số ví dụ về các cách tiếp cận khác bằng cách sử dụng DataSource và tôi biết DataSource không gặp sự cố này vì các kết nối cơ sở dữ liệu "chính" không hoạt động như mong đợi khi triển khai lại. Mặc dù vậy, tôi vẫn tin rằng đây là một vấn đề về cấu hình.
Do sự thiếu kinh nghiệm của tôi và có bao nhiêu điều tôi cần xem xét, tôi đoán là tôi đã bỏ qua điều gì đó trong tài liệu hoặc tôi không thực sự hiểu các cấu hình mà tôi đã chạm vào. Và cũng lạc lối như tôi, tôi đến để tìm kiếm kinh nghiệm của người khác về vấn đề này. Có điều gì khác mà tôi nên xem xét không? Tôi cũng đã thiết lập slow_query_logs
nhưng tệp đã nói vẫn trống sau nhiều ngày.
Có ai đã có loại vấn đề này trước đây? Nếu bạn cần thêm bất kỳ thông tin nào về cấu trúc hoặc cách triển khai của chúng tôi, vui lòng yêu cầu. Như bạn có thể đoán, chúng tôi là một công ty nhỏ vẫn đang học hỏi những điều này.
CẬP NHẬT:
Tôi đã đưa ra một số phương thức bổ sung trong chương trình phụ trợ của chúng tôi có thể giúp ích cho các kết nối bổ sung. Một số phương thức không được Ghi đè và vì chúng tôi đang mở rộng từ một lớp khác, siêu phương thức có thể không hoạt động. Các phương pháp này nhắm mục tiêu cụ thể đến cấu trúc dữ liệu mà các kết nối đang được truy cập từ đó.
Ngoài ra, sau một lần triển khai lại, tôi thấy số kết nối tăng từ 4 lên 8 (Dự kiến: 4 từ lần triển khai đầu tiên và 4 kết nối bổ sung từ lần triển khai lại) nhưng sau vài giờ, số lượng kết nối giảm xuống còn 6. Tôi hy vọng là như vậy kết thúc, nhưng ngày hôm sau chúng tôi lại có 8 kết nối đó.
Tệ hơn nữa, hôm nay tôi có cơ hội khởi động lại một số dịch vụ và thử nghiệm chỉ khởi động lại dịch vụ cơ sở dữ liệu. Lúc đầu, nó dường như hạ thấp các kết nối xuống 4 kết nối dự kiến cho mỗi người thuê, nhưng sau một thời gian, nó tăng lên đến cùng giá trị trước khi bắt đầu lại. Điều này cho tôi biết rằng các kết nối bị Tomcat giữ làm con tin (?), nghĩa là có lẽ có điều gì đó trong tài liệu đề cập đến hành vi này. Tôi chưa tìm được từ khóa phù hợp để tìm nó, nhưng đặt cược của tôi là xoay quanh ngữ cảnh, lĩnh vực hoặc một van.
Nếu tôi không thể tìm thấy gì, tôi sẽ tung ra một ConnectionProvider tùy chỉnh mà tôi đã mở rộng từ một EntityManagerFactoryBean
. Trong đó, tôi thiết lập một dừng lại()
phương pháp kích hoạt một @PreDestroy
phương pháp truy cập cấu trúc dữ liệu với các kết nối của người thuê và tắt chúng theo cách thủ công bằng các phương pháp riêng của Hikari. Về lý thuyết, đây là điều tôi có thể làm nhiều nhất từ mã để đóng các kết nối này. Nếu cách đó không hiệu quả và tôi cũng không thể tìm thấy bất cứ điều gì trong tài liệu của Tomcat, thì tôi cần lên tiếng và chọn giữa khởi động lại theo lịch trình hoặc xây dựng lại máy chủ + "triển khai lại phù hợp" (Dừng, Cập nhật, Bắt đầu).
CẬP NHẬT 2:
Tôi đã đầu tư vào ngày hôm qua để cố gắng đóng các kết nối theo cách thủ công bằng phương pháp được mô tả trong lần cập nhật trước và bằng một phương pháp khác giúp tôi ServletContextListener
. Không hoạt động, và phát hiện ra rằng phương pháp đóng lại()
trong HikariCPs nhà cung cấp kết nối không giới thiệu đến các kết nối, vì vậy rất tiếc. Tôi cũng đã quyết định thử và tự động tạo các Trình cung cấp kết nối trong một bean, với phương thức đóng/hủy thích hợp của nó nhưng vì phương thức mà tôi sử dụng không có ý nghĩa như vậy, nên tôi sẽ loại bỏ ý tưởng đó một phần.
Tiếp theo: Thay đổi từ nhà cung cấp kết nối
đến nguồn dữ liệu
. Nếu điều này hiệu quả, thì chúng ta có thể tiếp tục triển khai lại như chúng ta vẫn thường làm. Tôi sẽ thử ba phương pháp mà tôi đã nghĩ ra (Trong trường hợp các kết nối gặp vấn đề tương tự khi triển khai lại): @PreDestroy
phương pháp lặp lại bản đồ DataSources theo cách thủ công và đóng tất cả các kết nối có liên quan, tự động tạo và đăng ký tất cả Nguồn dữ liệu
s dưới dạng đậu (Có thể "nhóm" chúng bằng Giao diện hoặc thứ gì đó để MultiTenantResolver
có thể làm việc với nó hoặc thực hiện cách tiếp cận đầu tiên nhưng đóng các kết nối theo cách ServletContextListener
.
Một điều khác mà tôi nhận thấy là các kết nối đang được giữ ở mức cao hơn đối với bối cảnh ứng dụng web. Đây là thông tin quan trọng, nhưng tôi thực sự không hiểu tại sao một tập hợp các kết nối từ một ứng dụng không đóng vì tập hợp kia không phải tại sao Tomcat không để các chuỗi/kết nối đó chết sau khi hết thời gian. Nguồn của thông tin này là câu hỏi này từ StackOverflow.
Tôi đã âm thầm "cắt một phần của máy chủ" và thiết lập một môi trường thử nghiệm cá nhân bên cạnh môi trường thử nghiệm. Vì tôi chịu trách nhiệm về mặt kỹ thuật và điều này nhằm nỗ lực sửa chữa những thứ hiện đang diễn ra trong Sản xuất, tôi nghĩ mình có lý.
tôi có thể thử hỏi trong SO và HikariCPs Google Group, mặc dù với các mục đích khác nhau để giữ cho câu hỏi của tôi phù hợp với cả hai cộng đồng.
CẬP NHẬT 3
Việc thay đổi từ ConnectionProvider sang DataSource đã giải quyết được một nửa vấn đề và gây ra các lỗi mới, khó hiểu hơn:
- Trong khi hầu hết các nhóm được khởi tạo đúng cách ở 4 kết nối khi triển khai lại, hai trong số các nhóm đó vẫn ở trạng thái cũ (4 từ triển khai ban đầu + 4 của triển khai mới) và một, bằng cách nào đó, đã kết thúc với 12 khi triển khai lại. Đó là 4 ban đầu, 4 từ triển khai lại và chỉ là một số 4 bổ sung ngẫu nhiên.
- Trong khi kiểm tra bất kỳ hành vi kỳ lạ nào bằng hệ thống, tôi nhận thấy rằng mỗi khi tôi thay đổi đối tượng thuê, một nhóm mới lại được tạo. Sau đó, tôi phát hiện ra rằng trên thực tế, hai nhóm onlo đã được tạo khi khởi động và mọi nhóm khác chỉ được tạo khi được yêu cầu. Điều đó thực sự ổn, nhưng tôi vẫn có một đối tượng thuê với một số kết nối ngẫu nhiên khi khởi động khi sử dụng cơ sở dữ liệu cụ thể đó.
Sau đó, tôi đã thử tất cả các tùy chọn của mình và đóng các kết nối theo cách thủ công trong khi tắt máy, nhưng tôi thực sự không thể nói rằng bất kỳ tùy chọn nào trong số này hoạt động.
Có vẻ như tôi chỉ cần thay đổi cách hoạt động của máy chủ. Tôi hơi ngạc nhiên rằng dường như tôi không thể tìm thấy câu trả lời cho dù tôi có tìm kiếm gì đi nữa và tôi cảm thấy thất vọng vì sau tất cả thời gian tôi đầu tư vào việc này, tất cả có thể sẽ được giải quyết thành một tệp hàng loạt. triển khai lại bằng cách tắt, thay thế và khởi động lại.
Trong Tài liệu Hikari có tuyên bố rằng đối với các triển khai nóng (Và triển khai lại nóng theo phần mở rộng), người ta cần đóng các kết nối, nhưng nó nói về Nguồn dữ liệu, không có Trình cung cấp kết nối. Tại thời điểm này, tôi thậm chí đang cân nhắc việc bỏ Hikari để tìm một giải pháp khác nhưng tôi cũng cảm thấy điều này là không cần thiết và là sản phẩm của sự thất vọng của tôi.
Dù sao, tôi sẽ tiếp tục thử những thứ tôi đoán. Còn rất ít để tôi cố gắng.
Cập nhật 4:
Vâng, cuối cùng tôi đã từ bỏ. Tôi đã nói chuyện với người mà tôi cần nói chuyện và thực sự có thời hạn để hoàn thành những thứ khác, bao gồm cả một cuộc đại tu nhỏ máy chủ của chúng tôi. Đó là một phần lý do tôi cũng bắt đầu tìm hiểu về vấn đề này.Dù sao, với thời hạn này và vì tôi chưa tìm ra giải pháp, tôi sẽ xây dựng lại cấu trúc máy chủ: Tôi sẽ sử dụng máy chủ Proxy để cung cấp cho mỗi ứng dụng một phiên bản Tomcat trong các cổng được bảo mật, khác nhau. Bằng cách đó, khách hàng không cần phải thay đổi bất cứ điều gì. Bên trong, tôi sẽ cung cấp cho những người đứng đầu dự án các tập lệnh triển khai sẽ cập nhật nhánh triển khai của họ, tạo WAR cập nhật, dừng dịch vụ Tomcat cụ thể của họ, xóa các bản dựng trước đó, thêm bản dựng mới và khởi động dịch vụ Tomcat. Bằng cách này, tôi không cần phải lo lắng về các kết nối, cuối cùng cung cấp cho từng dự án sự độc lập cần thiết và tự động hóa việc triển khai để tránh nhiều lỗi nhất có thể.
Không nói dối đâu, nó hơi tệ khi kết thúc theo cách này nhưng không phải lúc nào chúng ta cũng thắng, phải không?