ما هو Memcache؟
حل ذاكرة التخزين المؤقت في بيئة مجموعة Memcache
Memcache هو نظام ذاكرة التخزين المؤقت لكائن الذاكرة الموزعة عالي الأداء. من خلال الحفاظ على جدول تجزئة موحد وضخم في الذاكرة ، يمكن استخدامه لتخزين البيانات بتنسيقات مختلفة ، بما في ذلك الصور ومقاطع الفيديو والملفات ونتائج استرجاع قاعدة البيانات. ببساطة ، هو استدعاء البيانات في الذاكرة ثم قراءتها من الذاكرة ، وبالتالي تحسين سرعة القراءة بشكل كبير.
Memcache هو مشروع في Danga. تم تقديمه لأول مرة من قبل LiveJournal. تم تطويره في الأصل لتسريع سرعة وصول LiveJournal. تم تبنيه لاحقًا من قبل العديد من المواقع الكبيرة.
يعمل Memcached على خادم واحد أو أكثر في وضع الخفي ، ويتلقى اتصالات وعمليات العميل في أي وقت.
لماذا يوجد اسمين memcache و memcached؟
في الواقع ، Memcache هو اسم هذا المشروع ، و Memcached هو اسم ملف البرنامج الرئيسي على جانب الخادم الخاص به. أنت تعرف ما أعنيه. أحدهما هو اسم المشروع والآخر هو اسم ملف البرنامج الرئيسي. لقد رأيته عبر الإنترنت لا يفهمه الكثير من الناس ، لذلك قمت بخلطه.
Memcached هو نظام تخزين للتخزين المؤقت لذاكرة الذاكرة العالي الأداء والموزع المستخدم لتقليل تحميل قاعدة البيانات وتحسين سرعة الوصول في التطبيقات الديناميكية. تم تطوير Memcached بواسطة Danga Interactive لتحسين الوصول إلى livejournal.com. لدى LJ الآلاف من زيارات الصفحة الديناميكية في الثانية ولديها 7 ملايين مستخدم. يقلل memcached بشكل كبير من تحميل قاعدة البيانات ، ويخصص الموارد بشكل أفضل والوصول بشكل أسرع.
ستغطي هذه المقالة ما يلي:
memcache
Memcache هو متجر قيمة رئيسي في الذاكرة لقطع صغيرة من البيانات التعسفية (السلاسل أو الكائنات) من نتائج DatabasEcalls أو مكالمات API أو عرض الصفحة.
وهذا يعني أن قاعدة بيانات ذاكرة التخزين المؤقت في الذاكرة ، وهي قاعدة بيانات زوجة مفتاح. توجد قاعدة البيانات لتخزين البيانات التي تم الحصول عليها من الخدمات الأخرى في الذاكرة مؤقتًا بشكل مؤقت ويمكن إرجاعها مباشرة من ذاكرة التخزين المؤقت لـ HIT عند الوصول المتكرر. هذا لا يزيد من معدلات الوصول فحسب ، بل يقلل أيضًا من الحمل على الخدمات الأخرى. هنا ، سيتم تنفيذ إصدار خادم واحد من MemCache ويدعم الاتصالات المتزامنة بين العديد من العملاء.
سيقوم العميل بإنشاء اتصال Telnet مع الخادم ثم يتفاعل مع ذاكرة التخزين المؤقت للخادم وفقًا لبروتوكول MEMCACHE. التعليمات التي تم تنفيذها هنا هي GET ، SET و DEL. دعونا نلقي نظرة على تنسيق كل تعليمات
تعيين
SET هي تعليمات التخزين. عند تخزين خصائص التعليمات ، قم بإدخال المعلومات الأساسية في السطر الأول وإدخال قيمته المقابلة في السطر الثاني.
SET <KEY> <LALLS> <EXPTIME> <BYTES> [noreply]/r/n
<value>/r/n
إذا نجحت التخزين ، فسيتم إعادة تخزينها ، وإذا كان التوجيه يحتوي على السمة غير المألوفة ، فلن يقوم الخادم بإرجاع المعلومات.
محتويات كل مجال في هذا التوجيه هي كما يلي:
إذا لم تفي التعليمات بالمعايير ، فسيقوم الخادم بإرجاع الخطأ.
يحصل
الحصول على أمر GET ، وخصائص هذا الأمر هي كما يلي:
احصل على <key>*/r/n
وهو يدعم تمرير قيم مفاتيح متعددة. إذا ضربت ذاكرة التخزين المؤقت مفتاحًا أو أكثر ، فسيتم إرجاع البيانات المقابلة وينتهي بنهاية. إذا لم يتم إجراء ضرب ، فإن الرسالة التي تم إرجاعها لا تحتوي على القيمة المقابلة للمفتاح. التنسيق كما يلي:
القيمة <Kear> <lass> <Bytes>/r/n <block data>/r/nvalue <Kear> <lass> <Bytes>/r/n <cloint>/r/nenddel
حذف الأمر ، تنسيق الأمر كما يلي:
del <key> [noreply]/r/n
إذا كان الحذف ناجحًا ، فسيعود حذفه/r/n ، وإلا فإنه سيعود not_found. إذا كانت هناك معلمة غير مخصصة ، فلن يقوم الخادم بإرجاع استجابة.
مقبس جافا
كل مقبس Java يحتاج إلى معرفته هو بروتوكول TCP والمآخذ وتدفقات IO. لن أخوض في التفاصيل هنا. يمكنك الرجوع إلى سلسلة المقالات الخاصة بي. يوصى أيضًا بقراءة برمجة شبكة Java. كتاب.
تنفيذ الكود
هناك شيء خاطئ في وظيفة الخريطة هنا. يمكنك الانتقال إلى عنوان مشروعي في نهاية المقالة لعرض مخطط الفصل.
هنا ، يتم استخدام وضع التعليمات ووضع المصنع لتنفيذ فصل تعليمات إنشاء وتنفيذ التعليمات. سيتلقى مصنع الأوامر سطر الأوامر وإرجاع مثيل أمر. كل أمر لديه طريقة تنفيذ لأداء عملياته الفريدة الخاصة. يتم نشر فقط التنفيذ الخاص لتعليم DEL هنا.
/** * توجيهات مختلفة * تدعم حاليًا الحصول على GET ، SET ، DELETE * * و CUSTOM * ERROR ، END */Public Interface Command {/** * execute command * param reader * param writer */void execute (reader reader ، writer) ؛ / *** احصل على نوع الأوامر* @RETURN*/ commandtype gettype () ؛} /*** مثيل واحد لمصنع التوجيه*/commandfactory الفئة العامة {private static commandfactory commandFactory ؛ ذاكرة التخزين المؤقت الثابتة الخاصة <Item> memcache ؛ commandfactory private () {} static static commandfactory getInstance (ذاكرة التخزين المؤقت <item> cache) {if (commandFactory == null) {commandFactory = new commandFactory () ؛ memcache = ذاكرة التخزين المؤقت ؛ } return commandFactory ؛ } / ** * احصل على أمر وفقًا لنوع خط الأوامر التوجيه * param * @return * / command public getCommand (string commandline) {if (commandline.matches ("^set. * $")) } آخر إذا (commandline.matches ("^get.*$")) {return new getCommand (commandline ، memcache) ؛ } if if (commandline.matches ("^del.*$")) {return new deletecommand (commandline ، memcache) ؛ } else if (commandline.matches ("^end $")) {return new endCommand (commandline) ؛ } else {return new errorCommand (commandline ، errorCommand.ErrorType.error) ؛ }}} /*** حذف توجيهات ذاكرة التخزين المؤقت*/فئة عامة DELETECMOMMAND Command {Private Final String Command ؛ ذاكرة التخزين المؤقت النهائية الخاصة <item> ذاكرة التخزين المؤقت ؛ مفتاح السلسلة الخاص ؛ منطقية خاصة Public Deletecommand (أمر السلسلة النهائية ، ذاكرة التخزين المؤقت النهائية <Item> ذاكرة التخزين المؤقت) {this.command = command ؛ this.cache = ذاكرة التخزين المؤقت ؛ initCommand () ؛ } private void initCommand () {if (this.command.contains ("noreply")) {noreply = true ؛ } string [] info = command.split ("") ؛ المفتاح = info [1] ؛ } Override public void execute (reader reader ، writer) {bufferedWriter bfw = (bufferedWriter) constr ؛ عنصر العنصر = cache.delete (مفتاح) ؛ if (! noreply) {try {if (item == null) {bfw.write ("not_found/r/n") ؛ } آخر {bfw.write ("deleted/r/n") ؛ } bfw.flush () ؛ } catch (ioException e) {try {bfw.write ("error/r/n") ؛ bfw.flush () ؛ } catch (ioException e1) {e1.printStackTrace () ؛ } E.PrintStackTrace () ؛ }}} Override publictype gettype () {return commandtype.search ؛ }}ثم ، قم بتنفيذ خادم الذاكرة. من أجل دعم وظيفة الأولى في الأول ، يتم استخدام LinkedTreeMap كتطبيق أساسي ، ويتم إعادة كتابة طريقة removeOldest. في الوقت نفسه ، يتم استخدام مؤشر ترابط خلفية CacheManager أيضًا لمسح إدخالات ذاكرة التخزين المؤقت منتهية الصلاحية في الوقت المناسب.
الفئة العامة memcache تنفذ ذاكرة التخزين المؤقت <item> {private logger logger = logger.getLogger (memcache.class.getName ()) ؛ // استخدم LinkedHashMap لتنفيذ LRU ثابت ثابت LinkedHashMap <string ، item> ذاكرة التخزين المؤقت ؛ نهائي خاص int maxSize ؛ // عامل تحميل خاص Final Float default_load_factor = 0.75f ؛ memcache العامة (Final int maxSize) {this.maxSize = maxSize ؛ // تأكد من عدم توسيع ذاكرة التخزين المؤقت تلقائيًا بعد الوصول إلى MaxSize سعة int = (int) Math.ceil (maxSize /default_load_factor) + 1 ؛ this.cache = new LinkedHashMap <string ، item> (السعة ، default_load_factor ، true) {override boolean removeeldestentry (map.entry <string ، item> enst) } حجم الإرجاع ()> maxSize ؛ }} ؛ // تنفيذ مجموعة الوصول المتزامنة. } isfull isfull () {return cache.size ()> = maxSize ؛ } Override Public Item get (string key) {item item = cache.get (key) ؛ if (item == null) {logger.info ("key in cache:" + key + "غير موجود") ؛ العودة لاغية. } if if (item! = null && item.isexpired ()) {// إذا انتهت عملية ذاكرة التخزين المؤقت ، حذف وإرجاع logger.info ("قراءة مفتاح Cache:" + Key + "value:" + item.getValue () + "انتهت صلاحية") ؛ cache.remove (مفتاح) ؛ العودة لاغية. } logger.info ("read key from cache:" + key + "value:" + item.getValue () + "yeary time year" + item.Remaintime ()) ؛ عنصر الإرجاع ؛ } Override Public Void Set (مفتاح السلسلة ، قيمة العنصر) {logger.info ("مفتاح الكتابة إلى ذاكرة التخزين المؤقت:" + KEY + "القيمة:" + value) ؛ cache.put (المفتاح ، القيمة) ؛ } Override Public Item Delete (string Key) {logger.info ("delete key from cache:" + key) ؛ إرجاع cache.remove (مفتاح) ؛ } Override public int size () {return cache.size () ؛ } Override public int caplet () {return maxSize ؛ } Override Public Iterator <map.entry <string ، item >> iterator () {return cache.entryset (). iterator () ؛ }} /*** Manager Cache* Background Thread* حذف ذاكرة التخزين المؤقت منتهية الصلاحية في ذاكرة التخزين المؤقت*/الفئة العامة Cachemanager تنفذ Runnable {private logger logger = logger.getLogger (cachemanager.class.getName ()) ؛ // Cache Public Cache <Item> ذاكرة التخزين المؤقت ؛ cachemanager العامة (ذاكرة التخزين المؤقت <Etem> cache) {this.cache = cache ؛ } Override public void run () {بينما (true) {iterator <map.entry <string ، item >> itemiterator = cache.iterator () ؛ بينما (itemiterator.hasnext ()) {map.entry <string ، item> entry = itemIratorator.next () ؛ عنصر العنصر = entry.getValue () ؛ if (item.isexpired ()) {logger.info ("key:" + enter.getKey () + "value" + item.getValue () + "منتهية الصلاحية ، تم حذفها من قاعدة البيانات") ؛ itemiterator.remove () ؛ }} جرب {// قم بتشغيل برنامج الخلفية كل 5 ثوانٍ timeUnit.Seconds.sleep (5) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}}أخيرًا ، قم بتنفيذ خادم مقبس متعدد الخيوط ، حيث يرتبط ServersOcket بواجهة ويسلم المقبس المقبول إلى مؤشر ترابط إضافي للمعالجة.
/*** Server*/public class ioserver تنفذ الخادم {private boolean stop ؛ // رقم المنفذ المنفذ النهائي في المنفذ ؛ // Server Thread ServerSocket ServerSocket ؛ logger logger النهائي الخاص = logger.getLogger (ioserver.class.getName ()) ؛ // pool thread ، سعة الخيط هي maxconnection private final sevicestervice seviceStervice ؛ ذاكرة التخزين المؤقت النهائية الخاصة <item> ذاكرة التخزين المؤقت ؛ ioserver العامة (منفذ int ، int maxconnection ، ذاكرة التخزين المؤقت <Etem> ذاكرة التخزين المؤقت) {if (maxConnection <= 0) رمي غير alficalArgumentException جديد ("يجب أن يكون الحد الأقصى لعدد الاتصالات المدعومة عددًا صحيحًا إيجابيًا") ؛ this.port = port ؛ ExecutorService = Executors.NewFixedThreadPool (MaxConnection) ؛ this.cache = ذاكرة التخزين المؤقت ؛ } Override public void start () {try {serversocket = new ServersOcket (port) ؛ logger.info ("يبدأ الخادم على المنفذ"+port+") ؛ بينما (صحيح) {try {socket socket = serversounce.accept () ؛ logger.info (" تلقى اتصال "+socket.getlocaladress ()+") e.printstacktrace () ؛ ! servers. /*** تعامل مع اتصالات كل عميل* أغلق الاتصال بعد الحصول على تعليمات النهاية S*/Public Class Sockethandler تنفذ Runnable {private static logger = logger.getLogger (Sockethandler.class.getName ()) ؛ مقبس المقبس النهائي الخاص ؛ ذاكرة التخزين المؤقت النهائية الخاصة <item> ذاكرة التخزين المؤقت ؛ الانتهاء من منطقية خاصة ؛ Public Sockethandler (Socket S ، Cache <Item> cache) {this.socket = s ؛ this.cache = ذاكرة التخزين المؤقت ؛ } Override public void run () {try {// الحصول على مدخلات الإدخال المقبس النهائي bufferreader reader = جديد bufferedReader (new inputStreamReader (socket.getInputStream ())) ؛ // Get Socket Output Dream Constrater Constrate = New BufferedWriter (New OutputStreamWriter (Socket.getOutputStream ())) ؛ commandFactory commandFactory = commandFactory.getInstance (ذاكرة التخزين المؤقت) ؛ بينما (! الانتهاء) {Final String CommandLine = reader.ReadLine () ؛ logger.info ("IP:" + socket.getlocaladdress () + "التوجيه:" + خط الأوامر) ؛ if (commandline == null || commandline.trim (). isEmpty ()) {contern ؛ } // استخدم مصنع الأوامر للحصول على أمر Command Onter Command = commandFactory.getCommand (سطر الأوامر) ؛ command.execute (قارئ ، كاتب) ؛ if (command.gettype () == commandtype.end) {logger.info ("طلب لإغلاق الاتصال") ؛ الانتهاء = صحيح ؛ }}} catch (ioException e) {E.PrintStackTrace () ؛ logger.info ("أغلق الاتصال من" + socket.getlocaladdress () + "") ؛ } أخيرًا {try {if (socket! = null) {socket.close () ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ }}}}يرجى النقر هنا للحصول على عنوان المشروع. إذا كنت تعتقد أنه جيد جدًا ، آمل أن تتمكن من إعطائي نجمة.
مراجع
موقع مواقع رسمية memcached
بروتوكول Memcache
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.