ແມ່ນຫຍັງຄືຄຸນນະສົມບັດແບບພິເສດ Hadoop MapReduce?

The basic MapReduce programming explains the work flow details. But it does not cover the actual working details inside the MapReduce programming framework. ບົດຄວາມນີ້ຈະອະທິບາຍການເຄື່ອນໄຫວຂໍ້ມູນຜ່ານສະຖາປັດຕະ MapReduce ແລະໂທ API ທີ່ໃຊ້ໃນການເຮັດການປະມວນຜົນທີ່ແທ້ຈິງ. ພວກເຮົາຍັງຈະປຶກສາຫາລືເຕັກນິກການປັບແຕ່ງແລະການທໍາງານທີ່ສຸດສໍາລັບຄວາມຕ້ອງການສະເພາະໃດຫນຶ່ງຄໍາຮ້ອງສະຫມັກ.

ລັກສະນະກ້າວຫນ້າທາງດ້ານ MapReduce ອະທິບາຍການປະຕິບັດແລະລາຍລະອຽດໃນລະດັບຕ່ໍາ. ໃນການດໍາເນີນໂຄງ MapReduce ປົກກະຕິ, APIs ພຽງແຕ່ຮູ້ແລະການນໍາໃຊ້ຂອງເຂົາເຈົ້າແມ່ນພຽງພໍທີ່ຈະຂຽນຄໍາຮ້ອງສະຫມັກ. ແຕ່ລາຍລະອຽດໃນການ MapReduce ເປັ​​ນຕ້ອງການທີ່ຈະເຂົ້າໃຈລາຍລະອຽດການເຮັດວຽກຕົວຈິງແລະໄດ້ຮັບຄວາມເຊື່ອຫມັ້ນ.

ໃນປັດຈຸບັນໃຫ້ພວກເຮົາປຶກສາຫາລືລັກສະນະກ້າວຫນ້າທາງດ້ານໃນພາກສ່ວນດັ່ງຕໍ່ໄປນີ້.

ປະເພດລູກຄ້າ (Data): ສໍາຫລັບຜູ້ໃຊ້ບໍລິ Mapper ແລະ Reducer, ໂຄງຮ່າງການ Hadoop MapReduce ສະເຫມີການນໍາໃຊ້ຂໍ້ມູນປະເພດ. ຂໍ້ມູນເຊິ່ງ passes ໂດຍຜ່ານ Mapper ແລະລົດຂະຫນາດແມ່ນເກັບຮັກສາໄວ້ໃນວັດຖຸ Java.

  • ສາມາດຂຽນໄດ້ Interface: ໃນການໂຕ້ຕອບມາດຂຽນເປັນຫນຶ່ງຂອງການໂຕ້ຕອບທີ່ສໍາຄັນທີ່ສຸດ. ວັດຖຸທີ່ສາມາດໄດ້ຮັບການຮວບຮວມກັບ / ຈາກໄຟລ໌ແລະໃນໄລຍະການນໍາໃຊ້ເຄືອຂ່າຍການໂຕ້ຕອບນີ້. Hadoop ຍັງໄດ້ນໍາໃຊ້ໃນການໂຕ້ຕອບນີ້ເພື່ອສົ່ງຂໍ້ມູນໃນຮູບແບບຕໍ່ເນື່ອງ. ບາງສ່ວນຂອງຫ້ອງການທີ່ປະຕິບັດໃນການໂຕ້ຕອບສາມາດຂຽນໄດ້ທີ່ໄດ້ກ່າວມາຂ້າງລຸ່ມນີ້
  1. ລະດັບຄວາມ(ມັນເກັບຂໍ້ມູນ String)
  2. LongWritable
  3. FloatWritable
  4. IntWritable
  5. BooleanWritable

ຊະນິດຂໍ້ມູນລູກຄ້າຍັງສາມາດໄດ້ຮັບການເຮັດໄດ້ໂດຍການປະຕິບັດ ສາມາດຂຽນໄດ້ interface. Hadoop ສາມາດສົ່ງປະເພດຂອງຂໍ້ມູນລູກຄ້າ (ທີ່ເຫມາະກັບຄວາມຕ້ອງຂອງທ່ານ) ທີ່ປະຕິບັດໃນການໂຕ້ຕອບສາມາດຂຽນໄດ້.

ຕໍ່ໄປນີ້ແມ່ນການໂຕ້ຕອບສາມາດຂຽນໄດ້ທີ່ຈະມີສອງວິທີການ READFIELD ແລະຂຽນ. ວິທີທໍາອິດ (READFIELD) ກຽມຂໍ້ມູນຂອງວັດຖຸຈາກຂໍ້ມູນທີ່ໄດ້ທີ່ມີຢູ່ໃນໃນ’ ນ້ໍາຄູ່. ວິທີທີ່ສອງ (ຂຽນ) ຖືກນໍາໃຊ້ເພື່ອສ້າງວັດຖຸທີ່ຈະນ້ໍາຄູ່ 'ອອກ'. ສັນຍາທີ່ສໍາຄັນທີ່ສຸດຂອງຂະບວນການທັງຫມົດແມ່ນວ່າຄໍາສັ່ງຂອງອ່ານແລະຂຽນນ້ໍາຄູ່ຄືກັນ.

Listing1: ສະແດງໃຫ້ເຫັນໃນການໂຕ້ຕອບສາມາດຂຽນໄດ້

ການໂຕ້ຕອບສາທາລະນະສາມາດຂຽນໄດ້ {

READFIELD void(DataInput ໃນ);

void ຂຽນ(DataOutput ອອກ);

}

ປະເພດລູກຄ້າ (ທີ່ສໍາຄັນ): ໃນພາກກ່ອນນັ້ນພວກເຮົາໄດ້ປຶກສາຫາລືກ່ຽວກັບປະເພດຂໍ້ມູນທີ່ກໍາຫນົດເອງເພື່ອຕອບສະຫນອງຄວາມຮຽກຮ້ອງຕ້ອງຂໍ້ມູນສະເພາະ. ມັນຄຸ້ມຄອງສ່ວນຫນຶ່ງມູນຄ່າພຽງແຕ່. ໃນປັດຈຸບັນພວກເຮົາຍັງຈະປຶກສາຫາລືກ່ຽວກັບປະເພດທີ່ສໍາຄັນທີ່ກໍາຫນົດເອງ. ໃນ Hadoop MapReduce, ການ Reducer ຂະບວນການທີ່ສໍາຄັນໃນຄໍາສັ່ງທີ່ການຈັດລຽງ. ດັ່ງນັ້ນປະເພດທີ່ສໍາຄັນທີ່ກໍາຫນົດເອງຕ້ອງການທີ່ຈະປະຕິບັດໃນການໂຕ້ຕອບທີ່ເອີ້ນວ່າ WritableComparable. ປະເພດທີ່ສໍາຄັນຍັງຄວນປະຕິບັດ hashCode ().

ຕໍ່ໄປນີ້ແມ່ນສະແດງໃຫ້ເຫັນ WritableComparable interface. ມັນເປັນຕົວແທນເປັນ ສາມາດຂຽນໄດ້ ເຊິ່ງແມ່ນຍັງ ເມື່ອທຽບກັບ.

Listing2: ສະແດງໃຫ້ເຫັນ WritableComparable interface

WritableComparable ການໂຕ້ຕອບສາທາລະນະ<T>

ຂະຫຍາຍສາມາດຂຽນໄດ້, ເມື່ອທຽບກັບ<T>

ວິທີການນໍາໃຊ້ປະເພດລູກຄ້າ: ພວກເຮົາໄດ້ສົນທະນາແລ້ວມູນຄ່າການລູກຄ້າແລະປະເພດທີ່ສໍາຄັນທີ່ສາມາດໄດ້ຮັບການປຸງແຕ່ງໂດຍການ Hadoop. ໃນປັດຈຸບັນພວກເຮົາຈະປຶກສາຫາລືກົນໄກເພື່ອໃຫ້ Hadoop ສາມາດເຂົ້າໃຈມັນ. ຈຸດປະສົງ JobConf (ເຊິ່ງໄດ້ກໍານົດວຽກເຮັດງານທໍາ) ມີສອງວິທີທີ່ເອີ້ນວ່າ setOutputKeyClass () ແລະ setOutputValueClass () ແລະວິທີການເຫຼົ່ານີ້ແມ່ນໄດ້ຖືກນໍາໃຊ້ເພື່ອຄວບຄຸມຄ​​່າແລະປະເພດຂໍ້ມູນທີ່ສໍາຄັນ. ຖ້າຫາກວ່າ Mapper ການຜະລິດປະເພດທີ່ແຕກຕ່າງກັນທີ່ບໍ່ມີຄໍາວ່າ Reducer ຫຼັງຈາກນັ້ນ JobConf ຂອງ setMapOutputKeyClass () ແລະ setMapOutputValueClass () ວິທີການສາມາດຖືກນໍາໃຊ້ເພື່ອກໍານົດປະເພດການປ້ອນຂໍ້ມູນຕາມທີ່ຄາດໄວ້ໂດຍການຫຼຸດຜ່ອນ.

ການປະຕິບັດໄດ້ໄວຂຶ້ນ: ຂະບວນການການຄັດເລືອກໃນຕອນຕົ້ນແມ່ນນ້ອຍຊ້າຍ້ອນວ່າມັນທໍາອິດອ່ານປະເພດທີ່ສໍາຄັນຈາກນ້ໍາຫຼັງຈາກນັ້ນແຍກນ້ໍາ byte ໄດ້ (ການນໍາໃຊ້ READFIELD() ວິ​ທີ​ການ) ແລະຫຼັງຈາກນັ້ນສຸດທ້າຍໂທຫາ compareTo () ວິທີການຂອງລະດັບທີ່ສໍາຄັນ. ວິທີການໄວຈະໄດ້ຮັບການຕັດສິນໃຈຄໍາສັ່ງລະຫວ່າງທີ່ໃຊ້ໂດຍການກວດສອບນ້ໍາ byte ໂດຍບໍ່ມີການແຍກທີ່ກໍານົດໄວ້ຂໍ້ມູນທັງຫມົດ. ເພື່ອປະຕິບັດກົນໄກການປຽບທຽບນີ້ໄວຂຶ້ນ, WritableComparator ລະດັບສາມາດໄດ້ຮັບການຂະຫຍາຍທີ່ມີການປຽບທຽບສະເພາະກັບຊະນິດຂໍ້ມູນຂອງທ່ານ. ຕໍ່ໄປນີ້ແມ່ນການປະກາດຫ້ອງຮຽນ.

Listing3: ສະແດງໃຫ້ເຫັນ WritableComparator ທີ່ຫ້ອງຮຽນ

WritableComparator ລະດັບສາທາລະນະ

ຂະຫຍາຍຈຸດປະສົງ

ປະຕິບັດ RawComparator

ດັ່ງນັ້ນຂໍ້ມູນລູກຄ້າແລະປະເພດທີ່ສໍາຄັນອະນຸຍາດໃຫ້ການນໍາໃຊ້ໂຄງສ້າງຂໍ້ມູນໃນລະດັບສູງໃນຂອບ Hadoop. ໃນຄໍາຮ້ອງສະຫມັກພາກປະຕິບັດ Hadoop ຊະນິດຂໍ້ມູນຂອງລູກຄ້າເປັນຫນຶ່ງໃນຄວາມຕ້ອງການສໍາຄັນທີ່ສຸດ. ດັ່ງນັ້ນຄຸນນະສົມບັດນີ້ອະນຸຍາດໃຫ້ການນໍາໃຊ້ປະເພດສາມາດຂຽນໄດ້ custom ແລະການສະຫນອງການປັບປຸງການປະຕິບັດທີ່ສໍາຄັນ.

ຮູບແບບການປ້ອນຂໍ້ມູນ: The InputFormat ເປັນຫນຶ່ງຂອງການໂຕ້ຕອບຂອງຄວາມສໍາຄັນທີ່ສຸດທີ່ກໍານົດຂໍ້ມູນປະກອບການຂອງວຽກເຮັດງານທໍາ MapReduce ເປັ​​ນ. Hadoop ມີບໍລິການປະເພດທີ່ແຕກຕ່າງກັນຂອງ InputFormat ສໍາລັບການຕີລາຄາຂອງປະເພດຕ່າງໆຂອງການປ້ອນຂໍ້ມູນ. ເລີ່ມຕົ້ນທົ່ວໄປທີ່ສຸດແລະເປັນ TextInputFormat ທີ່ໃຊ້ໃນການອ່ານຂອງສາຍຈາກໄຟລ໌ຂໍ້ຄວາມ. ເຊັ່ນດຽວກັນ SequenceFileInputFormat ຖືກນໍາໃຊ້ເພື່ອອ່ານຮູບແບບເອກະສານຄູ່.

ວຽກງານພື້ນຖານຂອງ InputFormat ຄືການອ່ານຂໍ້ມູນຈາກເອກະສານປະກອບ. ການປະຕິບັດຂອງລູກຄ້າ InputFormat ກໍເປັນໄປໄດ້ຕາມຄວາມຕ້ອງການຄໍາຮ້ອງສະຫມັກຂອງທ່ານ. ສໍາລັບການເລີ່ມຕົ້ນ TextInputFormat ການປະຕິບັດທີ່ສໍາຄັນແມ່ນ byte ຊົດເຊີຍຂອງອອນໄລນ໌ແລະມູນຄ່າແມ່ນເນື້ອໃນຂອງອອນໄລນ໌ລະງັບໂດຍ ' n ໄດ້’ character. ສໍາລັບການປະຕິບັດທີ່ກໍາຫນົດເອງ, ແຍກຕ່າງຫາກສາມາດລັກສະນະໃດແລະ InputFormat ຈະແຍກຕາມຄວາມເຫມາະສົມ.

ວຽກເຮັດງານທໍາຂອງ InputFormat ເປັນທີ່ຈະແບ່ງປັນໄຟລ໌ວັດຖຸດິບ (ແຫລ່ງຂໍ້ມູນ) ເຂົ້າໄປໃນ fragments ທີ່ມີການປ້ອນຂໍ້ມູນໃນແຜນທີ່ວຽກງານ. fragments ເຫຼົ່ານີ້ / splits ແມ່ນຫໍ່ຫຸ້ມຢູ່ໃນກໍລະນີຂອງການໂຕ້ຕອບ InputSplit ໄດ້. ແຫຼ່ງກອບມີຂໍ້ມູນສາມາດຈະມີສິ່ງໃດຄືຕາຕະລາງຖານຂໍ້ມູນ, file xml ຫຼືເອກະສານອື່ນໆ. ດັ່ງນັ້ນການແບ່ງປັນຈະໄດ້ຮັບການປະຕິບັດໂດຍອີງໃສ່ຄວາມຕ້ອງການຄໍາຮ້ອງສະຫມັກ. ຈຸດທີ່ສໍາຄັນທີ່ສຸດແມ່ນວ່າການດໍາເນີນງານການແບ່ງປັນຄວນຈະເປັນໄວແລະລາຄາຖືກ.

ຫຼັງຈາກແຕກອອກເອງໄຟລ໌, ອ່ານການດໍາເນີນງານຈາກການແບ່ງປັນສ່ວນບຸກຄົນມີຄວາມສໍາຄັນຫຼາຍ. The RecordReader ແມ່ນຮັບຜິດຊອບສໍາລັບການອ່ານຂໍ້ມູນຈາກການແບ່ງປັນ. The RecordReader ຄວນຈະປະສິດທິພາບພຽງພໍທີ່ຈະຈັດການຄວາມຈິງທີ່ວ່າການແບ່ງປັນບໍ່ສະເຫມີໄປໃນທີ່ສຸດ neatly ຢູ່ໃນຕອນທ້າຍຂອງເສັ້ນໄດ້. The RecordReader ສະເຫມີໄປອ່ານ till ໃນຕອນທ້າຍຂອງອອນໄລນ໌ເຖິງແມ່ນວ່າຖ້າຫາກວ່າມັນຂ້າມທີ່ສຸດທິດສະດີຂອງການແບ່ງປັນທີ່. ຄຸນນະສົມບັດນີ້ເປັນສິ່ງສໍາຄັນຫຼາຍເພື່ອຫຼີກເວັ້ນການຂາດຫາຍໄປຂອງການບັນທຶກການທີ່ອາດຈະໄດ້ຂ້າມເຂດແດນ InputSplit.

  • InputFormat Custom: ໃນຄໍາຮ້ອງສະຫມັກຂັ້ນພື້ນຖານ InputFormat ຖືກນໍາໃຊ້ໂດຍກົງ. ແຕ່ສໍາລັບການທີ່ກໍາຫນົດເອງອ່ານວິທີທີ່ດີທີ່ສຸດຄືການຄາດ FileInputFormat. ນີ້ລະດັບບໍ່ມີຕົວຕົນໃຫ້ເຮັດວຽກໃນການຈັດການໄຟລ໌ເປັນຄວາມຕ້ອງການຄໍາຮ້ອງສະຫມັກ. ສໍາລັບການວິເຄາະ custom, the getRecordReader () ວິທີການຕ້ອງໄດ້ຮັບການແທນທີ່ຈະກັບຄືນມາຍົກຕົວຢ່າງຂອງ RecordReader. ນີ້ RecordReader ແມ່ນຄວາມຮັບຜິດຊອບສໍາລັບການອ່ານແລະການວິເຄາະ.
  • ແຫລ່ງຂ່າວອື່ນອີກ (Data): The InputFormat ອະທິບາຍທັງສອງສິ່ງທີ່, ທໍາອິດແມ່ນການນໍາສະເຫນີຂອງຂໍ້ມູນທີ່ Mapper ໄດ້ແລະຄັ້ງທີສອງແມ່ນແຫລ່ງຂໍ້ມູນ. ທີ່ສຸດຂອງການປະຕິບັດແມ່ນອີງໃສ່ການ FileInputFormat, ບ່ອນທີ່ແຫລ່ງຂໍ້ມູນທີ່ເປັນລະບົບໄຟພາຍໃນປະເທດຂອງ HDFS (Hadoop Distributed File System).ແຕ່ສໍາລັບການປະເພດອື່ນໆຂອງແຫຼ່ງຂໍ້ມູນ, ການປະຕິບັດການລູກຄ້າຂອງ InputFormat ຈໍາເປັນຕ້ອງມີ. For example, ຖານຂໍ້ມູນ NoSQL ຄື HBase ໃຫ້ TableInputFormat ສໍາລັບການອ່ານຂໍ້ມູນຈາກຕາຕະລາງຖານຂໍ້ມູນ. ດັ່ງນັ້ນແຫລ່ງຂໍ້ມູນທີ່ສາມາດຈະມີສິ່ງໃດທີ່ສາມາດໄດ້ຮັບການຈັດການໂດຍການປະຕິບັດທີ່ກໍາຫນົດເອງ.

ຮູບແບບການຜະລິດ: The OutputFormat ແມ່ນຄວາມຮັບຜິດຊອບສໍາລັບການດໍາເນີນງານການຂຽນ. ພວກເຮົາໄດ້ສົນທະນາແລ້ວວ່າ InputFormat ແລະ RecordReader ການໂຕ້ຕອບມີຄວາມຮັບຜິດຊອບສໍາລັບການອ່ານຂໍ້ມູນເຂົ້າໄປໃນໂຄງການ MapReduce. ຫຼັງຈາກການປຸງແຕ່ງຂໍ້ມູນ, ການດໍາເນີນງານການຂຽນເພື່ອການເກັບຮັກສາຖາວອນແມ່ນການຄຸ້ມຄອງໂດຍ OutputFormat ແລະ RecordWriter ການໂຕ້ຕອບ. ຮູບແບບໃນຕອນຕົ້ນແມ່ນ TextOutputFormat ທີ່ຂຽນທີ່ສໍາຄັນ / ຄູ່ມູນຄ່າເປັນສະຕິງການເອກະສານຜົນຜະລິດ. ຮູບແບບການຜະລິດອື່ນໆເປັນ SequenceFileOutputFormat ແລະມັນເຮັດໃຫ້ຂໍ້ມູນໃນຮູບແບບໄບນາລີ. ຫ້ອງຮຽນທັງຫມົດເຫຼົ່ານີ້ນໍາໃຊ້ ຂຽນ () ແລະ READFIELD () ວິທີການຂອງ ສາມາດຂຽນໄດ້ ຫ້ອງຮຽນ.

The OutputFormat ການປະຕິບັດຕ້ອງໄດ້ຮັບການປັບແຕ່ງໃຫ້ຂຽນຂໍ້ມູນໃນຮູບແບບທີ່ກໍາຫນົດເອງ. The FileOutputFormat ລະດັບບໍ່ມີຕົວຕົນຕ້ອງໄດ້ຮັບການຂະຫຍາຍໄປເຮັດໃຫ້ລູກຄ້າ. The JobConf.setOutputFormat () ວິທີການຕ້ອງໄດ້ຮັບການແກ້ໄຂການນໍາໃຊ້ຮູບແບບທີ່ກໍາຫນົດເອງທີ່ແຕກຕ່າງກັນ.

ຂໍ້ມູນການແຍກ: ການແບ່ງປັນສາມາດຖືກກໍານົດເປັນຂະບວນການທີ່ກໍາຫນົດການຍົກຕົວຢ່າງ Reducer ຈະໄດ້ຮັບທີ່ລະດັບປານກາງທີ່ສໍາຄັນ / ຄູ່ມູນຄ່າ. ແຕ່ລະ Mapper ຄວນຈະກໍານົດ Reducer ຈຸດຫມາຍປາຍທາງສໍາລັບການທັງຫມົດທີ່ສໍາຄັນຜົນຜະລິດຂອງຕົນ / ຄູ່ຄ່າ. ຈຸດທີ່ສໍາຄັນທີ່ສຸດແມ່ນວ່າສໍາລັບການທີ່ສໍາຄັນໂດຍບໍ່ຄໍານຶງຂອງການຍົກຕົວຢ່າງ Mapper ຂອງຕົນຢ່າງໃດ, ການແບ່ງປັນຈຸດຫມາຍປາຍທາງຢ່າງດຽວກັນ. ສໍາລັບການປະຕິບັດວຽກຂໍ້ມູນເຫດຜົນບໍ່ເຄີຍຕິດຕໍ່ສື່ສານກັບແຕ່ລະຄົນອື່ນໆທີ່ຈະມີການແບ່ງປັນທີ່ສໍາຄັນໂດຍສະເພາະເປັນການ.

The ແບ່ງປັນ ໃນການໂຕ້ຕອບໄດ້ຖືກນໍາໃຊ້ໂດຍລະບົບການ Hadoop ການກໍານົດການແບ່ງປັນຈຸດຫມາຍປາຍທາງສໍາລັບການທີ່ສໍາຄັນຄູ່ມູນຄ່າ /. ຈໍານວນຂອງການແບ່ງປັນທີ່ຄວນຈະມີຄໍາທີ່ມີຈໍານວນຂອງລົດວຽກງານ. ໂຄງຮ່າງການ MapReduce ຕັດສິນກໍານົດຈໍານວນຂອງການແບ່ງປັນໃນເວລາທີ່ວຽກເຮັດງານທໍາເລີ່ມຕົ້ນ.

ຕໍ່ໄປນີ້ແມ່ນລາຍເຊັນຂອງການໂຕ້ຕອບການແບ່ງປັນການ.

Listing 4: ສະແດງໃຫ້ເຫັນໃນການໂຕ້ຕອບການແບ່ງປັນ

ການແບ່ງປັນການໂຕ້ຕອບສາທາລະນະ<K2, V2>

ຂະຫຍາຍ JobConfigurable

ສະ​ຫຼຸບ: ໃນການສົນທະນານີ້ພວກເຮົາໄດ້ກວມເອົາທີ່ສໍາຄັນທີ່ສຸດຄຸນນະສົມບັດ Hadoop MapReduce. ຄຸນນະສົມບັດເຫຼົ່ານີ້ແມ່ນມີປະໂຫຍດສໍາລັບຈຸດປະສົງການລູກຄ້າ. ໃນຄໍາຮ້ອງສະຫມັກພາກປະຕິບັດ MapReduce, ການເລີ່ມຕົ້ນຂອງ APIs ບໍ່ໄດ້ມີການນໍາໃຊ້ຫຼາຍ. Rather, ຄຸນນະສົມບັດທີ່ກໍາຫນົດເອງ (ເຊິ່ງແມ່ນອີງໃສ່ APIs ຮັບການ) ມີຜົນກະທົບທີ່ສໍາຄັນ. ລູກຄ້າທັງຫມົດເຫຼົ່ານີ້ສາມາດເຮັດໄດ້ຢ່າງງ່າຍດາຍເມື່ອແນວຄວາມຄິດມີຄວາມຊັດເຈນ. ຫວັງວ່າບົດຄວາມນີ້ຈະເປັນປະໂຫຍດສໍາລັບຄວາມເຂົ້າໃຈລັກສະນະກ້າວຫນ້າທາງດ້ານແລະການປະຕິບັດຂອງເຂົາເຈົ້າ.

 

Tagged on: , ,
============================================= ============================================== Buy best TechAlpine Books on Amazon
============================================== ---------------------------------------------------------------- electrician ct chestnutelectric
error

Enjoy this blog? Please spread the word :)

Follow by Email
LinkedIn
LinkedIn
Share