Vissza a bloghoz

Nginx szerver és location blokk kiválasztási algoritmusok: Áttekintés

Nginx szerver és location blokk kiválasztási algoritmusok: Áttekintés

Bevezetés

Nginx a világ legnépszerűbb webszerver-opciói közé tartozik. Képes sikeresen kezelni a szimultán klienskapcsolatok sokaságát. Ugyanakkor levelező-, web- vagy fordított proxy szerverként is működik.

Ez az útmutató bemutatja azokat a háttérben zajló folyamatokat, amelyek meghatározzák, hogyan dolgozza fel az Nginx a kliensek kéréseit. Érthetővé tesszük a server és a location blokk felépítését, valamint elmagyarázzuk, hogyan lehet a legjobban csökkenteni a kérések kezelésének látszólagos kiszámíthatatlanságát.

Mindenekelőtt, íme egy átfogó útmutató arról, hogyan telepítheti az Nginx-et az Ubuntu szerverére. Most pedig kezdjük el!

Blokk-konfiguráció az Nginx-szel

Az Nginx logikus megközelítése magában foglalja a különböző célokra szánt konfigurációk különálló, logikusabb tartalomblokkokba történő csoportosítását. Ezek hierarchikus struktúrában helyezkednek el. Amikor egy kliens kérést küld, az Nginx elindít egy folyamatot, amelynek során meghatározza, hogy ezen konfigurációs blokkok közül melyek a legalkalmasabbak a kérés kiszolgálására. Erre a döntési folyamatra fogunk összpontosítani.

Az elsődleges blokkok, amelyekről szó lesz, a server blokkok és a location blokkok. A server blokkok az Nginx által létrehozott konfigurációk azon részhalmazai, amelyek meghatározzák, hogy melyik virtuális szerver felelős egy adott típusú kérés kezeléséért. Leggyakrabban a bejövő kérés IP-címe, domainneve vagy portja alapján dől el. A rendszergazdák több server blokkot is konfigurálnak. Ezután el kell dönteniük, hogy a kapcsolatok közül melyiknek kell kezelnie a kérést.

A location blokkok a server blokkokon belül helyezkednek el. Ezek hozzák meg a döntést arról, hogyan és milyen erőforrásokat használhat fel a szülőszerverükre érkező kérések kezelésére. Ez a modell rendkívül rugalmas. Az URI-tér konfigurálható úgy, hogy ezeket a blokkokat a rendszergazda által legjobbnak ítélt módon használja.

Annak eldöntése, hogy melyik blokk melyik kérést kezelje az Nginx-szel

Az Nginx lehetővé teszi több server blokk meghatározását. Mindegyik különböző virtuális webszerverként működik. Ezért szükség van egy módszerre, amely meghatározza, hogy melyik szerver fogja kezelni az egyes bejövő kéréseket. Ez úgy történik, hogy meghatározott ellenőrzések rendszere révén megtalálják a kérés teljesítéséhez leginkább megfelelőt.

Az Nginx elsősorban két fő server blokk direktívával foglalkozik: a listen és a server_name.

Lehetséges egyezések keresése a „listen” direktívával

Az Nginx először a kérés portját és IP-címét értékeli ki. Ezután ezt összeveti az egyes szerverek listen direktívájával. A szerverlista ezen elemzése segít elkülöníteni azokat a server blokkokat, amelyek képesek megoldani a szóban forgó kérést.

Jellemzően a listen direktíva határozza meg azt a portot és IP-címet, amelyre egy adott server blokk válaszolni fog. Az olyan server blokk, amely nem tartalmaz listen direktívát, alapértelmezés szerint a 0.0.0.0:80 listen paramétereket kapja meg. Ha az Nginx-et egy normál, nem root felhasználó futtatja, a listen paraméter 0.0.0.0:8080-ként van meghatározva. Ez azt jelenti, hogy függetlenül az interfésztől, ha a blokkok a 80-as portról érkeznek, az így meghatározott blokkok fognak válaszolni rájuk. Ez az alapértelmezett érték azonban nem esik nagy súllyal a szerver kiválasztásának folyamatában.

A listen direktívát a következőkre konfigurálhatja:

  • Egyetlen IP-cím, amely az alapértelmezett porton (80) figyeli a kéréseket.
  • Egyetlen port, amely az adott porton bármely interfészt figyeli.
  • Egy port és IP-cím kombinációja.
  • Egy beállított Unix socket elérési út (ennek az opciónak csak akkor van jelentősége, ha a kérések különböző szervereken haladnak keresztül).

Az Nginx egy szabályrendszert alkalmaz, amikor eldönti, hogy melyik server blokknak küldjön el egy kérést. A szabályok a listen direktíva konkrét konfigurációjától függenek. Ezek a következők:

  • If a listen directive is incomplete, the missing pieces get their default values. This means that the IP address and port will be forced into completion with default values in order to process the request.
    • Ebben az esetben a listen direktívát nem tartalmazó blokk a 0.0.0.0:80 alapértelmezett értéket fogja használni.
    • Egy olyan blokk, amelyből hiányzik a port, és a 111.111.111.111 IP-címmel rendelkezik, a következővé válik: 111.111.111.111:80.
    • Ha nincs IP-cím, a 8888-as porttal rendelkező blokk megkapja az alapértelmezett IP-címet, amelyet hozzáfűzve létrejön a 0.0.0.0:8888.
  • A meghatározott IP-cím és port birtokában az Nginx ezután olyan szerverblokkokat keres, amelyek egyeznek az adott porttal.
  • Ha csak egy konkrét egyezést talál, ez lesz a szerverblokk. Ha több blokk is megfelel, az Nginx a server_name direktívához fordul, hogy tovább szűkítse a kört a kérdéses pontos szerverblokkra.

Az Nginx csak akkor folyamodik a server_name direktíva kiértékeléséhez, ha nem találta meg a listen direktívából származó pontos specifitási szinttel rendelkező szerverblokkot. Ha az example.com a 80-as porton van, 192.168.1.10-es IP-címmel, ebben a példában mindig az első blokk fogja kiszolgálni a kérést. Ez független attól, hogy mit mond a server_name direktíva:

Nginx Server

Ha egynél több olyan megfelelő szerverblokk van, amely specifitásban egyezik, akkor a server_name direktíva kerül figyelembevételre.

Lehetséges egyezések keresése a ‘Server_Name’ direktívával

Ha a listen direktívák egyformán specifikusak, az Nginx ellenőrzi a kérés ’Host’ fejlécét. Ez az az érték, amely kezdetben annak a tartománynak az IP-címét tartalmazza, amelyet az ügyfél el akart érni. Az Nginx a server_name direktívát fogja használni minden egyes még alkalmas szerverblokk-jelöltön belül. Ezeket a kiértékeléseket egy képlet alapján végzi el. Ez a következő:

  • Az Nginx első kísérlete egy olyan blokk azonosítása lesz, amelynek server_name értéke pontosan megegyezik a kérésben szereplő ‘Host’ fejléc értékével. Ha megtalálja, a pontos egyezést tartalmazó blokk fogja kiszolgálni a kérést. Ha több blokkot is talál, a listán szereplő elsőt választja.
  • Ha nincs pontos egyezés, az Nginx megpróbálja a server_name segítségével megtalálni azt a szerverblokkot, amely a konfigurációban a szerverblokk nevének elején található * helyettesítő karakterrel (wildcard) egyezik. Ha ezzel a módszerrel talál egyet, az azt jelenti, hogy a szerverblokk meghatározásra került. Ha egynél több egyezést talál, a leghosszabb egyezés fogja teljesíteni a kérést.
  • Egyező kezdő helyettesítő karakter hiányában az Nginx megpróbál egyező záró helyettesítő karakterrel rendelkező szerverblokkot találni. Más szóval, ez egy olyan szervernév lesz, amelynek a végén * szerepel a konfigurációban. Ha talál ilyet, azt használja a kéréshez. Ha viszont többet is talál, az Nginx ismét a leghosszabb egyezést fogja használni.
  • Abban az esetben, ha mindkét helyettesítő karakteres kísérlet után sincs egyezés, az Nginx azokat a szerverblokkokat fogja kiértékelni, amelyek a server_name-et reguláris kifejezésekkel határozzák meg (ezt a név előtti ~ jelzi). Az első olyan server_name előfordulás, amelynek kifejezése megegyezik a ‘Host’ fejlécével, lesz a kérés kezelésére kijelölt szerverblokk.
  • Ha ezen a ponton sincs egyezés, az Nginx az adott port és IP-cím kombinációhoz tartozó alapértelmezett szerverblokkot fogja használni.

Minden port/IP-cím kombinációhoz tartozik egy kijelölt szerverblokk. Ez kerül felhasználásra, ha a kérés kezeléséhez megfelelő szerverblokk meghatározására vonatkozó szabályok eredménytelenek. Ez lesz a konfiguráció első olyan blokkja, amely tartalmazza a default_server opciót a listen direktívában (ez felülírná a kezdetben talált algoritmust). Minden IP-cím/port kombináció legfeljebb egy default_server beállítással rendelkezhet.

Példák a szerverblokk kiválasztására

Ha a meghatározott server_name pontosan megegyezik a ‘Host’ fejléc értékével, akkor ez a szerverblokk kerül kiválasztásra a kérés feldolgozásához. A következő példa egy „host1.example.com” értékként megjelölt ‘Host’ fejlécet mutat be a kérésben. Ebben az esetben a második szervert fogja kiválasztani:

Nginx Server

Pontos egyezés hiányában az Nginx ellenőrzi, hogy létezik-e helyettesítő karakterrel ellátott server_name. Ha nem, a leghosszabb, helyettesítő karakterrel kezdődő egyezés kerül kiválasztásra. A következőkben a „www.example.org” szerepel a „Host” fejlécben. Ez azt jelenti, hogy a második blokkot fogja választani:

server screenshot

Ha nincs helyettesítő karakterrel kezdődő egyezés, az Nginx továbblép a záró helyettesítő karakteres ellenőrzésre. A kérés feldolgozásához a leghosszabb, helyettesítő karakterre végződő egyezés lesz kiválasztva. Ebben az esetben a „Host” fejléc „www.example.com”, így a harmadik szerverblokkot fogja választani:

Nginx Server

Ha még mindig nincs egyezés, az Nginx megpróbálja illeszteni a server_name direktívákat reguláris kifejezések használatával. Ezen kifejezések közül az első lesz kiválasztva a kérés feldolgozásához. Ha a „Host” értéke „www.example.com,” a második szerverblokk lesz kiválasztva a kérés kiszolgálására:

Nginx Server

Ha még mindig nincs egyezés, a kérés arra az IP-cím és port kombinációra kerül, amelyhez a megfelelő alapértelmezett szerver (default server) van beállítva.

Location blokk illesztés

Az Nginx-nek meg kell határoznia egy algoritmust is, amely alapján eldönti, hogy a szerver melyik location blokkja lesz felelős a kérés megválaszolásáért.

A location blokkok szintaxisa

Mielőtt elmagyaráznánk, hogyan dönti el az Nginx, hogy melyik location blokkot jelöli ki a kérések kezelésére, áttekintjük a location blokkok definícióinak szintaxisát. Mint korábban említettük, a location blokkok a server blokkokban (és más location blokkokban) találhatók. Céljuk, hogy döntéseket hozzanak a kérés URI-jának feldolgozásáról. Az URI a kérés azon része, amely az IP-cím és port, vagy a domain név után következik a kérésben.

A location blokkok általában így néznek ki:

Syntax for Location Blocks

Az Nginx ellenőrzi a kérés URI-ját a location_match-csel szemben. A fenti módosító jelenléte vagy hiánya határozza meg, hogyan kísérli meg az Nginx illeszteni a blokkokat. A módosítótól függően a location blokkok az alábbi szabályok szerint kerülnek értelmezésre:

  • Nincsenek módosítók: Módosítók nélkül a location prefix (előtag) egyezésként lesz értelmezve. Ez azt jelenti, hogy a megadott location a kérésben szereplő URI elejével lesz összehasonlítva a megfelelő egyezés meghatározásához.
  • =: Az egyenlőségjel azt jelzi, hogy ez a blokk akkor minősül egyezésnek, ha a kérés URI-ja pontosan megegyezik a megadott location-nel.
  • ~: A tilde módosító azt jelzi, hogy a location blokk illesztése kis- és nagybetű-érzékeny lesz.
  • ~*: A tilde és a csillag módosító kombinációja azt jelzi, hogy a location blokk kis- és nagybetű-független lesz az egyezés keresése során.
  • ^~: Ha a tilde módosító előtt egy kalap (caret) karakter áll, a reguláris kifejezések illesztése nem történik meg, amennyiben ezt a blokkot választják ki a legjobb nem-reguláris kifejezéses egyezésként.

Példák a location blokk szintaxisára

A prefix (előtag) egyezésre mutatva egy példát, a location blokk lesz kiválasztva a kérés URI-jának megválaszolására, ha az /site, /site/page1/index.html vagy /site/index/html formátumú:

location site

A szükséges URI-illesztés bemutatása céljából ez a blokk mindig a /page1 formátumú URI-kérések megválaszolására szolgál, és nem a /page1/index.html kérés URI-ra. Ha ez a kiválasztott blokk, és a kérést egy indexoldal használatával teljesíti, a kérés tényleges kezelője belsőleg át lesz irányítva egy másik location-re:

Nginx Server

Például egy olyan location esetén, amelyet kis- és nagybetű-érzékeny kifejezéssel kell értelmezni, a következő blokk nem tudná kezelni a /FLOWER.PNG kéréseket. Azonban kezelni fogja a /tortoise.jpg kéréseket:

Nginx Server and Location Block Selection Algorithms: Overview

Ezután figyeljen meg egy olyan blokkot, amely a fentihez hasonlóan lehetővé teszi a kis- és nagybetű-független illesztést. Ebben az esetben a blokk mind a //tortoise.jpg és /FLOWER.PNG:

location

Az utolsó változat az, amelyben egy blokk megakadályozza a reguláris kifejezések illesztését, ha a döntés szerint ez a legoptimálisabb nem-reguláris kifejezéses egyezés. Ez képes kezelni a /costumes/ninja.html kéréseket:

Nginx Server and Location Block Selection Algorithms: Overview

Pontosabban fogalmazva, a módosítók határozzák meg a location blokkok kiválasztásának módját. Ez azonban nem mondja meg, hogy az Nginx milyen döntési algoritmust használ annak a location blokknak az azonosítására, amelyre a kérést küldeni kell. Ezzel foglalkozzunk a következőkben.

A kéréseket kezelő Location kiválasztása az Nginx által

Az a módszer, amellyel az Nginx kiválasztja a kérést feldolgozó location-t, hasonló ahhoz, ahogyan a szerverblokkok kiválasztása történik. Más szóval, egy folyamaton végighaladva határozza meg a legoptimálisabb location-t minden egyes kéréshez. Az Nginx pontos és megfelelő konfigurálásához elengedhetetlen ennek a folyamatnak a megértése.

A korábban említett location deklarációkat szem előtt tartva, az Nginx hasonlóképpen használja a potenciális location kontextusokat úgy, hogy ellenőrzi az egyes location-ök alkalmasságát, összehasonlítva azokat az adott kérésből származó URI-val. Ebben a következő algoritmust alkalmazza:

  • Először az Nginx ellenőrzi az összes olyan location típust, amely nem tartalmaz reguláris kifejezést. Ezt úgy teszi, hogy megkeresi az összes location-alapú prefix (előtag) egyezést. Ehhez összeveti a location-t a kérés teljes URI-jával.
  • Az Nginx először pontos egyezést keres. Amint azonosít egy olyan location blokkot, amely az = módosítót használja, összehasonlítja azt a kért URI-val. Ha a kettő pontosan megegyezik, a rendszer azonnal ezt a location blokkot választja ki a kérés kezelésére.
  • Ha nincsenek olyan location-ök, amelyek pontosan megfelelnének az = módosítóval való összehasonlításnak, az Nginx továbblép a nem pontos prefixek kiértékelésére. Miután meghatározta a kérés URI-jára illeszkedő leghosszabb prefix location-t, a következő kiértékeléseket végzi el:
    • Ha a leghosszabb prefix egyezéssel rendelkező location a ^~ módosítót használja, a rendszer azonnal ezt a location-t választja ki.
    • Ha a leghosszabb prefixszel rendelkező location nem használja a ^~ módosítót, az Nginx ideiglenesen megjegyzi az egyezést, hogy a keresés fókusza továbbmozdulhasson.
  • Miután a leghosszabb prefix location egyezést megtalálta és eltárolta, az Nginx áttér a reguláris kifejezést használó location-ök kiértékelésére. Ezek közé tartoznak mind a kis- és nagybetű-érzékeny, mind az érzéketlen egyezések. Ha a leghosszabb egyező prefix location-ön belül találhatók reguláris kifejezést használó location-ök, az Nginx átrendezi a listát, hogy ezeket a location-ök listájának elejére helyezze. Az átrendezett listából az első olyan kifejezés lesz a kérés kiszolgálására kiválasztott location, amely illeszkedik a kérés URI-jára.
  • Ha nem található a kérés URI-jának megfelelő reguláris kifejezés, a korábban eltárolt location lesz kiválasztva a kérés feldolgozására.

Az Nginx alapértelmezés szerint előnyben részesíti a reguláris kifejezés alapú egyezéseket a preferált prefix alapúakkal szemben. Ugyanakkor először a prefix location-öket értékeli ki, így az adminisztrátor felülbírálhatja ezt a tendenciát az = és ^~ módosítókkal.

Egy másik fontos tanulság, hogy míg a prefix location-ök általában a legspecifikusabb, leghosszabb megtalált egyezésen alapulnak, a reguláris kifejezések ellenőrzése leáll, amint az első egyezést azonosítja. Ez azt jelenti, hogy a konfiguráción belüli elhelyezésnek valós következményei vannak a reguláris kifejezést használó location-ök esetében.

Az utolsó érintendő pont az, hogy a leghosszabb prefixszel rendelkező egyezésen belüli reguláris kifejezés egyezések lényegében előreugranak a sorban az Nginx location kiértékelései során. Ezek a lista elejére kerülnek, és a többi reguláris kifejezés előtt kerülnek kiértékelésre.

Mikor történik átugrás más location-ökre a location blokkok kiértékelése során?

Általában, miután egy kérést kiértékeltek és kiválasztották a kezelésére szolgáló location blokkot, a kérés kezelése teljesen azon a kontextuson belül történik. Ez azt jelenti, hogy csak az örökölt direktívák és a kiválasztott location-ök határozzák meg a kérés feldolgozását, a szomszédos location blokkok bármiféle közreműködése nélkül.

Bár ez egy általános irányelv, amely lehetővé teszi a location blokkok kiszámítható tervezését, néha a location-ön belüli bizonyos direktívák új keresést is kiválthatnak. Más szóval, a „csak egy location blokk” szabálynak van néhány kivétele. Előfordulhat, hogy ezek a kivételek nem felelnek meg a location blokkokkal kapcsolatos elvárásoknak, ezért lehet, hogy nem a várt módon kezelik a kérést.

Ezek a belső átirányítások bizonyos direktívák miatt jelenhetnek meg, többek között:

  • index
  • rewrite
  • error_page
  • try_files

Ha az index direktívát használja, az a kéréskezelés során mindig belső átirányítást fog eredményezni. Bár a helyegyezések (location matches) megtalálása általában leállítja az algoritmus futását a kiválasztási folyamat felgyorsítása érdekében, ha a megtalált helyegyezés egy könyvtár, a kérés valószínűleg átirányításra kerül egy másik helyre a formális feldolgozás érdekében.

Például az alábbi első location blokk egy /exact kérés URI-val egyezik meg. A kérés feldolgozásához azonban az index direktíva, amelyet a location blokk örököl, átirányítja a kérést egy másodlagos blokkba:

index

Ebben a forgatókönyvben, ha a végrehajtásnak az elsődleges blokkon belül kell maradnia, egy másik sémának kell feldolgoznia a könyvtárra irányuló kérést. Ennek egyik módja, ha érvénytelen indexet állít be a kérdéses blokkhoz, és helyette aktiválja az auto indexet:

location exact

Bár ez a módszer néhány esetben működhet, a legtöbb kontextusban általánosságban nem alkalmazható a gyakorlatban. Egy pontos könyvtáregyezés hasznos lehet olyan helyzetekben, amikor a kérést át kell írni. Ez egy teljesen új location keresést fog kiváltani.

Egy másik direktíva, amely a feldolgozási hely újraértékelésére használható, a try_files direktíva. Ez arra utasítja az Nginx-et, hogy kifejezetten ellenőrizze, létezik-e a fájlok vagy könyvtárak egy megnevezett csoportja, ahol az utolsó keresési feltétel az az URI, amelyre az Nginx-nek belsőleg át kell irányítania.

Gondoljuk át a következő konfigurációt:

root var

Ha érkezik egy kérés a /blahblah-ra, azt az első location fogja fogadni. Ha a blahblah fájl nem található a /var/www/main könyvtárban, az egy további keresést indít el a blahblah.html után. Ezután egy blahblah nevű alkönyvtárat fog keresni a /var/www/main könyvtárban. Ha mindezen ellenőrzések sikertelenek, átirányít a /fallback/index.html címre. Ez egy újabb location keresést indít el, amelyet egy másik location blokk fog fogadni. Ezután feldolgozza a /var/www/another/fallback/index.html fájlt.

Egy másik direktíva, amely egy másik location blokkba való átirányítást eredményez, a rewrite direktíva. Az Nginx egy új, egyező location-t fog keresni a rewrite direktíva eredménye alapján, ha a last paramétert használják. Ha az utolsó példát úgy módosítjuk, hogy most már tartalmazza ezt a rewrite direktívát, nyilvánvalóvá válik, hogy a kérés átirányítható egy másik helyre a try_files direktíva végrehajtása nélkül is:

root var ww main

Ebben a példában a /rewrite/hello kérést kezdetben az első location fogja kezelni. Miután átíródik a /hello címre, egy másodlagos location keresés indul el. Ez egyezni fog az első location-nel. A try_file direktíva fogja feldolgozni, és potenciálisan visszatér a /fallback/index.html címre, ha nem ad találatot.

Ha azonban a kérés a /rewrite/fallback/hello címre érkezik, egyezés található az első blokkal. Így a rewrite újra lefut, de ezúttal a /fallback/hello eredményt adja. A kérés egy másik location blokkban kerül feldolgozásra.

Hasonló helyzetek fordulnak elő, amikor a return direktívát használja 301-es vagy 302-es állapotkódok küldésére. Az egyetlen különbség az, hogy egy új kérés jön létre, ami egy nagyon nyilvánvaló átirányításban nyilvánul meg. Hasonlóképpen, ez előfordulhat a rewrite direktívánál is, ha a permanent vagy redirect jelzőket alkalmazza.

Egy másik direktíva, amely a try_again-hez hasonló belső átirányításokhoz vezethet, az error_page direktíva. Ezt akkor használhatja, ha a feldolgozás során bizonyos hibakódokkal találkozik. Ha a try_files direktíva be van állítva, az error_page direktíva valószínűleg soha nem fog végrehajtódni. Ez azért van, mert az a direktíva fogja kezelni a kérés teljes életciklusát.

Vegyük a következő példát:

root screenshot

Ebben az esetben minden kérést az első blokk fog feldolgozni, amely a /var/www/main könyvtárból szolgál ki fájlokat. Ez nem vonatkozik azokra a kérésekre, amelyek a /another előtaggal kezdődnek. De ha egy fájl nem található, egy belső átirányítás indul el a /another/whoops/html címre. Ez egy újabb location kereséshez vezet. Ez viszont egy másodlagos blokkhoz irányítja a kérést, és a fájl a /var/www/another/whoops.html helyről lesz kiszolgálva.

Amint látható, azon helyzetek megértése, amikor az Nginx új location keresést indít el, segíthet jobban előrejelezni a rendszer viselkedését a kérések feldolgozása során.

Összegzés

A rendszergazdák dolga rendkívül leegyszerűsödik, ha megértik azokat a módszereket, amelyekkel az Nginx kezeli az ügyfelek kéréseit. Ez lehetővé teszi a rendszergazdák számára, hogy megállapítsák, melyik server blokkhoz érkezik majd a kérés. A kérés URI-ja alapján azt is meghatározhatják, hogy melyik location blokk kerül kiválasztásra. Összességében ez arra is lehetőséget ad a rendszergazdáknak, hogy nyomon kövessék az Nginx által alkalmazott kontextusokat az egyes kérések kezelésekor.

Végül vethet egy pillantást a többi oktatóanyagra is a blogunkon, amelyek az Nginx-re fókuszálnak. Ezek segítenek majd jobban kihasználni a világ egyik legnépszerűbb webszerverének előnyeit:

Kellemes számítógépezést!

author

Manpreet Singh

Szerző · CloudSigma

Preslav Dobrev a CloudSigma kreatív tervezője, aki hagyományos és innovatív marketingcsatornák segítségével következetes vállalati identitás kialakítására összpontosít. Kiemelkedően képes ötvözni a művészi látásmódot a stratégiai marketinggel, hogy hatásos márkatörténeteket hozzon létre.

Hozzászólások

Még nincsenek hozzászólások. Legyen Ön az első.