Въведение
Някои от конфигурационните свойства, открити в Apache Hadoop, имат пряк ефект върху клиентите, като Apache HBase. Едно от тези свойства се нарича „dfs.datanode.max.xcievers“ и принадлежи към подпроекта HDFS. Той определя броя на нишките от страна на сървъра и – до известна степен – гнездата, използвани за връзки за данни. Задаването на този номер твърде ниско може да причини проблеми, докато разраствате или увеличавате използването на вашия клъстер. Тази публикация ще ви помогне да разберете какво се случва между клиента и сървъра и как да определите разумен брой за това свойство.
Проблемът
Тъй като HBase съхранява всичко, от което се нуждае, в HDFS, твърдата горна граница, наложена от конфигурационното свойство „dfs.datanode.max.xcievers“, може да доведе до твърде малко ресурси, достъпни за HBase, което се проявява като IOExceptions от двете страни на връзката. Ето пример от пощенския списък на HBase [1], където следните съобщения първоначално бяха регистрирани от страната на RegionServer:
2008-11-11 19:55:52,451 ИНФОРМАЦИЯ org.apache.hadoop.dfs.DFSClient: Изключение в createBlockOutputStream java.io.IOException:Не можа прочетено от поток
2008-11-11 19:55:52,451 ИНФОРМАЦИЯ org.apache.hadoop.dfs.DFSClient: Изоставяне на блок blk_-546701410875>2008-11-11 19:55:52,451 ИНФОРМАЦИЯ org.apache.hadoop.dfs.DFSClient: Изоставяне на блок blk_-546701410875>2008-11-11 19:55:52,451 11 19:55:58,455 WARN org.apache.hadoop.dfs.DFSClient: Изключение на DataStreamer:java.io.IOException:Не може да се създаде нов блок.
2008-11-11 19:55:58,455 WARN WARN .hadoop.dfs.DFSClient:Грешка Възстановяване на блок blk_-5467014108758633036_595771 bad datanode[0]
2008-11-11 19:55:58,482 FATAL се изисква Reserve. . Принудително изключване на сървъра
Корелирането на това с регистрационните файлове на Hadoop DataNode разкри следния запис:
ГРЕШКА org.apache.hadoop.dfs.DataNode: DatanodeRegistration(10.10.10.53:50010,storageID=DS-1570581820-10.10.10.53-50010-1224117842339,infoPort=50075, ipcPort=50020):DataXceiver:java.io.IOException надвишава лимита на xevers6P>2xcei8
В този пример ниската стойност на “dfs.datanode.max.xcievers” за DataNodes доведе до изключване на целия RegionServer. Това е наистина лоша ситуация. За съжаление, няма твърдо и бързо правило, което обяснява как да се изчисли необходимата граница. Обикновено се препоръчва да увеличите числото от 256 по подразбиране до нещо като 4096 (вижте [1], [2], [3], [4] и [5] за справка). Това става чрез добавяне на това свойство към файла hdfs-site.xml на всички DataNodes (обърнете внимание, че е изписано неправилно):
Забележка:Ще трябва да рестартирате своите DataNodes, след като направите тази промяна в конфигурационния файл.
Това би трябвало да помогне с горния проблем, но все пак може да искате да знаете повече за това как всичко това играе заедно и какво прави HBase с тези ресурси. Ще обсъдим това в останалата част от тази публикация. Но преди да го направим, трябва да сме наясно защо не можете просто да зададете това число много високо, да речем 64K и да приключите с него.
Има причина за горна граница и тя е двойна:първо, нишките се нуждаят от собствен стек, което означава, че заемат памет. За текущите сървъри това означава 1MB на нишка[6] по подразбиране. С други думи, ако използвате всички 4096 нишки на DataXceiver, имате нужда от около 4 GB памет, за да ги поберете. Това намалява пространството, което сте задали за memstore и блокове, както и всички други движещи се части на JVM. В най-лошия случай може да срещнете изключение OutOfMemoryException и процесът на RegionServer е тост. Искате да зададете това свойство на сравнително високо число, но не и твърде високо.
Второ, като имате тези много нишки активни, вие също ще видите, че вашият процесор става все по-натоварен. Ще има много превключвания на контекста, за да се справят с цялата едновременна работа, което отнема ресурси за реалната работа. Както при притесненията относно паметта, вие искате броят на нишките да не нараства безкрайно, а да осигури разумна горна граница – и за това е „dfs.datanode.max.xcievers“.
Подробности за файловата система на Hadoop
От страна на клиента HDFS библиотеката предоставя абстракцията, наречена Path. Този клас представлява файл във файлова система, поддържана от Hadoop, представена от класа FileSystem. Има няколко конкретни реализации на абстрактния клас FileSystem, един от които е DistributedFileSytem, представляващ HDFS. Този клас от своя страна обхваща действителния клас DFSClient, който обработва всички взаимодействия с отдалечените сървъри, т.е. NameNode и многото DataNode.
Когато клиент, като HBase, отвори файл, той прави това, например чрез извикване на методите open() или create() на класа FileSystem, тук най-опростените превъплъщения
public DFSInputStream open(String src) хвърля IOException
public FSDataOutputStream create(Path f) хвърля IOException
Върнатият екземпляр на поток е това, което се нуждае от сокет и нишка от страна на сървъра, които се използват за четене и запис на блокове от данни. Те са част от договора за обмен на данни между клиента и сървъра. Имайте предвид, че има други, базирани на RPC протоколи, които се използват между различните машини, но за целите на тази дискусия те могат да бъдат игнорирани.
Върнатият екземпляр на поток е специализиран клас DFSOutputStream или DFSInputStream, който обработва цялото взаимодействие с NameNode, за да разбере къде се намират копията на блоковете и комуникацията на данни за блок за DataNode.
От страна на сървъра DataNode обвива екземпляр на DataXceiverServer, който е действителният клас, който чете горния конфигурационен ключ и също така хвърля горното изключение, когато ограничението е надвишено.
Когато DataNode стартира, той създава група нишки и стартира споменатия екземпляр DataXceiverServer по следния начин:
this.threadGroup =new ThreadGroup(“dataXceiverServer”);
this.dataXceiverServer =нов Daemon( threadGroup,
нов DataXceiverServer(ss, conf, това));
this.threadGroup.setDaemon(true); // автоматично унищожаване, когато е празно
Обърнете внимание, че нишката DataXceiverServer вече заема едно място от групата нишки. DataNode също има този вътрешен клас за извличане на броя на текущо активните нишки в тази група:
/** Брой едновременни xceivers на възел. */
int getXceiverCount() {
return threadGroup ==null? 0 :threadGroup.activeCount();
}
Блоковете за четене и запис, инициирани от клиента, причиняват осъществяване на връзка, която е обвита от DataXceiverServer нишката в DataXceiver екземпляр. По време на това изключване се създава нишка и се регистрира в горната група нишки. Така за всяка активна операция за четене и запис се проследява нова нишка от страна на сървъра. Ако броят на нишките в групата надвишава конфигурирания максимум, тогава споменатото изключение се хвърля и се записва в регистрационните файлове на DataNode:
if (curXceiverCount> dataXceiverServer.maxXceiverCount) {
throw new IOException(“xceiverCount ” + curXceiverCount
+ ” надвишава ограничението за едновременни xcievers ”
+ dataXceiverServer.maxXceiverCount);
}
Последствия за клиентите
Сега въпросът е как четенето и писането на клиента са свързани с нишките от страна на сървъра. Преди да навлезем в подробностите обаче, нека използваме информацията за отстраняване на грешки, която класът DataXceiver регистрира, когато е създаден и затворен
LOG.debug(“Броят на активните връзки е:” + datanode.getXceiverCount());
…
LOG.debug(datanode.dnRegistration + “:Броят на активните връзки е:” + datanode.getXceiverCount());
и наблюдавайте по време на стартиране на HBase какво е регистрирано в DataNode. За простота това се прави на псевдо разпределена настройка с един екземпляр на DataNode и RegionServer. По-долу е показан горната част на страницата за състоянието на RegionServer.
Важната част е в секцията „Показатели“, където пише „storefiles=22“. Така че, ако приемем, че HBase има поне толкова много файлове за обработка, плюс някои допълнителни файлове за дневника с предварителна запис, трябва да видим горното съобщение за регистрационни файлове, че имаме поне 22 „активни връзки“. Да стартираме HBase и да проверим регистрационните файлове на DataNode и RegionServer:
Команден ред:
$ bin/start-hbase.sh
…
Регистър на DataNode:
2012-03-05 13:01:35,309 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:Броят на активните връзки е:1
2012-03-05 13:01:35,315 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010-, storageID=DS 1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:2
12/03/05 13:01:35 глобален сървър35 глобален сървър. globalMemStoreLimitLowMark=347.1m, maxHeap=991.7m
12/03/05 13:01:39 ИНФОРМАЦИЯ http.HttpServer:Портът, върнат от webServer.getConnectors()[0].getLocalPort() преди open() е -1 е -1 е -1. . Отваряне на слушателя на 60030
2012-03-05 13:01:40,003 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:1
12/03/05 13:01:40 INFO regionserver.HRegionServer:Получена заявка за отваряне на регион:-ROOT-,,0.70236052
2012-03-05 13:01:40,882 DEBUG org.apache.hadoop.hdfs.de.datade.data :Броят на активните връзки е:3
2012-03-05 13:01:40,884 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:5004-14 storageID86 -10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:4
2012-03-05 13:01:40,888 DEBUG org.f.server.a. datanode.DataNode:Броят на активните връзки е:3
…
12/03/05 13:01:40 INFO regionserver.HRegion:Onlined -ROOT-,,0.70236052; next sequenceid=63083
2012-03-05 13:01:40,982 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:3
2012-03-05 13 :01:40,983 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.604-13017pc=10.0.0.604-132510pc=13017000000000000000000000000000000000000000000000 Броят на активните връзки е:4
…
12/03/05 13:01:41 INFO regionserver.HRegionServer:Получена заявка за отваряне на регион:.META.,,1.1028785192
2012-03 -05 13:01:41,026 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:3
2012-03-05 13:01:41,027 DEBUG org.apache.hadoop. hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=127.0.0.1:50010, storageID =DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=502/500 порта е активно:ipcPort<500/500/5000/502 …
12/03/05 13:01:41 INFO regionserver.HRegion:Онлайн .META.,,1.1028785192; next sequenceid=63082
2012-03-05 13:01:41,109 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:3
2012-03-05 13 :01:41,114 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:4
2012-03-05 13:01:41,117 DEBUG org.apache.hadoop.server.hdfs .datanode.DataNode:Броят на активните връзки е:5
12/03/05 13:01:41 ИНФО regionserver.HRegionServer:Получена заявка за отваряне на 16 регион(и)
12/03/05 13 :01:41 INFO regionserver.HRegionServer:Получена заявка за отваряне на регион:usertable,,1330944810191.62a312d67981c86c42b6bc02e6ec7e3f.
12/03/05 13:03/05 12/03/04 13:03:12/03/05 13:03:00 13:00:00:00:00 11HR12:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 1330944810191.90d287473fe223f0ddc137020efda25d.
…
2012-03-05 13:01:41,246 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:Броят на активните връзки е:6
2012-03-05 13:01:41,248 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:7
…
2012-03-05 13:01:41,257 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-142364025-1423640250-142364040-1200-142364040-12040-142364020-1200-142364040-1200-142364040-1200-2000 , infoPort=50075, ipcPort=50020):Броят на активните връзки е:10
2012-03-05 13:01:41,257 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode.DataNode:Datanode(17 0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:9
12…03:01:41 INFO regionserver.HRegion:Онлайн потребителска таблица,user1120311784,1330944810191.90d287473fe223f0ddc137020efda25d.; next sequenceid=62917
12/03/05 13:01:41 INFO regionserver.HRegion:Онлайн потребителска таблица,,1330944810191.62a312d67981c86c42b6bc02e6ec7e3f.; next sequenceid=62916
…
12/03/05 13:01:41 INFO regionserver.HRegion:Онлайн потребителска таблица,user1361265841,1330944811370.80663fcf291e3ce009640; next sequenceid=62919
2012-03-05 13:01:41,474 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:6
2012-03-05 13 :01:41,491 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:7
2012-03-05 13:01:41,495 DEBUG org.apache.hadoop.server.hdfs .datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50075, ipcPort=50075, ipcPort=50075, ipcPort=0umber20br=5> активна връзка:ipcPort=0u0202-5> -05 13:01:41,508 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:7
…
12/03/05 13:01:41 INFO regionserver .HRegion:Онлайн потребителска таблица,user1964968041,1330944848231.dd89596e9129e1caa7e07f8a491c9734.; next sequenceid=62920
2012-03-05 13:01:41,618 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:6
2012-03-05 13 :01:41,621 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.604-13317pc=10.0.0.604-13250pc=130170000000000000000000000000000000000000000000000000 Броят на активните връзки е:7
…
2012-03-05 13:01:41,829 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010 storageID =DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:7
12/03/05 13:01:01:00:00:00 потребител Онлайн таблица:HR. ,user515290649,1330944849739.d23924dc9e9d5891f332c337977af83d.; next sequenceid=62926
2012-03-05 13:01:41,832 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:6
2012-03-05 13 :01:41,838 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.604-13750pc=13201000000000000000000000000000000000000000000000000) Броят на активните връзки е:7
12/03/05 13:01:41 INFO regionserver.HRegion:Онлайн потребителска таблица,user757669512,1330944850808.cd0d6f16d8ae9cf0c9277f6b9c; next sequenceid=62929
…
2012-03-05 14:01:39,711 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:4
2012 -03-05 22:48:41,945 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-1423642448-1423642448-10.02010, info=10.030305000000000000000000 ipcPort=50020):Броят на активните връзки е:4
12/03/05 22:48:41 ИНФО regionserver.HRegion:Онлайн потребителска таблица,user757669512,1330944850808.cd0d6f16d8c9c96f6f0c9c96f7cd9cd6f7cd6cd9cd7dcd6cd6cd6cd6cd6dc9cd7cdcdcdcdcdcd=50020):следващ идентификатор на последователност=62929
2012-03-05 22:48:41,963 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=6.0644) -50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:4
Можете да видите как регионите се отварят един след друг, но това, което също може да забележите, е, че броят на активните връзки никога не се изкачва до 22 – той едва достига дори 10. Защо така? За да разберем това по-добре, трябва да видим как файловете в HDFS се съпоставят с екземпляра на DataXceiver от страна на сървъра – и действителните нишки, които представляват.
Дълбоко гмуркане на Hadoop
Споменатите по-горе DFSInputStream и DFSOutputStream са наистина фасади около обичайните концепции за поток. Те обгръщат комуникацията клиент-сървър в тези стандартни Java интерфейси, докато вътрешно насочват трафика към избран DataNode – който е този, който държи копие на текущия блок. Той има свободата да отваря и затваря тези връзки, ако е необходимо. Тъй като клиентът чете файл в HDFS, класовете на клиентската библиотека се превключват прозрачно от блок на блок и следователно от DataNode към DataNode, така че той трябва да отваря и затваря връзки, ако е необходимо.
DFSInputStream има екземпляр на клас DFSClient.BlockReader, който отваря връзката с DataNode. Екземплярът на потока извиква blockSeekTo() за всяко извикване на read(), което се грижи за отварянето на връзката, ако вече няма такава. След като блокът бъде напълно прочетен, връзката се затваря. Затварянето на потока има същия ефект, разбира се.
DFSOutputStream има подобен помощен клас, DataStreamer. Той проследява връзката със сървъра, която се инициира от метода nextBlockOutputStream(). Той има допълнителни вътрешни класове, които помагат при изписването на блоковите данни, които пропускаме тук за краткост.
И блоковете за запис и четене изискват нишка, която да държи гнездото и междинните данни от страна на сървъра, увити в екземпляра на DataXceiver. В зависимост от това какво прави вашият клиент, ще видите, че броят на връзките варира около броя на файловете, достъпни в момента в HDFS.
Обратно към гатанката на HBase по-горе:причината да не виждате до 22 (и повече) връзки по време на старта е, че докато регионите се отварят, единствените необходими данни са информационният блок на HFile. Този блок се чете, за да получи жизненоважни подробности за всеки файл, но след това отново се затваря. Това означава, че ресурсът от страна на сървъра се освобождава в бърза последователност. Останалите четири връзки са по-трудни за определяне. Можете да използвате JStack за изхвърляне на всички нишки в DataNode, което в този пример показва този запис:
„DataXceiver за клиент /127.0.0.1:64281 [изпращане на блок blk_5532741233443227208_4201]” 5 tib06 priemon nid=0x1178b4000 runnable [1178b3000]
java.lang.Thread.State:RUNNABLE
…
„DataXceiver за клиент /127.0.0.1:64172 [получаващ блок blk_-2005512129579433420_4199 client=4199.0.0.000_000000000000 ,60020,1330984111693_1330984118810]” daemon prio=5 tid=7fb966109000 nid=0x1169cb000 runnable [1169ca000]
java.lang.Th...read.lang.Th...
Това са единствените записи на DataXceiver (в този пример), така че броят в групата на нишките е малко подвеждащ. Припомнете си, че демонната нишка на DataXceiverServer вече отчита един допълнителен запис, който в комбинация с двата по-горе отчита трите активни връзки – което всъщност означава три активни нишки. Причината, поради която регистърът посочва четири вместо това, е, че регистрира броя от активна нишка, която е на път да завърши. И така, малко след като се регистрира броят от четири, то всъщност е едно по-малко, т.е. три и следователно съответства на нашия брой активни нишки.
Също така имайте предвид, че вътрешните помощни класове, като PacketResponder, заемат друга нишка в групата, докато са активни. Изходът на JStack показва този факт, изброявайки нишката като такава:
„PacketResponder 0 за Block blk_-2005512129579433420_4199“ демон prio=5 tid=7fb96038 Object=7fb96030 ince. () [116acd000]
java.lang.Thread.State:TIMED_WAITING (на монитор на обект)
на java.lang.Object.wait(Native Method)
на org.apache.hadoop. hdfs.server.datanode.BlockReceiver$PacketResponder \
.lastDataNodeRun(BlockReceiver.java:779)
– заключен (a org.apache.hadoop.hdfs.server.datanode.PacketReceiver$BlockReceiver
>на org.apache.hadoop.hdfs.server.datanode.BlockReceiver$PacketResponder.run(BlockReceiver.java:870)
на java.lang.Thread.run(Thread.java:680)
Тази нишка в момента е в TIMED_WAITING състояние и не се счита за активна. Ето защо броят, излъчван от регистрационните оператори на DataXceiver, не включва този вид нишки. Ако станат активни поради изпращане на данни от клиента, броят на активните нишки ще се увеличи отново. Друго нещо, което трябва да се отбележи, че тази нишка не се нуждае от отделна връзка или сокет между клиента и сървъра. PacketResponder е просто нишка от страната на сървъра за получаване на блокови данни и поточно предаване към следващия DataNode в конвейера за запис.
Командата Hadoop fsck също има опция да докладва кои файлове са отворени в момента за запис:
$ hadoop fsck /hbase -openforwrite
FSCK стартира от larsgeorge от /10.0.0.29 за път / hbase в понеделник, 05 март 22:59:47 CET 2012 г.
……/hbase/.logs/10.0.0.29,60020,1330984111693/10.0.0.29%3A60020.133094810 блок. …………………………………………..Състояние:HEALTHY
Общ размер: 2088783626 B
Общо директории: 54
Общо файлове: 45
…
Това не се отнася непосредствено до заета нишка от страна на сървъра, тъй като те се разпределят по ID на блок. Но можете да разберете от него, че има един отворен блок за писане. Командата Hadoop има допълнителни опции за отпечатване на действителните файлове и идентификатора на блока, от който се състоят:
$ hadoop fsck /hbase -files -blocks
FSCK стартира от larsgeorge от /10.0.0.29 за път /hbase в вторник, 06 март 10:39:50 CET 2012 г.
…
/hbase/.META./1028785192/.tmp
/hbase/.META./1028785192/info
/hbase/.META./1028785192/info/4027596949915293355 36517 байта, 1 блок(а): OK
0. blk_5532741233443227208_4201 len=36517 repl=1
…
Състояние:HEALTHY
Общ размер: 2088788703 B
Общо директории : 54
Общо файлове: 45 (В момента се записват файлове:1)
Общо блокове (проверени): 64 (ср. размер на блока 32637323 B) (Общо отворени файлови блокове (непотвърдени):1)
Минимално репликирани блокове: 64 (100,0 %)
…
Това ви дава две неща. Първо, в резюмето се посочва, че има един отворен файлов блок в момента на изпълнение на командата - съответстващ на броя, отчетен от опцията "-openforwrite" по-горе. Второ, списъкът с блокове до всеки файл ви позволява да съпоставите името на нишката с файла, който съдържа блока, до който се осъществява достъп. В този пример блокът с идентификатор “blk_5532741233443227208_4201” се изпраща от сървъра към клиента, тук RegionServer. Този блок принадлежи към HBase .META. таблица, както е показано от изхода на командата Hadoop fsck. Комбинацията от JStack и fsck може да послужи като заместител на лошия човек за lsof (инструмент в командния ред на Linux за „изброяване на отворени файлове“).
JStack също така съобщава, че има нишка DataXceiver, с придружаващ PacketResponder, за идентификатор на блок „blk_-2005512129579433420_4199“, но този идентификатор липсва в списъка с блокове, отчетени от fsck. Това е така, защото блокът все още не е завършен и следователно не е достъпен за читателите. С други думи, Hadoop fsck отчита само пълни (или синхронизирани[7][8], за версията на Hadoop, която поддържа тази функция) блокове.
Обратно към HBase
Отварянето на всички региони не изисква толкова ресурси на сървъра, колкото бихте очаквали. Ако сканирате цялата таблица на HBase обаче, принуждавате HBase да прочете всички блокове във всички HFiles:
HBase Shell:
hbase(main):003:0> сканиране 'usertable'
…
1000000 ред(ове) за 1460,3120 секунди
Регистър на DataNode:
2012-03-05 14:42:20,580 DEBUG org.apache.hadoop.hdfs.server.datanode. DataNode:Броят на активните връзки е:6
2012-03-05 14:43:23,293 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:7
2012 -03-05 14:43:23,299 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-1423642448-1423642448-10.02010, info=10.030000000000000000000000000 ipcPort=50020):Броят на активните връзки е:8
…
2012-03-05 14:49:24,332 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127 0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:11
02,4-205 DE .apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:10
2012-03-05 14:49:59,987 DEBUG org.apache.hadoop.hdfs.server.datanod e.DataNode:Броят на активните връзки е:11
2012-03-05 14:51:12,603 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1, storageID=50) DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50020):Броят на активните връзки е:12
2012-03-05 12,Gfs.hp.hp.hp. .server.datanode.DataNode:Броят на активните връзки е:11
2012-03-05 14:51:46,473 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:12
…
2012-03-05 14:56:59,420 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:15
2012-03-05 14:57:31,722 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:16
2012-03-05 14:58:24,909 DEBUG org.apache.hadoop.hdf. server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772, infoPort=50075, ipcPort=50mber0 на act0N):ive връзки е:17
2012-03-05 14:58:24,910 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:16
…
2012-03-05 15:04:17,688 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:21
2012-03-05 15:04:17,689 DEBUG .hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-1321352233772):Активна връзка Port:233772, infoPort е 02, infoPort е 02, infoPort е 0 br />2012-03-05 15:04:54,545 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:21
2012-03-05 15:05:55,901 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:DatanodeRegistration(127.0.0.1:50010, storageID=DS-1423642448-10.0.0.64-50010-13213522337):връзка е активна:info. :22
2012-03-05 15:05:55,901 DEBUG org.apache.hadoop.hdfs.server.datanode.DataNode:Броят на активните връзки е:21
Броят на активните връзки сега достига неуловимите 22. Имайте предвид, че този брой вече включва нишката на сървъра, така че все още не ни достига това, което бихме могли да считаме за теоретичния максимум – въз основа на броя на файловете, които HBase трябва да обработва.
Какво означава всичко това?
И така, колко „xcievers (sic)“ имате нужда? Като се има предвид, че използвате само HBase, можете просто да наблюдавате горния показател „storefiles“ (които получавате и чрез Ganglia или JMX) и да добавите няколко процента за междинни и предварителни регистрационни файлове. Това трябва да работи за системи в движение. Въпреки това, ако трябва да определите това число на неактивна, напълно уплътнена система и да приемете, че е максималното, може да откриете, че това число е твърде ниско, след като започнете да добавяте още файлове на магазина по време на редовни изтривания на memstore, т.е. веднага щом започнете да добавяне на данни към таблиците на HBase. Или ако използвате и MapReduce в същия клъстер, агрегиране на регистрационни файлове на Flume и т.н. Ще трябва да отчетете тези допълнителни файлове и, което е по-важно, да отворите блокове за четене и писане.
Отбележете отново, че примерите в тази публикация използват един DataNode, нещо, което няма да имате в реален клъстер. За тази цел ще трябва да разделите общия брой на съхраняваните файлове (според метриката на HBase) на броя на DataNodes, които имате. Ако имате, например, броят на файловете на магазина от 1000 и вашият клъстер има 10 DataNode, тогава трябва да сте добре с по подразбиране 256 xceiver нишки на DataNode.
Най-лошият случай би бил броят на всички активни читатели и писатели, тоест тези, които в момента изпращат или получават данни. Но тъй като това е трудно да се определи предварително, може да помислите за изграждане на приличен резерват. Освен това, тъй като процесът на писане се нуждае от допълнителна – макар и по-краткотрайна – нишка (за PacketResponder), трябва да вземете предвид и това. Така че една разумна, но доста опростена формула може да бъде:
Тази формула взема предвид, че имате нужда от около две нишки за активен писател и още една за активен читател. След това това се сумира и разделя на броя на DataNode, тъй като трябва да посочите „dfs.datanode.max.xcievers“ за DataNode.
Ако се върнете към екранната снимка на HBase RegionServer по-горе, ще видите, че има 22 файла в магазина. These are immutable and will only be read, or in other words occupy one thread only. For all memstores that are flushed to disk you need two threads – but only until they are fully written. The files are finalized and closed for good, cleaning up any thread in the process. So these come and go based on your flush frequency. Same goes for compactions, they will read N files and write them into a single new one, then finalize the new file. As for the write-ahead logs, these will occupy a thread once you have started to add data to any table. There is a log file per server, meaning that you can only have twice as many active threads for these files as you have RegionServers.
For a pure HBase setup (HBase plus its own HDFS, with no other user), we can estimate the number of needed DataXceiver’s with the following formula:
Since you will be hard pressed to determine the active number of store files, flushes, and so on, it might be better to estimate the theoretical maximum instead. This maximum value takes into account that you can only have a single flush and compaction active per region at any time. The maximum number of logs you can have active matches the number of RegionServers, leading us to this formula:
Obviously, the number of store files will increase over time, and the number of regions typically as well. Same for the numbers of servers, so keep in mind to adjust this number over time. In practice, you can add a buffer of, for example, 20%, as shown in the formula below – in an attempt to not force you to change the value too often.
On the other hand, if you keep the number of regions fixed per server[9], and rather split them manually, while adding new servers as you grow, you should be able to keep this configuration property stable for each server.
Final Advice &TL;DR
Here is the final formula you want to use:
It computes the maximum number of threads needed, based on your current HBase vitals (no. of store files, regions, and region servers). It also adds a fudge factor of 20% to give you room for growth. Keep an eye on the numbers on a regular basis and adjust the value as needed. You might want to use Nagios with appropriate checks to warn you when any of the vitals goes over a certain percentage of change.
Note:Please make sure you also adjust the number of file handles your process is allowed to use accordingly[10]. This affects the number of sockets you can use, and if that number is too low (default is often 1024), you will get connection issues first.
Finally, the engineering devil on one of your shoulders should already have started to snicker about how horribly non-Erlang-y this is, and how you should use an event driven approach, possibly using Akka with Scala[11] – if you want to stay within the JVM world. Bear in mind though that the clever developers in the community share the same thoughts and have already started to discuss various approaches[12][13].
Links:
- [1] http://old.nabble.com/Re%3A-xceiverCount-257-exceeds-the-limit-of-concurrent-xcievers-256-p20469958.html
- [2] http://ccgtech.blogspot.com/2010/02/hadoop-hdfs-deceived-by-xciever.html
- [3] https://issues.apache.org/jira/browse/HDFS-1861 “Rename dfs.datanode.max.xcievers and bump its default value”
- [4] https://issues.apache.org/jira/browse/HDFS-1866 “Document dfs.datanode.max.transfer.threads in hdfs-default.xml”
- [5] http://hbase.apache.org/book.html#dfs.datanode.max.xcievers
- [6] http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#threads_oom
- [7] https://issues.apache.org/jira/browse/HDFS-200 “In HDFS, sync() not yet guarantees data available to the new readers”
- [8] https://issues.apache.org/jira/browse/HDFS-265 “Revisit append”
- [9] http://search-hadoop.com/m/CBBoV3z24H1 “HBase, mail # user – region size/count per regionserver”
- [10] http://hbase.apache.org/book.html#ulimit “ulimit and nproc”
- [11] http://akka.io/ “Akka”
- [12] https://issues.apache.org/jira/browse/HDFS-223 “Asynchronous IO Handling in Hadoop and HDFS”
- [13] https://issues.apache.org/jira/browse/HDFS-918 “Use single Selector and small thread pool to replace many instances of BlockSender for reads”