1. บทนำ
เทคโนโลยีการรวมกันถูกนำมาใช้กันอย่างแพร่หลายใน Java ในระยะสั้นจะใช้พูลวัตถุเพื่อจัดเก็บอินสแตนซ์ที่มีจำนวน จำกัด นักพัฒนาได้รับอินสแตนซ์จากพูลวัตถุจากนั้นสลับกลับไปที่พูลวัตถุหลังการใช้งานซึ่งจะช่วยลดค่าใช้จ่ายของการสร้างวัตถุบ่อยครั้งของระบบและทำลายวัตถุในระดับหนึ่ง พูลเธรด Java และพูลการเชื่อมต่อฐานข้อมูลเป็นแอพพลิเคชั่นทั่วไป แต่ไม่ใช่วัตถุทั้งหมดที่เหมาะสมสำหรับการรวม การรวมวัตถุที่มีค่าใช้จ่ายค่อนข้างเล็กจะส่งผลกระทบต่อประสิทธิภาพเนื่องจากการบำรุงรักษาพูลวัตถุยังต้องใช้ค่าใช้จ่ายทรัพยากรบางอย่าง สำหรับวัตถุที่มีค่าใช้จ่ายสูงและการสร้างและการใช้งานบ่อยครั้งการใช้เทคโนโลยีการรวมจะปรับปรุงประสิทธิภาพอย่างมาก
มีกลุ่มการเชื่อมต่อฐานข้อมูลที่เป็นผู้ใหญ่มากมายในอุตสาหกรรมเช่น C3P0, DBCP, Proxool และ Druid ของอาลีบาบา มีหลายโอเพ่นและคุณสามารถค้นหาซอร์สโค้ดบน GitHub นักพัฒนาสามารถเลือกได้ตามความต้องการของตนเองและลักษณะและประสิทธิภาพของพวกเขา บทความนี้เป็นเพียงการเข้าใจเทคโนโลยีการรวมการเรียนรู้และใช้พูลการเชื่อมต่อฐานข้อมูลอย่างง่าย หากมีข้อผิดพลาดใด ๆ ฉันหวังว่าจะวิพากษ์วิจารณ์และแก้ไขพวกเขา
2. การออกแบบ
คลาสหลักและอินเทอร์เฟซ
.ConnectionParam - คลาสพารามิเตอร์การเชื่อมต่อฐานข้อมูลซึ่งรับผิดชอบในการกำหนดค่าการเชื่อมต่อฐานข้อมูลและพารามิเตอร์ที่เกี่ยวข้องกับพูลการเชื่อมต่อ การใช้งานโดยใช้ตัวสร้าง
รหัสผ่านผู้ใช้ URL ไดรเวอร์ - จำเป็นต้องเชื่อมต่อกับฐานข้อมูล
MinConnection - จำนวนการเชื่อมต่อขั้นต่ำ
MaxConnection - จำนวนการเชื่อมต่อสูงสุด
Minidle - จำนวนขั้นต่ำของการเชื่อมต่อที่ไม่ได้ใช้งาน
Maxwait - เวลารอสูงสุด
ไดรเวอร์สตริงสุดท้ายส่วนตัว URL สตริงสุดท้ายส่วนตัว; ผู้ใช้สตริงสุดท้ายส่วนตัว; รหัสผ่านสตริงสุดท้ายส่วนตัว การเชื่อมต่อ int สุดท้ายส่วนตัว; MaxConnection int สุดท้าย minidle int สุดท้ายส่วนตัว; Maxwait Long Final Long;
.ConnectionPool - พูลการเชื่อมต่อฐานข้อมูล
ConnectionPool Constructor ได้รับการประกาศว่าเป็นการป้องกันห้ามการสร้างภายนอกและได้รับการจัดการอย่างสม่ำเสมอโดย ConnectPoolFactory
ConnectionPool ใช้เมธอด DataSource Interface และ Re-GetConnection ()
ConnectionPool ถือสองคอนเทนเนอร์ - หนึ่งคิวเก็บการเชื่อมต่อที่ไม่ได้ใช้งานและเวกเตอร์อื่น ๆ (พิจารณาการซิงโครไนซ์) เก็บการเชื่อมต่อที่ใช้งาน
เมื่อนักพัฒนาใช้การเชื่อมต่อฐานข้อมูลจะถูกดึงมาจากคิวและหากไม่มีมันจะส่งคืนว่างเปล่า เมื่อการเชื่อมต่อปิดเสร็จสมบูรณ์จะถูกนำกลับไปที่เวกเตอร์
ConnectPool ให้กลไกการขยายตัวแบบไดนามิกอย่างง่ายโดยใช้ minidle และ maxconnection
INT int intial_size ครั้งสุดท้ายส่วนตัว = 5; สตริงสุดท้ายคงที่ส่วนตัว close_method = "ปิด"; เครื่องบันทึกเครื่องบันทึกแบบสแตติกส่วนตัว; ขนาด int ส่วนตัว; ConnectionParam Private ConnectionParam; arrayblockingqueue ส่วนตัว <การเชื่อมต่อ> IdleconnectionQueue; เวกเตอร์ส่วนตัว <การเชื่อมต่อ> BusyconnectionVector;
.ConnectionPoolFactory - คลาสการจัดการพูลการเชื่อมต่อ
ConnectPoolfactory ถือสแตติกพร้อมกันเพื่อจัดเก็บวัตถุพูลการเชื่อมต่อ
ConnectPoolFactory ช่วยให้สามารถสร้างพูลการเชื่อมต่อได้หลายแบบด้วยการกำหนดค่าที่แตกต่างกันของฐานข้อมูลที่แตกต่างกัน
นักพัฒนาจำเป็นต้องลงทะเบียน (ผูก) กลุ่มการเชื่อมต่อที่มีชื่อเฉพาะเป็นครั้งแรกจากนั้นรับการเชื่อมต่อจากกลุ่มการเชื่อมต่อที่ระบุในแต่ละครั้ง
หากไม่มีการใช้งานพูลการเชื่อมต่ออีกต่อไปผู้พัฒนาสามารถออกจากระบบ (ยกเลิก) พูลการเชื่อมต่อ
แผนที่คงที่ส่วนตัว <String, ConnectPool> poolMap = ใหม่พร้อมกันพร้อมกัน <> (); การเชื่อมต่อแบบคงที่สาธารณะ getConnection (String poolname) พ่น sqlexception {nameCheck (ชื่อ poolname); ConnectionPool ConnectionPool = PoolMap.get (ชื่อ poolName); ส่งคืน ConnectionPool.getConnection (); } โมฆะคงที่สาธารณะ registerConnectionPool (ชื่อสตริง, ConnectionParam ConnectionParam) {registerCheck (ชื่อ); poolmap.put (ชื่อ, ConnectionPool ใหม่ (ConnectionParam)); } // ให้ GC เป็นโมฆะสาธารณะคงที่ unregisterConnectionPool (ชื่อสตริง) {nameCheck (ชื่อ); Final ConnectionPool ConnectionPool = PoolMap.get (ชื่อ); poolmap.remove (ชื่อ); เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะ run () {connectionPool.clear ();}}) เริ่มต้น (); -รหัสหลัก
รหัสหลักของพูลการเชื่อมต่อฐานข้อมูลคือวิธี getConnection () โดยปกติหลังจากที่นักพัฒนาได้ประมวลผลการดำเนินการฐานข้อมูลวิธีการปิด () จะถูกเรียก การเชื่อมต่อควรปิดและควรมีการปล่อยทรัพยากร ในพูลการเชื่อมต่อฐานข้อมูลเมื่อผู้ใช้เรียกวิธีการปิด () การเชื่อมต่อไม่ควรปิดโดยตรง แต่ควรนำกลับเข้าไปในพูลและนำกลับมาใช้ใหม่ ที่นี่ใช้กลไกพร็อกซีแบบไดนามิก Java GetConnection ส่งคืนการเชื่อมต่อ "ของจริง" แต่เป็นคลาสพร็อกซีที่กำหนดเอง (คลาสที่ไม่ระบุชื่อถูกใช้ที่นี่) เมื่อผู้ใช้เรียกวิธีการปิด () มันจะสกัดกั้นและนำกลับเข้าไปในพูล สำหรับพร็อกซีแบบไดนามิกคุณสามารถอ้างถึงบล็อกอื่น "Java Dynamic Proxy Simple Application"
@Override การเชื่อมต่อสาธารณะ getConnection () พ่น sqlexception {ลอง {การเชื่อมต่อสุดท้ายการเชื่อมต่อ = idleConnectionQueue.poll (ConnectionParam.getMaxWait (), TimeUnit.milliseconds); if (connection == null) {logger.info (emgtymsg ()); ensurecapacity (); คืนค่า null; } busyconnectionVector.add (การเชื่อมต่อ); return (การเชื่อมต่อ) proxy.newproxyinstance (this.getClass (). getClassLoader (), คลาสใหม่ [] {connection.class}, ใหม่ rechocationHandler () {@Override วัตถุสาธารณะเรียกใช้ args); } catch (interruptedException e) {e.printStackTrace (); } return null; -2. ใช้
ขั้นแรกผู้ใช้จะสร้างพารามิเตอร์พูลการเชื่อมต่อฐานข้อมูล (ConnectionParam) รวมถึงไดรเวอร์, URL, ผู้ใช้, รหัสผ่านที่จำเป็น คุณสามารถปรับแต่งตัวเลือกเช่นการเชื่อมต่อ minconnection, maxconnection ฯลฯ หากไม่ได้ตั้งค่าให้ใช้ค่าเริ่มต้นของระบบ นี่คือประโยชน์ของการใช้ตัวสร้างเพื่อสร้างแอตทริบิวต์จำนวนมากรวมถึงแอตทริบิวต์ที่ต้องการและแอตทริบิวต์เสริม จากนั้นลงทะเบียนพูลการเชื่อมต่อด้วย ConnectionPoolFactory ด้วยชื่อเฉพาะและในที่สุดก็รับการเชื่อมต่อโดยเรียกใช้วิธีการเชื่อมต่อ PoolFactory Static Factory
String driver = "com.mysql.jdbc.driver"; string url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; สตริงรหัสผ่าน = "รูท"; ConnectionParam ConnectionParam = ใหม่ ConnectionParam.ConnectionParambuilder (ไดรเวอร์, URL, ผู้ใช้, รหัสผ่าน) .build (); ConnectPoolFactory.registerConnectionPool ("ทดสอบ", ConnectionParam); Connection Connection = ConnectionPoolFactory.getConnection ("ทดสอบ");3. รหัส
.paramconfiguration
Database Package.Config; Import Java.io.serializable;/** * พารามิเตอร์การเชื่อมต่อฐานข้อมูล * สร้างโดย Michael Wong เมื่อปี 2016/1/18 */คลาสสาธารณะ ParamConfiguration ใช้ serializable {สาธารณะคงที่สุดท้าย int min_connection = 5; สาธารณะคงที่สุดท้าย int max_connection = 50; สาธารณะคงที่สุดท้าย int min_idle = 5; สาธารณะคงที่สุดท้าย Long Max_wait = 30000; paramconfiguration ส่วนตัว () {}}. builder
ฐานข้อมูลแพ็คเกจ;/** * Builder * สร้างโดย Michael Wong เมื่อวันที่ 2016/1/18 */ตัวสร้างอินเตอร์เฟสสาธารณะ <t> {t build ();}.ConnectionParam
ฐานข้อมูลแพ็คเกจ; นำเข้าฐานข้อมูล. config.paramconfiguration;/** * พารามิเตอร์การเชื่อมต่อฐานข้อมูล * สร้างโดย Michael Wong เมื่อปี 2016/1/18 */คลาสสาธารณะ ConnectionParam {ไดรเวอร์สตริงสุดท้ายส่วนตัว; URL สตริงสุดท้ายส่วนตัว; ผู้ใช้สตริงสุดท้ายส่วนตัว; รหัสผ่านสตริงสุดท้ายส่วนตัว การเชื่อมต่อ int สุดท้ายส่วนตัว; MaxConnection int สุดท้าย minidle int สุดท้ายส่วนตัว; Maxwait Long Final Long; Private ConnectionParam (ผู้สร้าง ConnectionParambuilder) {this.driver = builder.driver; this.url = builder.url; this.user = builder.user; this.password = builder.password; this.minconnection = builder.minconnection; this.maxConnection = builder.maxConnection; this.minidle = builder.minidle; this.maxwait = builder.maxwait; } Public String getDriver () {return this.driver; } สตริงสาธารณะ getUrl () {return this.url; } สตริงสาธารณะ getUser () {return this.user; } สตริงสาธารณะ getPassword () {return this.password; } public int getMinconnection () {return this.minconnection; } public int getMaxConnection () {return this.maxConnection; } public int getMinidle () {return this.minidle; } สาธารณะยาว getMaxWait () {return this.maxwait; } คลาสสแตติกระดับสาธารณะ ConnectionParambuilder ใช้ Builder <NectionParam> {// พารามิเตอร์ที่ต้องการไดรเวอร์สตริงสุดท้ายส่วนตัว; URL สตริงสุดท้ายส่วนตัว; ผู้ใช้สตริงสุดท้ายส่วนตัว; รหัสผ่านสตริงสุดท้ายส่วนตัว // พารามิเตอร์ทางเลือก - เริ่มต้นเป็นค่าเริ่มต้นค่าเริ่มต้น int minconnection = paramConfiguration.min_connection; maxConnection ส่วนตัว = paramConfiguration.max_connection; ส่วนตัว int minidle = paramConfiguration.min_idle; // รับการเชื่อมต่อเวลารอเวลาส่วนตัวยาว maxwait = paramConfiguration.max_wait; Public ConnectionParambuilder (ไดรเวอร์สตริง, url สตริง, ผู้ใช้สตริง, รหัสผ่านสตริง) {this.driver = ไดรเวอร์; this.url = url; this.user = ผู้ใช้; this.password = รหัสผ่าน; } Public ConnectionParambuilder minconnection (int minconnection) {this.minconnection = minconnection; คืนสิ่งนี้; } Public ConnectionParambuilder MaxConnection (int maxConnection) {this.maxConnection = maxConnection; คืนสิ่งนี้; } Public ConnectionParambuilder minidle (int minidle) {this.minidle = minidle; คืนสิ่งนี้; } Public ConnectionParambuilder MaxWait (int maxwait) {this.maxwait = maxWait; คืนสิ่งนี้; } @Override Public ConnectionParam build () {ส่งคืนการเชื่อมต่อใหม่ (นี้); -.ConnectionPool
ฐานข้อมูลแพ็คเกจ. factory; นำเข้าฐานข้อมูล. connectionparam; นำเข้า Javax.sql.datasource; นำเข้า java.io.printwriter; นำเข้า java.lang.reflect.invocationhandler; นำเข้า java.lang.reflect.method; java.sql.driverManager; นำเข้า java.sql.sqlexception; นำเข้า java.sql.sqlfeaturenotsupportedexception; นำเข้า java.util.vector; นำเข้า java.util.concurrent.arrayblockingqueue; * การเชื่อมต่อพูล * สร้างโดย Michael Wong เมื่อวันที่ 2016/1/18 */คลาสสาธารณะการเชื่อมต่อพูลใช้ DataSource {ส่วนตัวคงที่ int intial_size = 5; สตริงสุดท้ายคงที่ส่วนตัว close_method = "ปิด"; เครื่องบันทึกเครื่องบันทึกแบบสแตติกส่วนตัว; ขนาด int ส่วนตัว; ConnectionParam Private ConnectionParam; arrayblockingqueue ส่วนตัว <การเชื่อมต่อ> IdleconnectionQueue; เวกเตอร์ส่วนตัว <การเชื่อมต่อ> BusyconnectionVector; Protected ConnectionPool (ConnectionParam ConnectionParam) {this.connectionParam = ConnectionParam; int maxConnection = ConnectionParam.getMaxConnection (); IdleConnectionQueue = new ArrayBlockingQueue <> (MaxConnection); BusyconnectionVector = เวกเตอร์ใหม่ <> (); logger = logger.getLogger (this.getClass (). getName ()); initConnection (); } private void initConnection () {int minconnection = connectionParam.getMinconnection (); int fiarsize = initial_size <minconnection? MinConnection: initial_size; ลอง {class.forName (ConnectionParam.getDriver ()); สำหรับ (int i = 0; i <ค่าเริ่มต้น+connectionParam.getMinconnection (); i ++) {idleConnectionQueue.put (newConnection ()); ขนาด ++; }} catch (Exception e) {โยนข้อยกเว้นใหม่ IniNitializerIrorror (E); }} @Override การเชื่อมต่อสาธารณะ getConnection () พ่น sqlexception {ลอง {การเชื่อมต่อการเชื่อมต่อสุดท้าย = idleConnectionQueue.poll (ConnectionParam.getMaxWait (), TimeUnit.milliseconds); if (connection == null) {logger.info (emgtymsg ()); ensurecapacity (); คืนค่า null; } busyconnectionVector.add (การเชื่อมต่อ); return (การเชื่อมต่อ) proxy.newproxyinstance (this.getClass (). getClassLoader (), คลาสใหม่ [] {connection.class}, ใหม่ rechocationHandler () {@Override วัตถุสาธารณะเรียกใช้ args); } catch (interruptedException e) {e.printStackTrace (); } return null; } การเชื่อมต่อส่วนตัว newConnection () พ่น sqlexception {string url = connectionparam.geturl (); String user = ConnectionParam.getUser (); รหัสผ่านสตริง = ConnectionParam.getPassword (); Return DriverManager.getConnection (URL, ผู้ใช้, รหัสผ่าน); } ขนาด int ที่ได้รับการป้องกัน () {ขนาดคืน; } ป้องกัน int iDleConnectionQuantity () {return idleConnectionQueue.size (); } ป้องกัน int busyconnectionQuantity () {return busyconnectionVector.size (); } โมฆะส่วนตัว ensureCapacity () พ่น sqlexception {int minidle = connectionParam.getMinidle (); int maxConnection = ConnectionParam.getMaxConnection (); int newCapacity = size + minidle; newCapacity = newCapacity> maxConnection? MaxConnection: newcapacity; int growcount = 0; if (size <newCapacity) {ลอง {สำหรับ (int i = 0; i <newCapacity - size; i ++) {idleConnectionQueue.put (newConnection ()); Growcount ++; }} catch (interruptedException e) {e.printStackTrace (); }} size = size + growCount; } Void Protected Clear () {ลอง {ในขณะที่ (ขนาด-> 0) {การเชื่อมต่อการเชื่อมต่อ = idleConnectionQueue.take (); Connection.close (); }} catch (interruptedException | sqlexception e) {e.printstacktrace (); }} สตริงส่วนตัว emptymsg () {return "ฐานข้อมูลไม่ว่างโปรดรอ ... "; } @Override การเชื่อมต่อสาธารณะ getConnection (ชื่อผู้ใช้สตริง, รหัสผ่านสตริง) พ่น sqlexception {return null; } @Override Public Printwriter getLogwriter () พ่น sqlexception {return null; } @Override โมฆะสาธารณะ setLogWriter (printwriter out) พ่น sqlexception {} @Override โมฆะสาธารณะ setlogintimeout (int วินาที) พ่น sqlexception {} @Override สาธารณะ int getLogintimeout () โยน sqlexception } @Override logger สาธารณะ getParentLogger () พ่น sqlfeatuRenotsupportedException {return null; } @Override สาธารณะ <t> t Unwrap (คลาส <t> iface) พ่น sqlexception {return null; } @Override บูลีนสาธารณะ iswrapperfor (คลาส <?> iface) พ่น sqlexception {return false; -.ConnectionPoolFactory
ฐานข้อมูลแพ็คเกจ. factory; นำเข้าฐานข้อมูล. connectionparam; นำเข้า java.sql.connection; นำเข้า java.sql.sqlexception; นำเข้า java.util.map; นำเข้า java.util.concurrent.currenthashmap;/** * */คลาสสาธารณะ ConnectionPoolFactory {Private ConnectionPoolFactory () {} แผนที่คงที่ส่วนตัว <String, ConnectionPool> PoolMap = ใหม่พร้อมกันพร้อมกัน <> (); การเชื่อมต่อแบบคงที่สาธารณะ getConnection (String poolname) พ่น sqlexception {nameCheck (ชื่อ poolname); ConnectionPool ConnectionPool = PoolMap.get (ชื่อ poolName); ส่งคืน ConnectionPool.getConnection (); } โมฆะคงที่สาธารณะ registerConnectionPool (ชื่อสตริง, ConnectionParam ConnectionParam) {registerCheck (ชื่อ); poolmap.put (ชื่อ, ConnectionPool ใหม่ (ConnectionParam)); } // ให้ GC เป็นโมฆะสาธารณะคงที่ unregisterConnectionPool (ชื่อสตริง) {nameCheck (ชื่อ); Final ConnectionPool ConnectionPool = PoolMap.get (ชื่อ); poolmap.remove (ชื่อ); เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะ run () {connectionPool.clear ();}}) เริ่มต้น (); } ขนาด int คงที่สาธารณะ (String poolName) {NameCheck (ชื่อ poolName); ส่งคืน poolmap.get (ชื่อ poolname) .size (); } สาธารณะคงที่ int getIdleConnectionQuantity (String poolName) {nameCheck (ชื่อ poolName); return poolmap.get (ชื่อ poolname) .idleconnectionquantity (); } สาธารณะคงที่ int getBusyconnectionQuantity (String poolname) {nameCheck (ชื่อ poolName); ส่งคืน poolmap.get (ชื่อ poolname) .BusyconnectionQuantity (); } โมฆะคงที่ส่วนตัว registerCheck (ชื่อสตริง) {if (name == null) {โยน unlegalArgumentException ใหม่ (nullName ()); }} โมฆะคงที่ส่วนตัว nameCheck (ชื่อสตริง) {ถ้า (ชื่อ == null) {โยน unlegalargumentException ใหม่ (nullName ()); } if (! poolmap.containskey (ชื่อ)) {โยน unlegalargumentException ใหม่ (notexists (ชื่อ)); }} สตริงคงที่ส่วนตัว nullName () {return "ชื่อพูลต้องไม่เป็นโมฆะ"; } private string notexists (ชื่อสตริง) {return "การเชื่อมต่อพูลชื่อ" + ชื่อ + "ไม่มีอยู่"; - 4. ทดสอบ
การทดสอบหน่วย Junit
ฐานข้อมูลแพ็คเกจ. factory; นำเข้าฐานข้อมูล. connectionparam; นำเข้า org.junit.test; นำเข้า java.sql.connection; นำเข้า java.sql.sqlexception; นำเข้า java.util.arraylist; นำเข้า java.util.list; 2016/1/20 */คลาสสาธารณะ ConnectPoolFactoryTest {@Test โมฆะสาธารณะ Public Void TestGetConnection () พ่น sqlexception {String driver = "com.mysql.jdbc.driver"; string url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; สตริงรหัสผ่าน = "รูท"; ConnectionParam ConnectionParam = ใหม่ ConnectionParam.ConnectionParambuilder (ไดรเวอร์, URL, ผู้ใช้, รหัสผ่าน) .build (); ConnectPoolFactory.registerConnectionPool ("ทดสอบ", ConnectionParam); รายการ <connection> ConnectionList = arrayList ใหม่ <> (); สำหรับ (int i = 0; i <12; i ++) {connectionList.add (connectionPoolFactory.getConnection ("ทดสอบ")); } print (); ปิด (ConnectionList); พิมพ์(); ConnectionList.Clear (); สำหรับ (int i = 0; i <12; i ++) {connectionList.add (connectionPoolFactory.getConnection ("ทดสอบ")); } print (); ปิด (ConnectionList); ConnectPoolFactory.unregisterConnectionPool ("ทดสอบ"); } @Test (คาดว่า = unlueLArgumentException.class) โมฆะสาธารณะ testException () {ลอง {connectionPoolFactory.getConnection ("ทดสอบ"); } catch (sqlexception e) {e.printstacktrace (); }} void ส่วนตัวปิด (รายการ <connection> connectionList) พ่น sqlexception {สำหรับ (Connection Conn: ConnectionList) {if (conn! = null) {conn.close (); }}} private void print () {system.out.println ("idle:" + connectionpoolfactory.getIdleConnectionQuantity ("ทดสอบ")); System.out.println ("ไม่ว่าง:" + ConnectionPoolfactory.getBusyconnectionQuantity ("ทดสอบ")); System.out.println ("ขนาด:" + connectionpoolfactory.size ("ทดสอบ")); -ข้างต้นเป็นเรื่องเกี่ยวกับบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์กับการเรียนรู้ของทุกคน