Memcache คืออะไร?
โซลูชันแคชในสภาพแวดล้อมคลัสเตอร์ Memcache
MemCache เป็นระบบแคชวัตถุหน่วยความจำแบบกระจายประสิทธิภาพสูง ด้วยการบำรุงรักษาตารางแฮชแบบรวมและขนาดใหญ่ในหน่วยความจำสามารถใช้ในการจัดเก็บข้อมูลในรูปแบบต่าง ๆ รวมถึงรูปภาพวิดีโอไฟล์และผลลัพธ์การดึงฐานข้อมูล พูดง่ายๆก็คือการเรียกข้อมูลลงในหน่วยความจำแล้วอ่านจากหน่วยความจำซึ่งจะเป็นการปรับปรุงความเร็วในการอ่านอย่างมาก
Memcache เป็นโครงการใน Danga มันถูกเสิร์ฟครั้งแรกโดย LiveJournal เดิมทีมันได้รับการพัฒนาเพื่อเร่งความเร็วการเข้าถึงของ LiveJournal มันถูกนำมาใช้ในภายหลังโดยเว็บไซต์ขนาดใหญ่หลายแห่ง
Memcached ทำงานบนเซิร์ฟเวอร์หนึ่งเซิร์ฟเวอร์หรือมากกว่าในโหมด daemon และได้รับการเชื่อมต่อไคลเอนต์และการดำเนินงานได้ตลอดเวลา
เหตุใดจึงมีชื่อ Memcache และ Memcached สองชื่อ?
ในความเป็นจริง Memcache เป็นชื่อของโครงการนี้และ Memcached เป็นชื่อไฟล์โปรแกรมหลักในฝั่งเซิร์ฟเวอร์ คุณรู้ว่าฉันหมายถึงอะไร หนึ่งคือชื่อโครงการและอีกชื่อหนึ่งคือชื่อไฟล์โปรแกรมหลัก ฉันเห็นมันออนไลน์ที่หลายคนไม่เข้าใจดังนั้นฉันจึงผสมมัน
Memcached เป็นระบบแคชวัตถุหน่วยความจำแบบกระจายที่ใช้เพื่อลดการโหลดฐานข้อมูลและปรับปรุงความเร็วในการเข้าถึงในแอปพลิเคชันแบบไดนามิก Memcached ได้รับการพัฒนาโดย Danga Interactive เพื่อปรับปรุงการเข้าถึง LiveJournal.com LJ มีการเยี่ยมชมหน้าแบบไดนามิกหลายพันครั้งต่อวินาทีและมีผู้ใช้ 7 ล้านคน MEMCACHED ช่วยลดการโหลดฐานข้อมูลได้ดีขึ้นจัดสรรทรัพยากรที่ดีขึ้นและเข้าถึงได้เร็วขึ้น
บทความนี้จะครอบคลุมสิ่งต่อไปนี้:
memcache
MemCache เป็นร้านค้าคีย์-ค่าในหน่วยความจำสำหรับชิ้นเล็ก ๆ ของข้อมูลโดยพลการ (สตริงวัตถุ) จากผลลัพธ์ของฐานข้อมูลการเรียก API หรือการเรนเดอร์หน้า
นั่นคือฐานข้อมูลแคชในหน่วยความจำซึ่งเป็นฐานข้อมูลคู่คีย์-ค่า ฐานข้อมูลมีอยู่เพื่อจัดเก็บข้อมูลที่ได้จากบริการอื่น ๆ ชั่วคราวในหน่วยความจำและสามารถส่งคืนได้โดยตรงจากแคช HIT เมื่อเข้าถึงซ้ำ สิ่งนี้ไม่เพียง แต่เพิ่มความเร็วในการเข้าถึง แต่ยังช่วยลดการโหลดในบริการอื่น ๆ ที่นี่ MemCache เวอร์ชันเซิร์ฟเวอร์เดียวจะถูกนำไปใช้และรองรับการเชื่อมต่อพร้อมกันระหว่างไคลเอนต์หลายตัว
ไคลเอนต์จะสร้างการเชื่อมต่อ telnet กับเซิร์ฟเวอร์จากนั้นโต้ตอบกับแคชเซิร์ฟเวอร์ตามโปรโตคอล MemCache คำแนะนำที่นำมาใช้ที่นี่คือ Get, Set และ Del มาดูรูปแบบของแต่ละคำสั่ง
ชุด
ชุดเป็นคำแนะนำการจัดเก็บ เมื่อจัดเก็บคุณสมบัติของคำสั่งอินพุตข้อมูลพื้นฐานในบรรทัดแรกและป้อนค่าที่สอดคล้องกันในบรรทัดที่สอง
ตั้งค่า <key> <lags> <Exptime> <bytes> [noreply]/r/n
<value>/r/n
หากที่เก็บข้อมูลสำเร็จการจัดเก็บจะถูกส่งคืนและหากคำสั่งมีแอตทริบิวต์ Noreply เซิร์ฟเวอร์จะไม่ส่งคืนข้อมูล
เนื้อหาของแต่ละโดเมนในคำสั่งนี้มีดังนี้:
หากคำสั่งไม่เป็นไปตามเกณฑ์เซิร์ฟเวอร์จะส่งคืนข้อผิดพลาด
รับ
Get เป็นคำสั่งรับและลักษณะของคำสั่งนี้มีดังนี้:
รับ <key>*/r/n
รองรับค่าผ่านของหลายปุ่ม หากแคชกระทบอย่างน้อยหนึ่งปุ่มข้อมูลที่เกี่ยวข้องจะถูกส่งคืนและสิ้นสุดด้วยการสิ้นสุด หากไม่มีการตีข้อความที่ส่งคืนจะไม่มีค่าที่สอดคล้องกับคีย์ รูปแบบมีดังนี้:
value <key> <lags> <bytes>/r/n <ข้อมูลบล็อก>/r/nvalue <key> <glags> <bytes>/r/n <data block>/r/nenddel
ลบคำสั่งรูปแบบของคำสั่งมีดังนี้:
del <key> [noreply]/r/n
หากการลบสำเร็จจะส่งคืนการลบ/r/n มิฉะนั้นจะส่งคืน not_found หากมีพารามิเตอร์ที่ไม่มีการทำเซิร์ฟเวอร์จะไม่ส่งคืนการตอบกลับ
ซ็อกเก็ตชวา
ซ็อกเก็ต Java ทั้งหมดต้องรู้คือโปรโตคอล TCP, ซ็อกเก็ตและสตรีม IO ฉันจะไม่เข้าไปดูรายละเอียดที่นี่ คุณสามารถอ้างถึงชุดบทความของฉัน ขอแนะนำให้อ่านการเขียนโปรแกรมเครือข่าย Java หนังสือ.
การใช้รหัส
มีบางอย่างผิดปกติกับฟังก์ชั่นแผนที่ที่นี่ คุณสามารถไปที่ที่อยู่โครงการของฉันในตอนท้ายของบทความเพื่อดูแผนภาพชั้นเรียน
ที่นี่โหมดการเรียนการสอนและโหมดโรงงานใช้เพื่อใช้การแยกการสร้างคำสั่งและการดำเนินการ โรงงานคำสั่งจะได้รับคำสั่งและส่งคืนอินสแตนซ์คำสั่ง แต่ละคำสั่งมีวิธีการดำเนินการเพื่อดำเนินการที่ไม่ซ้ำกัน เฉพาะการใช้งานพิเศษของคำสั่ง DEL เท่านั้นที่โพสต์ที่นี่
/** * คำสั่งต่าง ๆ * ปัจจุบันรองรับ Get, Set, DELETE * * และ Custom * ข้อผิดพลาด, End */Public Interface Command {/** * ดำเนินการคำสั่ง * @Param Reader * @Param Writer */Void Execute (Reader Reader, นักเขียนนักเขียน); / *** รับประเภทของคำสั่ง* @return*/ CommandType getType ();} /*** อินสแตนซ์เดียวของ Directive Factory*/Public Class CommandFactory {Private Static CommandFactory CommandFactory; แคชแบบคงที่ส่วนตัว <itect> memcache; Private CommandFactory () {} public Static CommandFactory getInstance (แคช <ition> แคช) {ถ้า (commandFactory == null) {commandFactory = ใหม่ commandFactory (); memcache = แคช; } return CommandFactory; } / ** * รับคำสั่งตามประเภทของคำสั่ง * @param commandline * @return * / คำสั่งสาธารณะ getCommand (สตริง Commandline) {ถ้า (commandline.matches ("^set. * $")) {ส่งคืน setCommand ใหม่ (Commandline, memcache); } อื่นถ้า (commandline.matches ("^get.*$")) {ส่งคืนใหม่ getCommand (Commandline, memcache); } อื่นถ้า (commandline.matches ("^del.*$")) {ส่งคืน deleteCommand ใหม่ (Commandline, memcache); } อื่นถ้า (commandline.matches ("^end $")) {ส่งคืน endcommand ใหม่ (commandline); } else {ส่งคืนข้อผิดพลาดใหม่ (CommandLine, ErrorCommand.erRortype.error); - /*** ลบคำสั่งแคช*/คลาสสาธารณะ DELETECOMMAND ใช้คำสั่ง {คำสั่งสตริงสุดท้ายส่วนตัว; แคชสุดท้ายสุดท้าย <itect> แคช; คีย์สตริงส่วนตัว บูลีนส่วนตัว Public DeLetEcommand (คำสั่งสตริงสุดท้าย, แคชสุดท้าย <ition> แคช) {this.Command = คำสั่ง; this.cache = แคช; initcommand (); } โมฆะส่วนตัว initCommand () {ถ้า (this.Command.contains ("noreply")) {noreply = true; } string [] info = command.split (""); คีย์ = ข้อมูล [1]; } @Override โมฆะสาธารณะ Execute (Reader Reader, Writer Writer) {BufferedWriter BFW = (BufferedWriter) นักเขียน; รายการ item = cache.delete (คีย์); if (! noreply) {ลอง {ถ้า (item == null) {bfw.write ("not_found/r/n"); } else {bfw.write ("ลบ/r/n"); } bfw.flush (); } catch (ioexception e) {ลอง {bfw.write ("ข้อผิดพลาด/r/n"); bfw.flush (); } catch (ioexception e1) {e1.printstacktrace (); } E.PrintStackTrace (); }}} @Override Public CommandType getType () {return CommandType.search; -จากนั้นใช้เซิร์ฟเวอร์หน่วยความจำ เพื่อรองรับฟังก์ชั่นแรกในการออกครั้งแรก LinkedTreeMap จะใช้เป็นการนำไปใช้งานพื้นฐานและวิธีการ removeoldest จะถูกเขียนใหม่ ในเวลาเดียวกันเธรดพื้นหลัง CacheManager ยังใช้เพื่อล้างรายการแคชที่หมดอายุในเวลา
MemCache คลาสสาธารณะใช้ CACHE <ition> {Private Logger Logger = logger.getLogger (memcache.class.getName ()); // ใช้ LinkedHashMap เพื่อใช้งาน LRU ส่วนตัวคงที่ LinkedHashMap <สตริงรายการ> แคช; Maxsize สุดท้ายส่วนตัว; // load factor ส่วนตัว Final Float default_load_factor = 0.75f; memcache สาธารณะ (int maxsize สุดท้าย) {this.maxsize = maxSize; // ตรวจสอบให้แน่ใจว่าแคชจะไม่ขยายโดยอัตโนมัติหลังจากที่ MaxSize ถึงความจุ int = (int) math.ceil (maxsize /default_load_factor) + 1; this.cache = ใหม่ linkedHashMap <string, item> (ความจุ, default_load_factor, true) {@Override boolean protected removeldestentry (map.entry <string, its> eldest) {ถ้า (ขนาด ()> maxsize) {logger.info ( } return size ()> maxsize; - // ใช้คอลเลกชันการเข้าถึงแบบซิงโครไนซ์ SynchronizedMap (Cache); } บูลีนที่ซิงโครไนซ์สาธารณะ isfull () {return cache.size ()> = maxsize; } @Override รายการสาธารณะรับ (คีย์สตริง) {item item = cache.get (คีย์); if (item == null) {logger.info ("คีย์ในแคช:" + คีย์ + "ไม่มีอยู่"); คืนค่า null; } else if (item! = null && item.isexpired ()) {// ถ้าแคชหมดอายุให้ลบและส่งคืน null logger.info ("อ่านคีย์จากแคช:" + คีย์ + "value:" + item.getValue () + "หมดอายุ"); CACHE.REMOVE (กุญแจ); คืนค่า null; } logger.info ("อ่านคีย์จากแคช:" + คีย์ + "ค่า:" + item.getValue () + "เวลาที่เหลืออยู่ที่เหลืออยู่" + item.remaintime ()); รายการส่งคืน; } @Override โมฆะสาธารณะตั้งค่า (คีย์สตริง, ค่ารายการ) {logger.info ("คีย์เขียนไปยังแคช:" + คีย์ + "ค่า:" + ค่า); cache.put (คีย์, ค่า); } @Override รายการสาธารณะลบ (คีย์สตริง) {logger.info ("ลบคีย์จากแคช:" + คีย์); ส่งคืนแคช REMOVE (กุญแจ); } @Override ขนาด int สาธารณะ () {return cache.size (); } @Override ความจุ int สาธารณะ () {return maxsize; } @Override public iterator <map.entry <String, item >> iterator () {return cache.entryset (). iterator (); - /*** ตัวจัดการแคช* เธรดพื้นหลัง* ลบแคชที่หมดอายุในแคช*/คลาสสาธารณะ cachemanager ใช้งาน runnable {private logger logger = logger.getLogger (cacheManager.class.getName ()); // แคชแคชสาธารณะ <ite> แคช; Public CacheManager (Cache <Item> Cache) {this.cache = cache; } @Override โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (จริง) {iterator <map.entry <string, item >> itemiterator = cache.iterator (); ในขณะที่ (itemiterator.hasnext ()) {map.entry <string, item> entry = itemiterator.next (); รายการ item = entry.getValue (); if (item.isexpired ()) {logger.info ("คีย์:" + entry.getKey () + "value" + item.getValue () + "หมดอายุแล้วลบออกจากฐานข้อมูล"); itemiterator.remove (); }} ลอง {// รันโปรแกรมพื้นหลังทุก 5 วินาที TimeUnit.seconds.sleep (5); } catch (interruptedException e) {e.printStackTrace (); -ในที่สุดใช้เซิร์ฟเวอร์ซ็อกเก็ตแบบมัลติเธรดซึ่ง Serversocket ถูกผูกไว้กับอินเทอร์เฟซและส่งมอบซ็อกเก็ตที่ยอมรับไว้กับเธรดเพิ่มเติมสำหรับการประมวลผล
/*** เซิร์ฟเวอร์*/คลาสสาธารณะ iOSERVER ใช้เซิร์ฟเวอร์ {Private Boolean Stop; // หมายเลขพอร์ตพอร์ต INT สุดท้ายส่วนตัว; // เซิร์ฟเวอร์เธรดเซิร์ฟเวอร์เซิร์ฟเวอร์ส่วนตัวเซิร์ฟเวอร์; Logger สุดท้าย logger สุดท้าย = logger.getLogger (ioserver.class.getName ()); // พูลเธรดความจุเธรดคือ MaxConnection Private ExecutorService ExecutorService; แคชสุดท้ายสุดท้าย <itect> แคช; iOServer สาธารณะ (พอร์ต int, int maxConnection, แคช <ite> แคช) {ถ้า (maxConnection <= 0) โยน unlegalargumentException ใหม่ ("จำนวนสูงสุดของการเชื่อมต่อที่รองรับจะต้องเป็นจำนวนเต็มบวก"); this.port = พอร์ต; ExecutorService = Executors.NewFixedThreadPool (MaxConnection); this.cache = แคช; } @Override โมฆะสาธารณะเริ่มต้น () {ลอง {serversocket = ใหม่ serversocket (พอร์ต); logger.info ("เซิร์ฟเวอร์เริ่มต้นที่พอร์ต"+พอร์ต+"); ในขณะที่ (จริง) {ลอง {ซ็อกเก็ตซ็อกเก็ต = serversocket.accept (); logger.info (" ได้รับการเชื่อมต่อของ "+socket.getLocaladdress ()+" "); E.PrintStackTrace (); ! Serversocket.isclosed ();} / *** หยุดเซิร์ฟเวอร์* / public void shutdown () {ลอง {if (serversocket! = null) {serversocket.close (); /*** จัดการการเชื่อมต่อของไคลเอนต์แต่ละตัว* ปิดการเชื่อมต่อหลังจากได้รับคำสั่งสิ้นสุด S*/คลาสสาธารณะ Sockethandler ใช้งาน {Private Static Logger Logger = logger.getLogger (Sockethandler.class.getName ()); ซ็อกเก็ตซ็อกเก็ตสุดท้ายส่วนตัว แคชสุดท้ายสุดท้าย <itect> แคช; บูลีนส่วนตัวเสร็จสิ้น; Sockethandler สาธารณะ (ซ็อกเก็ต S, แคช <Item> แคช) {this.socket = s; this.cache = แคช; } @Override โมฆะสาธารณะเรียกใช้ () {ลอง {// รับซ็อกเก็ตอินพุตสตรีมสุดท้าย bufferedReader reader = new BufferedReader (ใหม่ inputStreamReader (socket.getInputStream ())); // get socket output stream final bufferedWriter Writer = ใหม่ bufferedWriter (ใหม่ outputStreamWriter (socket.get.getOutputStream ())); CommandFactory CommandFactory = CommandFactory.getInstance (แคช); ในขณะที่ (เสร็จสิ้น) {สตริงสุดท้าย commandline = reader.readline (); logger.info ("IP:" + socket.getLocalAddress () + "คำสั่ง:" + commandline); if (commandline == null || commandline.trim (). isempty ()) {ดำเนินการต่อ; } // ใช้โรงงานคำสั่งเพื่อรับอินสแตนซ์คำสั่งคำสั่งสุดท้ายคำสั่ง = commandFactory.getCommand (commandline); command.execute (ผู้อ่านนักเขียน); if (command.getType () == CommandType.end) {logger.info ("คำขอปิดการเชื่อมต่อ"); เสร็จสิ้น = จริง; }}} catch (ioexception e) {e.printstacktrace (); logger.info ("ปิดการเชื่อมต่อจาก" + socket.getLocaladdress () + ""); } ในที่สุด {ลอง {ถ้า (ซ็อกเก็ต! = null) {socket.close (); }} catch (ioexception e) {e.printstacktrace (); -กรุณาคลิกที่นี่สำหรับที่อยู่โครงการ ถ้าคุณคิดว่ามันค่อนข้างดีฉันหวังว่าคุณจะให้ดาราให้ฉันได้
การอ้างอิง
เว็บไซต์ทางการ Memcached
Memcache Protocol
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น