sex hikayeescort şişliavcılar escortzeytinburnu escorttopkapı escortkurtköy escorteminönü escortcihangir escortcevizlibağ escortçengelköy escortbeyoğlu escortcebeci escortistanbul escortdudullu escortbomonti escortdragos escortbeykoz escortçapa escortatalar escortçağlayan escortbeyazıt escortistanbul escortcaddebostan escortbeşiktaş escortbüyükdere escortbebek escortchatbeşiktaş escortbayrampaşa escortkurtköy escortbaşakşehir escortbakırköy escortbalmumcu escortbahçeşehir escortanadoluhisarı escortbahçelievler escortbayrampaşa escortsohbetbahçeköy escortbahariye escortataşehir escortarnavutköy escortanal escortacıbadem escortaltınşehir escortkurtköy escortbodrum escortbodrum escortbodrum escortalibeyköy escortbakırköy escortpendik escortacıbadem escortadalar escortmecidiyeköy escortadapazari escortistanbul escortkemerburgaz escortedirnekapı escortataşehir escortpendik escortömerli escortkurtköy escortferiköy escortnişantaşı escortbakırköy escortbayrampaşa escortcaglayan escortalibeyköy escortmahmutbey escortşirinevler escortbeylikdüzü escortpriligy fiyatıpriligy 60 mg fiyatql 36cialis fiyatıcialis 100 mg eczane satış fiyatılifta 20 mg yorumsertleştirici hapcialis kullananlarcialis 20 mg kullanıcı yorumlarıviagra 100 mg fiyatpriligy fiyatıpriligydapoksetinpenis büyütücü kremegzama şampuanıgeciktirici hapviagra fiyatıcialis 100 mgcialis fiyatbolu escortvan escortşahinbey escortankara escortgebze escortkayseri escortmanisa escorttokat escortvan escortdiyarbakır escorthatay escortalanya escortnilüfer escortbursa escortzonguldak escortyozgat escortyalova escortvan escortuşak escorturfa escorttunceli escorttrabzon escorttokat escorttekirdağ escortşırnak escortsivas escortsinop escortsiirt escortsamsun escortsakarya escortrize escortosmaniye escortordu escortniğde escortnevşehir escortmuş escortmuğla escortmersin escortmardin escortmaraş escortmanisa escortmalatya escortkütahya escortkonya escortkocaeli escortkırşehir escortkırklareli escortkırıkkale escortkilis escortkayseri escortkastamonu escortkars escortkaraman escortkarabük escortizmir escortistanbul escortısparta escortığdır escorthatay escorthakkari escortgiresun escortgaziantep escorteskişehir escorterzurum escorterzincan escortelazığ escortedirne escortdüzce escortdiyarbakır escortdenizli escortçorum escortçankırı escortçanakkale escortbursa escortburdur escortbolu escortbitlis escortbingöl escortbilecik escortbayburt escortbatman escortbartın escortbalıkesir escortaydın escortartvin escortardahan escortantalya escortankara escortamasya escortağrı escortafyon escortadıyaman escortadana escortgümüşhane escortaksaray escortyüreğir escorttrabzon escorttekirdağ escortizmir escortselçuklu escortşehitkamil escortşanlıurfa escortsamsun escortsakarya escortrize escortosmangazi escortnilüfer escortmuğla escortmersin escortmarmaris escortmalatya escortkuşadası escortkonya escortkocasinan escortkeçiören escortkayseri escortizmit escorteskişehir escortelazığ escortdenizli escortçorlu escortçeşme escortçankaya escortbuca escortbodrum escortatakum escortgaziantep escortantalya escortantakya escortankara escortaksaray escortgebze escortalanya escortdenizli escortadıyaman escorterzurum escortçorum escortkütahya escortadıyaman escortaydın escortbalıkesir escortçanakkale escorterzurum escorthatay escortmanisa escortvan escortsivas escortsincan escortyalova escortuşak escortsivas escorturfa escortrize escortosmaniye escortordu escortnevşehir escortmuş escortmardin escortkonya escortkırşehir escortkilis escortkarabük escorthakkari escortgiresun escortantep escorteskişehir escortbursa escortbilecik escortbitlis escortbingöl escortbatman escortaydın escortağrı escortafyon escortadana escortantakya escortistanbul escortadalar escortsilivri escortanadolu yakası escortarnavutköy escortataşehir escortavcılar escortavrupa yakası escortbahçelievler escortbakırköy escortbaşakşehir escortbayramapaşa escortbeşiktaş escortbeykoz escortbeylikdüzü escortbeyoğlu escortbüyükçekmece escortçatalca escortesenler escorteyüpsultan escortesenyurt escortfatih escortkadıköy escortkağıthane escortkartal escortmaltepe escortpendik escortsarıyer escortsultanbeyli escortşile escortşişli escorttuzla escortümraniye escortüsküdar escortzeytinburnu escortbağcılar escortküçükçekmece escortbingöl escorteskişehir escortkırklareli escortçekmeköy escortsultangazi escortsancaktepe escortadana escortadıyaman escortafyon escortağrı escortaksaray escortamasra escortamasya escortankara escortantalya escortardahan escortaydın escortbartın escortbatman escortbayburt escortbilecik escortbitlis escortbolu escortburdur escortçanakkale escortçankırı escortçorum escortdenizli escortdiyarbakır escortdüzce escortedirne escortelazığ escorterzincan escorterzurum escortgaziantep escortgiresun escortgümüşhane escorthakkari escorthatay escortığdır escortısparta escortistanbul escortizmir escortmaraş escortkarabük escortkaraman escortkars escortkastamonu escortkayseri escortkırıkkale escortkilis escortkocaeli escortkonya escortkurucaşile escortkütahya escortmalatya escortmanisa escortmardin escortmersin escortmuğla escortnevşehir escortniğde escortordu escortosmaniye escortrize escortsamsun escortsiirt escortsinop escortsivas escortşırnak escorttekirdağ escorttokat escorttrabzon escorttunceli escortulus escorturfa escortuşak escortvan escortyalova escortyozgat escortzonguldak escortmecidiyeköy escortşişli bayan escortşişli escort bayanşişli escortümraniye escortdefne samyeli kimdirashley ortizdefne samyelieskişehir apartman temizliğigoogle analyticsKim KardashianGoogle Anahtar KelimeVajina kaşıntısıVajinada Kaşıntımoz rank sorgulada pa sorgulamamoz rankda pa sorgulatanıtım yazısıserel yereli kimdirtwerkgoogle search consolesite speed testserver scanRegl dönemiinstagram takipçi hilesipubg hile alkalça büyültme esteteğiseoseo nedirpussy tattooindoxploit shellc99 shellphp shellr57 shelllogsuz shell indirwso shellc99 shelltuzluca escortegil escortergani escorthani escorthazro escortkayapinar escortkocakoy escortkulp escortlice escortsilvan escortsur escortyenisehir escortakcaova escortcilimli escortcumayeri escortgolkaya escortgumusova escortkaynasli escortyigilca escortpriligy 30 mg fiyatenez escorthavsa escortipsala escortkesan escortlalapasa escortmeric escortsuluoglu escortuzunkopru escorthozan escortguney escortkale escortmerkezefendi escortpamukkale escortsaraykoy escortsaraykoy escortserinhisar escortazdırıcı kadın viagrasıtavas escortbaglar escortbismil escortilgaz escortkizilirmak escortkorgun escortorta escortsabanozu escortgoogle sıra bulucuyaprakli escortalaca escortbayat escorterotik fotograflarbogazkale escorterotik resimlersexs resimleridodurga escortiskilip escortkargi escortlacin escortmecitozu escortoguzlar escortortakoy escortosmancik escortsungurlu escortosmancik escortugurludag escortacipayam escortbabadag escortmarlboro double fusionbaklan escortbekilli escortbeyagac escortbozkurt escortbuldan escortcal escortcameli escortcardak escortcivril escortkaramanli escorttefenni escortyesilova escortbuyukorhan escortgemlik escortgursu escortharmancik escortinegol escortiznik escortkaracabey escortkeles escortkestel escortmudanya escortorhaneli escortmustafakemalpasa escortnilufer escortorhaneli escortorhagazi escortosmangazi escortyenisehir escortyildirim escortayvacik escortbayramic escortbiga escortbozcaada escortcan escorteceabat escortezine escortgelibolu escortgokceada escortlapseki escortyenice escortatcakaracalar escortbayramoren escortcerkes escorterdivan escortsogut escortadakli escortgenc escortkarliova escortkigi escortsolhan escortyayladere escortyedisu escortadilcevaz escortahlat escortlifta 5 mg 28 tablet fiyategzama şampuanıtuzsuz şampuancaptain black sigaraburun estetiğiseo araclarıbeylikdüzü escortguroymak escorthizan escortmutki escortbursa escorttatvan escortmecidiyeköy escortdortdivan escortgerede escortgoynuk escortkibriscik escortmengen escortmağaza elektrik işleriseo nasıl yapılırofis elektrik işleriofis mekanik tesisat işlerimağaza mekanik tesisat işleridedektifmudurnu escortyenicaga escortereksiyon hapları ve ilaçlarıaglasun escorterenkoy escortaltinyayla escortavcılar escortbucak escorthalkali escortescort lazımcavdir escortesenyurt escortceltikci escortgolhisar escortkepez escortkorkuteli escortkumluca escortmanavgat escortmuratpasa escorttserik escortcildir escortdamal escortgole escorthanak escortposof escortardanuc escortarhavi escortborcka escorthopa escortkemalpasa escortmurgul escortsavsat escortyusufeli escortbozdogan escortbuharkent escortgermencik escortefeler erscortefeler erscortefeler erscortincirliova escortkaracasu escortkarpuzlu escortkocarli escortkosk escortkusadasi escortaksaray escorttaksim escortkuyucak escortsultanhisar esortyenipazar escortnazilli escortsoke escortaltieylul escortayvalik escortbalya escortbandirma escortbigadic escortbigadic escortdursunbey escortedremit escorterdek escortgomec escortgonen escorthavran escortivrindi escortkaresi escortkapsut escortmanyas escortmarmara escortsavastepe escortsindirgi escortsusurluk escortamasre escortkurucasile escortulus escortbesiri escortgercus escorthasankeyf escortkozluk escortsason escortaydintepe escortdemirozu escortbozuyuk escortgolpazari escortinhisar escortosmaneli escortpazaryeri escortsoogut escortkozlu escortdevrek escortcaycuma escortyerkoy escortsorgun escortsarikaya escortciflikkoy escortakdagmadeni escortiibradi escortkas escortkemer escortfinike escortfinike escortgazipasa escortpursaklar escortsereflikochisar escortelmali escortalanya escortsincan escortyenimahalle escortdemre escortaksu escortakseki escortpolatli escortnallihan escortelmadag escortevren escortguldul escortnallihan escoortmamak escortkizilcahamam escortkecioren escortgumushacikoy escorthaymana escortkahramankazan escortetimeskut escortevren escortkalecik escortbeypazari escortcamlidere escortcankaya escortcubuk escortbala escortbala escortayas escortaltindag escortakyurt escorttasova escortgoynucek escorthamamozu escortmerzifon escortsuluova escorttasova escortdogubayazit escorteleskirt escorthamur escortpatnos escorttaslicay escortşişli escortkıbrıs escortedremit escortmarlboro shufflekurtkoy escorttaslicay escorttutak escortagacoren escorteskil escortAnadolu yakası escortümraniye escortkadıköy escortPorno izlePornoAtaşehir escortBostancı escortAnadolu yakası escortAnkara rus escortgulagac escortguzelyurt escortgoynucek escortdiyadin escortsuhut escortiscehisar escortsultanhani escortsariyahsi escortortakoy escortviransehir escortsultandagi escortbaykan escortsinanpasa escortkiziloren escortihsaniye escorthocalar escortevciiler escorttut escortbasmakci escortbasmakci escortbayat escortbolvadin escortcay escortcobanlar escortSimge Sağın kimdirdinar escortemirdag escortbogazliyan escortarapli escortevciler escortsincik escortsamsat escortkahta escortedremit escortkaraisali escortipekyolu escortaltinova escortkaratas escortarmutlu escortkozan escortozalp escortpozanti escorttusba escortercis escortsaimbeyli escortsaricam escorttufanbeyli escortyumurtalik escortbesni escortgerger escortgolbasi escortcelikhan escortyuregir escortimamoglu escortfeke escortcukurova escortceyhan escortaladag escortatabey escortegirdir escortgelendost escortcialis eczane satışkeciborlu escortsarkiraagac escortsenirkent escortsutculer escortuluborlu escortcialis siparişyapay kızlık zarıyalvac escortviagra satın aleczanede satılan cinsel ilaçlareczane viagra satışgeç boşaltan haplaryenisarbademli escortorjinal viagra satışaliaga escortbayan azdırıcıbalcova escortvega 100 mg hapbayindir escortbayan azdırıcıSelin Ciğerci kimdirseo analizbayrakli escortbeydag escortbergama escortbornova escortbuca escortGetir Çalışma Saatleriinstagram Keşfete Çıkmacesme escortcigli escortdikili escortfoca escortgaziemir escortguzelbahce escortkarabaglar escortkaraburun escortDemi RoseGoogle Adwords ile SEOkarsiyaka escortkinik escortkiraz escortWanda Icardikonak escortZararlı Bağlantıları ReddetmeKilo Vermenin Püf NoktalarıYaşlanmayı Geciktiren Besinlermenderes escortücretsiz wordpress temalarıinstagram fotoğraflarını toplu olarak bilgisayara indirmeücretsiz backlink almamenemen escortkadınlarda orgazmın püf noktalarıtüp bebek fiyatlarıBurçin Terzioğlu kimdirnarlidere escortAdsense Nedirodemis escortseferihisar escortselcuk escorttire escorttorbali escorturla escortalexa sorgulamaspam skoru sorgulamaspam skoru öğrenistanbul escortalexa sorguwho isbacklink sorgulamagoogleafsin escortandirin escortcaglayancerit escortdulkadirli escortekinokuzu escortelbistan escortnuehak escortonikisubat escortgoksun escortpazarcik escortturkoglu escorteflani escortovacik escortsafranbolu escorteskipazar escortsusuz escortayranci escortabana escortbasyayla escortarac escortazdavay escortermenek escortkazimkarebekir escortcatalzeytin escortsariveliler escortcide escortdaday escortakyaka escortarpacay escortdevrekani escortdigor escortkagizman escortsarikamis escortselim escortdoganyurt escortinebolu escorttosya escorttaskopru escortsenpazar escortseydiler escorthasonu escortakkisla escortbunyan escortishangazi escortdeveli escortkure escortsertleştirici haplarbayan azdırıcıgeciktiricilerkadın viagrasıgeciktirici krembayan azdırıcı damla isimleripembe kadın viagrasıfelahiye escorthacilar escortpinarbasi escortincesu escortkocasinan escortmelikgazi escortozvatan escortsarioglan escortsariz escorttalas escorttomarza escortyahyali escortescobar vipeybeyli escortmusabeyli escortyesilhisar escortpolateli escortbahsili escortcelebi escortdelice escortkeskin escortsulakyurt escortkarakecili escortyahsihan escortakpinar escortkazman escortmucur escortbasiskale escortcicekdagi escortcayirova escortboztepe escortyozgat escortgiresun escortip whoisdomain analizhacklink satışdomain whoistoplu whoisip sorgulaip sorgulamasunucu taramasite hız testihtml kod şifrelemeseoseo analizseo nedirseo newsadana escortadana eskorthoşgeldin bonusuhoşgeldin bonusu veren bahis sitelerikayseri escorteryaman escortbedava bonusdeneme bonususincan escortkayseri escortgaziantep escortsohbet numaralarıbedava bonus veren bahis sitelerideneme bonusu veren bahis sitelerisohbet numaralarıgaziantep escortmersin escortinstagram takipci hilesismm panelsmm panelinstagram free likessmm panelinstagram beğeni hilesiinstagram free followersinstagram takipci hilesiinstagram takipci hilesiinstagram unfollow freeinstagram Followersinstagram takipci satın alinstagram free followersinstagram takipci hilesikorsan taksikabatas escortkocaeli escortgebze escortyahya kaptan escortescort bayanseo analizphp encodeseo analysisdarinca escortgebze escorthendek escortbartin escortkirikkale escortderince escortbacklink nedirSearch Engine Optimizationdilovasi escorttokat escortadiyaman escortgolcuk escortizmit escortkandira escortsincan escorteryaman escortkızılay escortescort bayanegzama için şampuansaç egzaması için şampuanseboreik dermatit şampuanıegzama şampuanısaç uzatıcısaç vitaminibiotinsakarya escortbursa escorttuzla escortseo sorgulamaseo sorguladövme modellerivagina tattookorfez escortkartepe escortsilopi escortkaramursel escortakoren escortaksehir escortbeysehir escortbozkir escortceltik escortcumra escortgaziantep escortderbent escortderebucak escortdoganhisar escortemirgazi escorteregli escortcihanbeyli escortguneysinir escortsakarya escorthadim escortsuruc escorthalkapinar escorthuyuk escortkadinhani escortilgin escortkarapinar escortkaratay escortkulu escortsarayonu escortselcuklu escortseydisehir escorttaskent escortmeram escortaltintas escorttaskent escortemet escortgeyve escorttuzla escort bayananal yapan escortucuz escorteve gelen escortgebze escortsinirsız escort bayangölcük escortizmit escortdarıca escortgeciktirici hap eczanekocaeli escortescort bayanotele gelen escortizmir escortgörükle escortantalya escortbornova escortyunak escortetlik escortçankaya escortaslanapa escortdomanic escortbursa escort bayangediz escortmecidiyeköy escorthisarcik escorttepeören escortbursa eskortbursa escort kızyazihan escortescort bursaorhanlı escortaydıntepe escortsimav escortputurge escortsite analizhekimhan escortkuluncak escortpriligy fiyatısağlık bakanlığı onaylı eczanelerde satılan geciktiricigeciktirici hap eczane fiyatıcialis 100 mg eczane satış fiyatıviagra kullananların yorumlarıorganik hityanık yarasına ne iyi gelirElif Domaniç kimdirRobots.txt dosyası oluşturmabotoks nasıl uygulanırbotoks markalarıbotoks eskişehirhamilelikte süt yapan yiyeceklerbotoks fiyatlarısexs videohamilelikte mide yanması neden olurhamilelikte süt yapan meyvelerhamilelikte mide yanmasıgögüs büyütme yöntemlerigögüs büyütme estetiğihamilelikte mide yanmasına ne iyi gelirMeme DikleştirmeMeme Dikleştirme estetiğipopo kaldırmapopo kaldırma estetiğidoganyol escorterdemli escortworld newscialis 20 mg kullanıcı yorumlarınilüfer escortbursa merkez escortgörükle escortbursa escort bayanaltıparmak escortcialis 100 mg fiyatıev yemekleri tarifigoogle haberYozgat escortataşehir escortkartal escortbostancı escortkadıköy escortkurtköy escortpendik escortüsküdar escortümraniye escortbostancı escortgöztepe escortataşehir escortbostanci escorttuzla escortbostancı escortkartal escortbostancı escortataşehir escortkadıköy escortümraniye escortümraniye escortataşehir escortanadolu yakası escortümraniye escortataşehir escortmaltepe escortkadıköy escortkadıköy escortkurtköy escortşişli escort bayanvan escortzonguldak escortsinop escortyozgat escortsanliurfa escorttrabzon escortaydin escortcorum escorterzurum escortgiresun escortmaras escortEskişehir Web Tasarımkutahya escortSelülitten kurtulmanın yollarıSelülitten kurtulmakurtköy escortMarlboro Touch GrayParliament Night Bluekazlicesme escortistanbul escortkanlica escortbursa sinirsiz escortgebze escortdikilitaş escortcubuklu escortçamlıca escortistanbul evde masajciftehavuzlar escortpendik escorttaksim escortcasinoOlmeca Tekilapendik escortkartal escortcaptain blackEscort Bodrumbodrum escort bayanbodrum eskortbursa escort bayanbursa escort bayangörükle escortbursa sınırsız escortgemlik escortbursa merkez escortkartal escortkartal escortpendik escortkurtkoy escorttuzla escortkent d range bluekaynarca escortbostancı escortkocaeli anal yapan escortizmit sınırsız escortizmit otele gelen escortgölcük escortizmit kendi evi olan escortpendik escortbursa escortbodrum escortbursa merkez escortcialis siparişcialis 20 mgcialisMetin2 pvpcinsel hastalıklarviagra siparişlifta 20 mgAnahtar Kelime Analizikocaeli esgortcialis fiyatcialis 5 mg fiyatcialis 5 mgviagra 100 mg fiyatcialis jelviagra satın alviagra fiyatıdegravigrandedegra 100 mgorcafilcialis nedircialis 20 mg eczane fiyatıcialis fiyatcialis 20 mgcialis 100 mgviagra fiyatliftalifta 5 mgcialis siparişgeciktiricigeciktirici mendildelay spreystag spreynely8viga spreyviga kremgeciktirici spreygeciktirici kremnovagraviagra 100 mg fiyatorijinal viagraviagra 100 mgeczane viagraviagra siparişviagra satın alviagra nedirviagra yorumviagra fiyatviagra fiyatı 2021cialiscialis fiyatcialis 20 mgcialis 100 mgcialis nedircialis 5 mg fiyatıcialis hapcialis 5 mgcialis 20 mg eczane fiyatıcialis 100 mg fiyatcialis satışjeligravigaroocombo 100 mgvigrande 100 mgvigrandesinegrasildegrasildegra 100 mgdegra 100 mgdegra fiyatlifta 20 mgflynta 20 mglifta 5 mghardcisflynta 5 mgorcafil 5 mgEreksiyon Haplarıkamagra nedirsüper kamagrakamagra 100mg fiyatkamagra 100mgcialis eczanekamagra fiyatıkamagra jel fiyatıkamagra fiyatkamagra siparişizmit escortkamagra jel siparişkamagra jelkamagrakamagra satın alonline eczane viagrapfizer viagra satışviagra satın alviagra eczaneviagra fiyatviagra fiyatlarıcialis fiyatıcialis fiyatlarıcialis satışcialis eczanecialis 5 mgcialis 20cialis hapcialis 100 mgcialis 20 mgviagra eczaneonline eczane viagraviagra satışviagra fiyatlarıcialis 100 mg fiyatizmit eve gelen escortcialis 5 mg fiyatıataşehir escortümraniye escortescort kocaeliAnkara Araç KiralamaBursa Araç KiralamaBursa Araç Kiralamaİstanbul Araç KiralamaEskişehir Araç Kiralamaeskişehir rent a cartitan gelistanbul escortbusa escortyapay kızlık zarıhtml kod şifrelemebacklink alsunucu taramawordpress ücretsiz temaizmit escort bayankocaeli escortkocaeli sınırsız escorthacklink satışbacklink saleshtml code encryptionwordpress themeswordpress free temaWordpress Free Themesseo analysisbodrum escortfree shop sigaramugla escortistanbul escortantalya escortmarmaris escortserdivan escortescort bayanzonguldak escortyozgat escortyalova escortvan escortadana escortuşak escortadıyaman escorttunceli escorttrabzon escorttokat escorttekirdağ escortsivas escortafyon escortşırnak escortsinop escortağrı escortsiirt escortsakarya escortsamsun escorturfa escortrize escortaksaray escortamasya escortankara escortosmaniye escortordu escortardahan escortniğde escortmuş escortnevşehir escortantalya escortmuğla escortmersin escortmardin escortartvin escortaydın escortmaraş escortbalıkesir escortbartın escortizmir escortbatman escortmanisa escortbayburt escortmalatya escortkütahya escortkarabük escortkaraman escortkars escortkastamonu escortkayseri escortbilecik escortkonya escortkocaeli escortkırşehir escortkilis escortkırklareli escortkırıkkale escortbingöl escortistanbul escortbitlis escortbolu escortburdur escorthakkari escortisparta escortbursa escortçanakkale escortçankırı escortçorum escortdenizli escortedirne escortelazığ escortığdır escorthatay escorterzincan escorterzurum escortgümüşhane escortgiresun escorteskişehir escortgaziantep escortpasta tarifiyemek tarifleriyemek tarifi altatlı tarifiyemek tarifihacklink satışhacklink panelsite analizsunucu taraescort bayansakarya escortsakarya escort bayanadapazarı escortşişli escortmozrankPablo EscobarAdsense onayı nasıl alınırmarmaris escortdüzce escortdiyarbakır escortcratosslotbacklink satışhacklinkhacklink alhacklink nedirhacklink panelbuy backlinksbacklinkDavidoff Sigaradjarum blackmarlboro sigaraMarlboro Touch Mix Slender SigaraMarlboro Edge Slimmarlboro shuffleMarlboro Touch BlueMarlboro Double Fusion Summer KarpuzluMarlboro Winter ShuffleMarlboro Touch AquaMarlboro Double FusionMarlboro GoldMarlboro Vista Blossom Fusion SigaraMarlboro Edge Sunset Superslim Sigara Kavun ve MentolMarlboro Silver Blue Sigarablack devil sigaraCavallo SigaraOscar Nano Blueberry Yaban Mersinli SigaraMarlboro Luna Shuffle SigaraMarlboro White SigaraMarlboro White Fresh mentolluHEETS Amber SelectionCavallo Sync MentollüCavallo Strawberry Superslim Sigarawinston slender blueWinston Dark BlueWinston Expand Duo SigaraWinston XS Blue superslimCavallo Clip Go Sigara Yabanmersini AromalıCavallo by Vasily Vinteroff Mentollü SigaraDunhill SigaraSobranie Cocktail Renkli Sigarawinston slenderWinston SigaraCavallo Apple Superslim SigaraCavallo By Gino Ferrara SigaraColts SigaraCavallo Gummint Superslim SigaraEsse SigaraHarvest sigaraparliament sigarasobranie sigara520 kalpli sigaraKent Sigaramarvel sigarasenator sigaraLucky StrikeSobranie Black Russian Sigara520 kalpli sigaraSobranie Sigaralark sigaralm sigaracamel sigaramore sigaraome sigaraviceroy sigaramuratti sigaramore sigaraal capone sigararothmans sigaraheets sigarapall mall sigaraVogue Frisson sigaraMilano SigaraMilano EjectMilano 20 Signatures Red Grape Sigara Metal KutuOscar Silver Sigaraoscar sigaraMilano Coffee SigaraMilano Strawberry SigaraMilano Apple SigaraMilano Gum Sigara Sakız AromalıMilano 20 Signatures White Sigara Metal KutuMilano Gum Mint SigaraMilano 20 Signatures Black Sigara Metal KutuMilano 20 Signatures Blue Sigara Metal KutuMilano Tech Lock Süperslim SigaraRockets sigaraoris sigaraOscar Fanpack Chocolate Flavour Sigaraeve sigaraNeo sigarabond sigaramond sigarakeno club sigaraPhilip Morris sigaraIQOS Terea RussetUnited SigaraIQOS Terea WillowIQOS Terea Mauve WaveChapman Classic Natural Aromasız SigaraChapman Vanilla Tatlı Vanilya Aromalı SigaraChapman SigaraChapman Cherry Tatlı Kiraz Aromalı SigaraChapman Coffee Tatlı Kahve Aromalı SigaraAmerican Spirit Sigaraşişli bayan escortgorukle escortzorunlu trafik sigortasısekabet girişholiganbetholiganbetBetvoleKralbetMegabahisSekabetYouwinVdcasinoNakitBahisSuperbahisPiabetSuperbetinPiabellacasinoPiabellacasinoPiabellacasino GirişPiabellacasino Twitter
目录

Erlang教程

面向消息、面向并发的语言Erlang!!!

1. 安装与执行

# 去下面这个网址下载最新的安装包 各大系统都有
https://www.erlang-solutions.com/resources/download.html
$ erlc -d -W hello.erl                                                         # 编译成 *.beam 文件
$ erl -noshell -pa dir -s module fun                     # 在指定目录下,执行指定模块的函数
$ erl -noshell -s hello start -s init stop  # 在当前目录,先执行 shell:start() 再执行 init:stop()
Hello world

1.1 erlang shell

$ erl     # 进入 erl shell
> q(). # 退出 erl shell
> b(). # 打印当前变量绑定
> f(). # 删除所有当前变量绑定, f(X) 删除指定变量绑定
> regs(). # 打印所有绑定了 PID 的原子
> cd("目录"). # 进入目录, erlang shell 可以查找到,当前目录下的 *.beam 里的函数,直接调用它们
> c("xxx.erl", [debug_info]). # 编译
> help(). # 展示 erl shell 中所有可以调用的命令
> xxmodule:module_info().  # 打印该模块所有信息,module_info("xxx") 打印指定属性

1.2 escript

#!/usr/bin/env escript
%% 将参数转换成整数类型后调用阶乘函数, A 是命令行参数
%% ./fac.sh 4
%% factorial 4 = 24
main([A]) ->
    I = list_to_integer(A),
    F = fac(I),
    io:format("factorial ~w = ~w~n", [I, F]),
    init:stop().

fac(0) ->1;
fac(N) ->N*fac(N-1).

2. 变量与数据类型

2.1 变量

大写字母开头的单词是变量 (Name X Age),在命令式的语言(C Go JAVA)里,变量名其实是伪装起来的内存地址,X = 12 表达的将该内存地址处的值修改为 12.

而在 Erlang 中,X = 12 表示 X 本身就是 12,两者绑定在一起了,不可更改。

1> Name.                            % unbond variable
* 1: variable 'Name' is unbound
1> Name = "Link".                   % bind "Link" to Name
"Link"
2> Name = "codekissyoung".          % Name can not changed once bind to one Value
** exception error: no match of right hand side value "codekissyoung"

2.2 整数

格式 : Base#Value
-234      % 十进制 -234
2#1010    % 二进制表示的  10
-16#EA    % 十六进制表示的 -234
$a $A $\n % ASCII 字符表示的整数 97 65 10

2.2.1 算术运算符

+ - * / rem 求余 div 整除

2.2.2 关系运算符

== 值相等 /= 值不相等

=:= 值与类型都相等 =/= 值与类型都不相等

< 小于 <= 小于等于 > 大于 >= 大于等于

2.2.3 逻辑运算

andalso 等价于 &&
orelse 等价 ||
xor 异或
not

2.2.4 位运算符

band binary and
bor binary or
bxor binary xor
bnot binary not 按位否定运算符

2.3 原子 Atom

而小写字母开头(name friend)后可接 字母 数字 @ . _ 作为原子值

所谓原子值就是没有具体含义,只需要知道这个值,而不关心它具体是啥。类似于 C 语言中的#define age 10中的age。最大作用在于标识 匹配 和 比较.

2> name.                            % atom variable
name

2.3.2 true false ok

两个保留的原子值 true false 被当作布尔值使用

4> {1,2} < {1,3}.
true
5> {1,2} == {2,3}.
false
6> {1,2} /= {2, 3}.
true
7> 

2.4 位串 Bit

4> <<5,10,20>>. % <<5,10,20>>
5> <<65,66,67>>.
<<"ABC">>
6> <<"hello">>. 
<<"hello">>

% list_to_binary(L) -> B
7> Bin1 = <<1,2,3>>.
8> Bin2 = <<4,5>>.
9> Bin3 = <<6>>.
10> list_to_binary([ Bin1, 1, [2, 3, Bin2], 4 | Bin3 ]).
<<1,2,3,1,2,3,4,5,4,6>>

% term_to_binary(Term) -> Bin  把任何数据类型转换成一个二进制类型
% binary_to_term(Bin) -> Term
11> B = term_to_binary( {binaries, "are", useful } ).
<<131,104,3,100,0,8,98,105,110,97,114,105,101,11 ... >>
12> X = binary_to_term(B).
{binaries,"are",useful}

2.4.1 位语法

12> Pixels = <<213,35,23,64,78,12,34,12,234>>.
14> <<Pix1:24,Pix2:24,Pix:24>> = Pixels. % 数字表示匹配的 bit 数
17> <<R:8,G:8,B:8>> = <<Pix1:24>>.
18> R.
213

21> <<R:8, Rest/binary>> = Pixels.  % /binary 匹配剩下的所有 bit
23> Rest.
<<35,23,64,78,12,34,12,234>>

24> << <<X>> || <<X>> <= <<1,2,3,4,5>> , X rem 2 == 0 >>. % 二进制推导式
<<2,4>>

% 二进制 到 列表
26> RGB = [ {R, G, B} || << R:8, G:8, B:8>> <= Pixels ].
[{213,35,23},{64,78,12},{34,12,234}]

% 列表 到 二进制
27> << <<R:8, G:8, B:8>> || {R,G,B} <- RGB >>.
<<213,35,23,64,78,12,34,12,234>>

2.5 元组 tuple

元组用来组合多个值。并且提供了一种相等匹配模式,用来非常方便的取出元组里的值。

14> P = {10, 45}.                                          % P表示一个点
16> P.                                                     % 展示 P
{10,45}
15> Person = {person, {name, "codekissyoung"}, {age, 24}}. % Person 表示一个人
17> Person.                                                % 展示 Person
{person,{name,"codekissyoung"},{age,24}}
18> PersonLocation = {Person, P}.                          % 组合 Person 与 P 成为 PersonLocation
{{person,{name,"codekissyoung"},{age,24}},{10,45}}
1> tuple_size({abc, {def, 123}, ghi}). 
3
2> element(2, {abc, {def, 123}, ghi}). % 索引从 1 开始
{def,123}

如何从元组定位取值呢? 通过匹配

32> PersonLocation.                                 % 先查看下 PersonLocation 的值
{{person,{name,"codekissyoung"},{age,24}},{10,45}}
33> {{_,{_,Name},_},_} = PersonLocation.            % 写匹配等式
{{person,{name,"codekissyoung"},{age,24}},{10,45}}
34> Name.
"codekissyoung"

_是占位符,用来接收不需要的值。我们再来复述一遍相等匹配模式:假如要使=两边的东西相等,那么Erlang要做啥操作呢?答案是:让 Name 的实际内容存储为codekissyoung

2.6 列表 list

2.6.1 基础

列表用来容纳多个值,并且提供了一种相等匹配模式[H|T]H表示列表的第一个,T表示剩下的),用来非常方便的存入和取出操作。

22> ThingsToBuy = [{apple, 10}, {pear, 6}, {milk, 3}].    % 声明列表
42> [Buy|ThingsLeft] = ThingsToBuy.                       % 通过 [H|T] 模式匹配取数据
[{apple,10},{pear,6},{milk,3}]
43> Buy.
{apple,10}
44> ThingsLeft.
[{pear,6},{milk,3}]
45> ThingsToBuy.
[{apple,10},{pear,6},{milk,3}]
27> ThingsToBuy2 = [{orange,4}|ThingsToBuy].              % 通过 [H|T] 模式往列表存数据
[{orange,4},{apple,10},{pear,6},{milk,3}]

2.6.2 列表推导

list comprehension

[ Expression || Generator ... , filter ... ] % || 左边是想要的结果,|| 右边的表达式执行顺序是 从左到右
4> [ 2 * X || X <- L ]. %  X <- L 是生成器表达式
[2,4,6,8,10]

% 使用列表推导表达式改写的 total 版本
total( L ) ->
    sum( [shop:cost(A) * B || {A, B} <- L] ).

5> Buy = [{oranges,4},{newspaper,1},{apples,10},{pears,5}].
[{oranges,4},{newspaper,1},{apples,10},{pears,5}]

6> [{Name, 2 * Number} || {Name, Number} <- Buy].
[{oranges,8},{newspaper,2},{apples,20},{pears,10}]

10> L3 = [ X * X || X <- [1,2,3,4,5,6,7], X > 3 ].     % 生成器, 过滤器
[16,25,36,49]

% 生成表达式也起到了过滤器作用
7> [X || {a, X} <- [{a,1}, {b,2}, {c,3}, {a, 4}, hello, "link"] ].
[1,4]

% 多个Generator, 注意是全排列
11> [X + Y || X <- [1,2], Y <- [3,4]].
[4,5,5,6]

再来介绍一种连接列表的语法++

12> [1,2,3] ++ [7,9] ++ [23,45].
[1,2,3,7,9,23,45]

使用列表生成器和++语法写成的快速排序:

% 快速排序 生成表达式 + 过滤表达式
qsort([]) -> [];
qsort([Piv | T]) ->
  qsort([ X || X <-T, X < Piv])
  ++ [Piv] ++
  qsort([ X || X <-T, X >= Piv]).
2> shop:qsort([8,7,5,2,1,45,23,45,90]).
[1,2,5,7,8,23,45,45,90]

勾股定理,算出N以内符合勾股定理的数:

pythag(N) ->
    [ {A,B,C} ||
        A <- lists:seq(1,N),
        B <- lists:seq(1,N),
        C <- lists:seq(1,N),
        A + B + C =< N,
        A * A + B * B =:= C * C ].
9> shop:pythag(16).
[{3,4,5},{4,3,5}]
10> shop:pythag(30).
[{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{12,5,13}]

所有字母的排列组合:

perms( [] ) -> [[]];
perms( L )  -> [ [H|T] || H <- L, T <- perms( L -- [H]) ].
5> shop:perms("cats").
["cats","cast","ctas","ctsa","csat","csta","acts","acst",
 "atcs","atsc","asct","astc","tcas","tcsa","tacs","tasc",
 "tsca","tsac","scat","scta","sact","satc","stca","stac"]

2.6.3 字符串 string

字符串是使用双引号括起来的一串字符, 本质是一个整数列表。

48> [83,117,114,112,114,105,115,101].
"Surprise"
51> "中国汉语".
[20013,22269,27721,35821]

2.7 记录 Record

% records.hrl
-record(todo, {status=reminder,who=joe,text}). % 定义 名为 todo 的 record 类型

% 在函数 records 的模式匹配
clear_status(#todo{status = S, who = W} = R) ->
  R#todo{text="Finished",status=finished}.

% 匹配某个类型的 records
do_something(X) when is_record(X, todo) ->
  X#todo{status = finished}.
3> rr("records.hrl").  % 载入头文件

4> #todo{}. % 实例化一个 todo
#todo{status = reminder,who = joe,text = undefined}

5> X = #todo{status=urgent, text="Fix errata in book"}. % 实例化一个 todo 绑定到 X
#todo{status = urgent,who = joe,text = "Fix errata in book"}

7> X2 = X#todo{status=done, who=joe, text="gone with Wind"}. % 修改 X 后,绑定到 X2
#todo{status = done,who = joe,text = "gone with Wind"}

8> #todo{who=W, text=Txt} = X2. % 模式匹配
9> W. % joe
10> Txt. % "gone with Wind"

11> X2#todo.text. % 直接访问 record 里字段的值
"gone with Wind"
12> X2#todo.who. 

2.8 映射 Map

#{Key1 Op Val1, Key2 Op Val2 ... }
% Op 可以是 => 新增 和 := 修改 
1> F1 = #{ a => 1, b => 2 }. # 创建一个 Map 绑定到 F1
#{a => 1,b => 2}

2> Facts = #{ {wife,fred} => "Sue", {age, fred} => 45 }. % 键值可以是任何 Erlang 类型
#{{age,fred} => 45,{wife,fred} => "Sue"}

3> F2 = F1#{ c => xx }. # 新增键后,绑定到 F2
#{a => 1,b => 2,c => xx}

4> F3 = F2#{ c := ok }. # 修改键后,绑定到 F3
#{a => 1,b => 2,c => ok}

操作map的内置函数

与json的互转

3. 模块与函数

3.1 基本概念

引用透明性 : referential transparency 对于同样的参数,函数永远要返回同样的值.

% 函数格式: Body 必须是一个或者多个用 , 分割的 Erlang 表达式, 最后一个表达式的值作为返回值
Name(Args) -> Body.

函数可以顺序并行执行,而模块则是包含了多个函数,是组织代码的基本单元。

-module(geometry).                      % 声明本文件是 geometry 模块
-define(PI, 3.14159).                                                 % 定义宏
-define(sub(X,Y), X - Y).
-export([test/0, area/1]).              % 导出 test area 函数,0 1 是参数数目

% 实现1 求正方形面积
area({rectangle, Width, Height})
    -> Width * Height;
area({circle, Radius})
    -> ?PI * Radius * Radius;                             % 使用了宏替换
area({square, Side})
    -> Side * Side.

% 测试用例
test() ->
    12 = area({rectangle, 3, 4}),       % 用例1
    144 = area({square, 12}),           % 用例2
    tests_passed.                       % 用例全部通过

代码的编译和执行过程:

$ erl                                   % 进入 erlang shell
1> c(geometry).                         % 编译
{ok,geometry}
2> geometry:area({rectangle, 10, 5}).   % 调用 area 函数
50
3> geometry:area({square, 3}).
9
4> geometry:test().                     % 调用 test 函数
tests_passed

再来解释下标点符号的使用:

  • , 用于分隔开函数调用参数、数据构造、和模式中的参数、表达式

  • ; 用于分隔函数子句,以及匹配表达式

  • . 是句号,分隔函数整体

Erlang 是没有专门的控制流程的语句的,有的只有表达式.但是我们可以构造出通用的控制流程结构.

3.2 分支流程

3.2.1 函数匹配

函数匹配就是一种分支控制,如上面例子中的area函数匹配。

3.2.2 Guard 保护式/卫式/关卡/断言

当函数的匹配结构是一样的时候,需要对具体的值进行判断了,这时候可以使用 Guard ,进一步分支。

when 开头,; 连接相当于 or, ,连接相当于 and. 常见的用于 Guard 的的函数有:is_atom(X) is_function 等.

max(X, Y) when X > Y -> X; % 如果匹配这句,就直接返回
max(X, Y) -> Y.

% , 等价 andalso ; 等价 orelse
% if( is_int(X) && X > Y && Y < 6 ) return X
moreThan6(X, Y) when is_integer(X), X > Y, Y < 6 -> X; 
moreThan6(X, Y) -> Y.

3.2.3 if 表达式

if 的表现与 "函数+卫式" 的构造一样:

cost2(X) ->
  if
    X == orange -> 5;
    X == newspaper -> 8;
    X == apples -> 2;
    X == pears -> 9;
    X == milk -> 7;
    true -> unkown             % 用于捕获其他所有的情况
  end.

3.2.4 case of 表达式

case of 的表达式就像是整个函数头,可以对函数的每个参数使用 匹配模式 + Guard

beach(Temprature) -> % 根据温度决定去不去海滩玩耍
  case Temprature of
    {celsius, N } when N >= 20, N =< 45 -> 'favorable';
    {kelvin, N} when N >= 293, N =< 318 -> 'favorable';
    _ -> 'avoid beach'
  end.

个人感觉,去掉 if 和 case of 完全可行,同一个操作没必要使用多种写法。

3.3 循环流程

Erlang 没有 for 语句,但是使用模式匹配和高阶函数,却可以创造自己的"控制抽象",一则可以根据实际问题创建出精确的控制结构,二来不受语言自带的少量固定控制结构的束缚.

for(Max, Max, F) -> % 这句一定要在前面,是终止条件
  [ F(Max) ];
for(I, Max, F) ->
  [ F(I) | for(I+1, Max, F) ].
%% 21> lib:for(1, 10, fun(X) -> X end).
%% [1,2,3,4,5,6,7,8,9,10]
%% 22> lib:for(1, 10, fun(X) -> X * 2 end).
%% [2,4,6,8,10,12,14,16,18,20]

3.4 高阶函数

4> Double = fun(X) -> 2 * X end.
5> Double(2).
4
7> Hypot = fun(X, Y) -> math:sqrt(X*X +Y*Y) end.
8> Hypot(10, 20).
22.360679774997898

10> TempConvert = fun({c, C}) -> {f, 32+C*9/5};
10>                  ({f, F}) -> {c, (F-32)*5/9} end.
11> TempConvert({c, 100}).
{f,212.0}
12> TempConvert(TempConvert({c, 100})).
{c,100.0}

再来看两个接收高阶函数的作为参数的常用函数lists:map()lists:filter()

# lists:map(F, L) L 函数
6> L = [1,2,3,4,5,6].
[1,2,3,4,5,6]
7> lists:map(fun(X) -> 2 * X end, L).
[2,4,6,8,10,12]

# lists:filter(F, L) L 函数
8> Even = fun(X) -> (X rem 2) =:= 0 end.
#Fun<erl_eval.7.126501267>
10> lists:map(Even, [1,2,3,4,5,6,7,8]).
[false,true,false,true,false,true,false,true]
11> lists:filter(Even, [1,2,3,4,5,6,7,8]).
[2,4,6,8]

再来看一下将高阶函数作为返回值的例子:

# lists:member(X, L) Bool  X 是否在 L 内
12> Fruit = [apple,pear,orange].
[apple,pear,orange]
13> MakeTest = fun(L) -> (fun(X) -> lists:member(X, L) end ) end.
#Fun<erl_eval.7.126501267>
14> IsFruit = MakeTest(Fruit).
#Fun<erl_eval.7.126501267>
15> IsFruit(dog).  
false
16> IsFruit(apple).
true
17> lists:filter(IsFruit, [dog,orange,cat,apple,bear]).
[orange,apple]

然后我们通过高阶函数来改造下shop例子:

sum([H|T]) -> H + sum(T);
sum([]) -> 0.

map(_, []) -> [];
map(F, [H|T]) -> [F(H) | map(F, T)].
total( L ) ->
    sum( map( fun({What,N}) -> shop:cost(What) * N end, L ) ).
% 6> shop:total([{oranges,6},{newspaper, 1},{milk, 2}]).
$ 52

可重入解析代码 reentrant parsing code

解析组合器 parser combinator

惰性求值器 lazy evaluator

4. 并发

4.1 new process

% 运行一个模块里的函数作为进程
Pid = spawn(模块名,函数名,参数list).

16> F = fun() -> io:format("new process~n") end.              
17> spawn(F). % 产生一个新进程
new process
<0.126.0>

% 产生多个新进程,并且通过闭包,传递的值 X 
13> G = fun(X) -> timer:sleep(10), io:format("~p~n", [X]) end.
15> [spawn(fun() -> G(X) end) || X <- lists:seq(1,10)]. 
[<0.114.0>,<0.115.0>,<0.116.0>,<0.117.0>,<0.118.0>,
 <0.119.0>,<0.120.0>,<0.121.0>,<0.122.0>,<0.123.0>]

4.2 send data

Pid ! {a, 12}                                     % 向 Pid 发送消息 {a, 12} 
foo(12) ! area({square, 5}) % foo(12) 必须返回一个 Pid , area(...) 将表达式计算出的值,发送给 Pid
6> self() ! hello.                 % 向 erlang shell 发送数据
hello
7> self() ! world. 
world
8> self() ! "how are you". 
"how are you"
9> flush(). % 由于 erlang shell 一直没有接收(暂时存在进程邮箱里),可以通过 flush 将它们冲刷出来
Shell got hello
Shell got world
Shell got "how are you"
ok

4.3 receive data

receive
    Pattern1 when Guard1 -> Expr1;  % 匹配模式一一验证
    Pattern2 when Guard2 -> Expr2;
    Pattern3 -> Expr3
end

海豚例子:

dolphin() ->
  receive
    {From, do_a_flip} ->
      From ! "Nice to meet you, give me fish ~",
      dolphin();
    {From, fish} ->
      From ! "thanks"; % 没有递归了,进程或直接退出
    _ ->
      io:format("do nothing ~n"),
      dolphin()
  end.
2> D1 = spawn(concurence, dolphin, []).
3> D1 ! {self(),fish}.
4> flush().
Shell got "thanks"
false

4.4 keep status process

fridge(FoodList) ->
  receive
    {From, {store, Food}} ->
      From ! {self(), ok},
      fridge([Food|FoodList]);
    {From, {take, Food}} ->
      case lists:member(Food, FoodList) of
        true ->
          From ! {self(), {ok, Food}},
          fridge(lists:delete(Food, FoodList));
        false ->
          From ! {self(), {ok, not_found}},
          fridge(FoodList)
      end;
    terminate -> ok
  end.
1> Pid = spawn(concurence, fridge, [[baking_soda]]).
2> Pid ! {self(), {store, milk}}.
3> flush().
Shell got {<0.62.0>,ok}
4> Pid ! {self(), {store, bacon}}.
{<0.60.0>,{store,bacon}}
5> Pid ! {self(), {take, bacon}}.
{<0.60.0>,{take,bacon}}
6> Pid ! {self(), {take, turkey}}.
{<0.60.0>,{take,turkey}}
7> flush().
Shell got {<0.62.0>,ok}
Shell got {<0.62.0>,{ok,bacon}}
Shell got {<0.62.0>,{ok,not_found}}

5. 错误处理

常见处理错误的方式:

  • 在正常代码逻辑的每一层都处理错误

  • 正常代码逻辑处直接抛出异常,推到程序最顶层的 try catch 处理

Erlang 还有第三种方式:

  • 将异常处理逻辑从程序的正常执行流中移出来,放到另外一个并发进程中,这样做可以让业务代码更加整洁,只需要处理"正常的情况"

5.1 throw exit error

generate_exception(1) -> a;           % 正常
generate_exception(2) -> {'EXIT', a}; % 正常
generate_exception(3) ->
  throw(a); % 抛出异常, 只是为了改变控制流(非局部返回),并期望调用方去处理异常
generate_exception(4) ->
  exit(a); % 终止进程,如果未捕捉,广播 {'EXIT', Pid, Why} 到链接到的其他进程,不返回 Stack
generate_exception(5) ->
  error(a). % 崩溃性错误,会结束当前进程,会返回 Stack

5.2 catch

% 旧的异常处理风格
case (catch foo(...)) of
    {'EXIT', Why} ->...
    Val           ->...
end.

% 使用 catch 直接将任何异常转换为 {"EXIT", ...} 元组
demo2() ->
  [{I, catch generate_exception(I) } || I <- [1,2,3,4,5]].

%%2> error:demo2().
%%[{1,a},
%%{2,a},
%%{3,{'EXIT',a}},
%%{4,{'EXIT',a}},
%%{5,
%%{'EXIT',{a,[{error,generate_exception,1,
%%[{file,"error.erl"},{line,15}]} ...
  • catch 从 exit(P) 拿到的是 {'EXIT', P}

  • catch 从 throw(Q) 拿到的就是 Q

如果系统的顶层,不设法修正一个它检测到的错误,那么进程将终止,并且广播给所有的 Linker 和 Monitor. 进程死掉的时候产生了一个 {'EXIT',Why} 异常,那么退出信号 {'EXIT', P, Why} 会发送给所有 Linker 和 Monitor.

任何收到 Why 不为 normal 信号的进程都会一起死亡,除非它是“系统进程”,系统进程会将退出信号转换为一个进程间通信消息,添加到它的邮箱中。

go() -> 
    process_flag(trap_exit, true), % 成为系统进程
    loop().
loop() -> 
    receive
        {'EXIT', P, Why} -> Why;
    end.

例外是 exit(P, kill) 调用,这种信号可以直接杀死 P 系统进程.

5.3 try catch

try FuncOrExpressionSequence of  %% 首先对FuncOrExpressionSequence求值
    Pattern1 [when Guard1] ->Expressions1;
    ...
catch
    ExceptionType: ExPattern1 [when ExGuard1] ->ExExpressions1;
    ...
after
    AfterExpressions
end.
% 新的异常处理风格
catcher(N) ->
  try 
    generate_exception(N) % 中间可以写多个表达式, 号隔开
  of
    Ret -> {N, normal, Ret} % 如果表达式执行正常,未抛出异常
  catch
    throw:Ret -> {N, caught, thrown, Ret}; % 处理 throw 异常
    throw:{fileError, Ret} -> {N, caught, thrown, Ret}; % 处理 fileError 异常
    exit:Ret  -> {N, caught, exited, Ret};  % 处理 exit 异常
    error:Ret -> {N, caught, error, Ret};  % 处理 error 异常
    _:_       -> {unkown, error}                                            % 处理所有异常错误的代码
  after 
    {N, always, exec } % 一定会执行的子句,不返回任何值,常用来关闭打开的文件等操作
  end.

demo1() ->
  [catcher(I) || I <- [1,2,3,4,5]].
%%2> error:demo1().
%%[{1,normal,a},
%%{2,caught,thrown,a},
%%{3,caught,exited,a},
%%{4,normal,{'EXIT',a}},
%%{5,caught,error,a}]

5.4 link

通过 link(Pid) 函数将本进程 与 指定进程链接到一起,当其中一个进程死亡时,另一个可以收到消息通知.

myproc() ->
  timer:sleep(5000),
  throw({no_reason}). % 改成 exit 和 error 分别试试

chain(0) ->
  receive
    _ -> ok
  after 2000 ->
    throw("chain dies here") % 改成 exit 和 error 分别试试
  end;
chain(N) ->
  link(spawn( fun() -> chain(N - 1) end )),
  receive
    _ -> ok
  end.
3> link(spawn(linkmon, myproc, [])). % exit({no_reason}) 的表现
** exception error: {no_reason}

5> link(spawn(linkmon, myproc, [])). % error({no_reason}) 表现
Error in process <0.95.0> with exit value:
{{no_reason},[{linkmon,myproc,0,[{file,"linkmon.erl"},{line,7}]}]}
** exception error: {no_reason}
     in function  linkmon:myproc/0 (linkmon.erl, line 7)
                                                   
7> link(spawn(linkmon, myproc, [])). % throw({no_reason}) 表现
Error in process <0.103.0> with exit value:
{{nocatch,{no_reason}},[{linkmon,myproc,0,[{file,"linkmon.erl"},{line,7}]}]}
** exception error: {nocatch,{no_reason}}
     in function  linkmon:myproc/0 (linkmon.erl, line 7)
                                                   
12> link(spawn(linkmon, chain, [3])). % 错误一直从 chain(0) 传递到本 shell
Error in process <0.131.0> with exit value:
{{nocatch,"chain dies here"},
 [{linkmon,chain,1,[{file,"linkmon.erl"},{line,13}]}]}
** exception error: {nocatch,"chain dies here"}
     in function  linkmon:chain/1 (linkmon.erl, line 13)                                                

错误在进程之间传播,并且杀死对方,是通过"信号"实现的.通过将进程设置为"系统进程",可以将"kill"信号转化为消息,从而避免被杀死.

6> process_flag(trap_exit, true). % 设置本进程成为 系统进程

7> spawn_link(fun() -> 1/0 end). 
Error in process <0.92.0> with exit value:
{badarith,[{erlang,'/',[1,0],[]}]}

8> flush().
Shell got {'EXIT',<0.92.0>,{badarith,[{erlang,'/',[1,0],[]}]}}

现在快速杀死进程的机制已经有了,现在再看下快速重启的机制.

5.5 monitor

连接是双向的,并且两个进程之间只能存在一条链接,而监控是单向的,并且可以叠加多条监控.

> Ref = erlang:monitor(prcess,B) % 本进程 A 监控进程 B
{'DOWN', Ref, process, B, Why} % B 死掉时,A 进程会收到本消息
12> erlang:monitor(process, spawn(fun() -> timer:sleep(500) end)).
#Ref<0.950962386.96468998.180487>
13> flush().
Shell got {'DOWN',#Ref<0.950962386.96468998.180487>,process,<0.99.0>,normal}

5.6 register process

通过链接和监控都能检测到我们依赖的某个进程的死亡,然后我们可以做些什么呢?

% 给 Client 调用的
send(X) ->
  Ref = make_ref(),
  critic ! {self(), Ref, {X}},
  receive
    {Ref, Y} -> Y
  after 2000 ->
    timeout
  end.

% 启动一个服务进程
start() ->
  spawn(?MODULE, protect_critic, []).

% critic 的保护进程
protect_critic() ->
  process_flag(trap_exit, true),                  % 系统进程
  Pid = spawn_link(?MODULE, critic, []),
  register(critic, Pid),                          % 给进程命名, 挂到注册树上
  receive
    {'EXIT', Pid, normal}     -> ok;              % exit normal
    {'EXIT', Pid, shutdown}   -> ok;              % exit by hand
    {'EXIT', Pid, _}          -> protect_critic() % restart it
  end.

% critic 进程
critic() ->
  receive
    {From, Ref, {"a"}}           -> From ! {Ref, "A"};
    {From, Ref, {"b"}}           -> From ! {Ref, "B"};
    {From, Ref, {X}}             -> From ! {Ref, X};
    {From, Ref, _}               -> From ! {Ref, unkown}
  end,
  critic().

端口收到的数据,都会转化成 {Port, {data, D}} 消息,给到控制进程。

5.7 热更新

A 模块使用 B 模块的代码,假如 B 模块更新来,原来执行B模块代码中进程,既可以选择执行老版本代码,也可以选择执行新版本代码。

-moudule(m).
loop(Data, F) -> 
    receive
            {From, Q} -> {Reply, Data1} -> F(Q, Data),
        m:loop(data1, F); % 每次调用都加载最新版本
        % loop(data1, F); % 希望继续执行当前版本代码,而不是新版本代码
    end.

6. 定时通知小应用

定时通知小应用代码

7. OTP框架

Erlang 内置机制:链接link() 监控monitor() 服务器 超时 捕获退出信号 使用引用标记信息.

并发编程陷阱:保证执行的顺序 避免竞争条件 进程随时可能死亡 代码热加载 命名进程 监督者进程.

OTP就是利用上述的机制,考虑了各种陷阱,创建的一组模块和标准。

Erlang并发程序模式:一个函数负责创建新的进程,一个函数负责提供初始值,一个函数负责主循环,具备容错功能的消息发送函数

7.1 gen_server

通用型服务进程。

7.2 gen_fsm

State(S) x Event(E) -> Actions(A), State(S')

有限状态机适用于文本解析,模式匹配、游戏逻辑等等方面的处理。

gen_fsm:start_link(进程注册名,回调模块,传递给 init/1 的参数, 状态机的选项)

7.3 gen_event

事件管理器。

7.4 supervisor

监督者。

8. OTP 应用

OTP应用特指用 OTP behavior 实现进程,再用一种特定的结构对它们进行打包。通过这个结构,VM 就知道如何进行初始化以及退出清理。

9. 分布式编程

9.1 分布式编程

spawn(Node, Fun) % 在一个远端节点 Node, 运行 Fun 进程
monitor(Node)    % 监视整个节点

9.2 端口 Port

Port ! {Pid, Command} % Pid 是端口控制进程 控制端口完成Command任务
% {command, Data} 往端口写入数据
% close 关闭端口
% {connect, Pid1} 移交端口控制权给 Pid1

10. 分布式OTP应用