Materyal esas olarak yeni başlayan web programcıları için tasarlanmıştır.

Giriiş.

Genellikle kendi kendine yazılmış CMS'ye sahip olan veya acemi web programcıları tarafından yazılmış modülleri olan ve verileri korumak için neyin gerekli olduğunu anlamayan ve genellikle nasıl çalıştıklarını ve bunlarla tam olarak ne yapılması gerektiğini düşünmeden filtreleme işlevlerini kopyalayan müşterilerle görüşüyorum. .

Burada mümkün olduğunca ayrıntılı olarak açıklamaya çalışacağım. Yaygın hatalar verileri filtrelerken PHP betiği ve ver basit ipuçları verileri düzgün bir şekilde nasıl filtreleyebilirim.

İnternette veri filtreleme hakkında birçok makale var, ancak olması gerektiği gibi tam değil ve ayrıntılı örnekler içermiyor.

Bilgilendirme.

Filtreleme. 1 numaralı hata
Sayısal değişkenler için aşağıdaki kontrol kullanılır:
$number = $_GET["input_number"]; if (intval($number)) ( ... SQL sorgusu yürüt... )
neden yol açar SQL enjeksiyonu? Mesele şu ki, kullanıcı bir değişkende belirtebilir Numara giriniz anlam:
1"+BİRLİK+SEÇ
Bu gibi durumlarda, kontrol başarıyla geçilecektir, çünkü intval işlevi, değişkenin tamsayı değerini alır, yani. 1, ancak değişkenin kendisinde $sayı hiçbir şey değişmedi yani zararlı kod SQL sorgusuna iletilecektir.
Doğru filtreleme:
$number = intval($_GET["girdi_numarası"]); if ($number) ( ... SQL sorgusu yürüt... )
Tabii ki koşul değişebilir, örneğin yalnızca belirli bir aralık almanız gerekiyorsa:
if ($number >= 32 AND $number<= 65)

Sayısal değerlere sahip onay kutuları veya çoklu seçim kullanıyorsanız şunu kontrol edin:
$checkbox_arr = array_map("intval", $_POST["onay kutusu"]);
dizi_haritası
Ayrıca şu şekilde filtreleme ile karşılaşıyorum:
$number = htmlspecialchars(intval($_GET["input_number"]));
htmlözel karakterler
Veya:
$number = mysql_escape_string(intval($_GET["input_number"]));
mysql_escape_string

Bir gülümsemeden başka bir şey buna sebep olamaz :)

Filtreleme. Hata #2.
Dize değişkenleri için aşağıdaki filtreleme kullanılır:
$input_text =addlashes($_GET["input_text"]);
Addslashes işlevi, spesifikasyondan kaçar. karakterler, ancak veritabanı kodlamasını dikkate almaz ve filtrelemeyi atlamak mümkündür. Bu güvenlik açığını açıklayan yazarın metnini kopyalamayacağım ve sadece Chris Shiflett'e bir bağlantı vereceğim (çeviriyi Runet'te arayabilirsiniz).

mysql_escape_string veya mysql_real_escape_string işlevini kullanın, örnek:
$input_text = mysql_escape_string($_GET["input_text"]);
girmeyi düşünmüyorsanız html etiketleri, o zaman aşağıdaki filtrelemeyi yapmak en iyisidir:
$input_text = strip_tags($_GET["input_text"]); $input_text = htmlözel karakterler($input_text); $input_text = mysql_escape_string($input_text);
strip_tags - html etiketlerini çıkarır.
htmlspecialchars - özel dönüştürür. html varlığındaki karakterler.
SQL enjeksiyonuna ek olarak kendinizi XSS saldırılarından bu şekilde korursunuz.
Html etiketlerine ihtiyacınız varsa, ancak yalnızca kaynak kodunu görüntülemek için kullanmanız yeterlidir:
$input_text = htmlspecialchars($_GET["input_text"]); $input_text = mysql_escape_string($input_text);

Değişkenin değerinin boş olmaması sizin için önemliyse, trim işlevini kullanın, örneğin:
$input_text = trim($_GET["input_text"]); $input_text = htmlözel karakterler($input_text); $input_text = mysql_escape_string($input_text);

Filtreleme. Hata #3.
Veritabanını aramakla ilgili.
Sayılara göre arama yapmak için ilk hatada açıklanan filtrelemeyi kullanın.
Metne göre arama yapmak için, ikinci hatada açıklanan filtrelemeyi kullanın, ancak çekincelerle.
Kullanıcının mantıksal bir hata yapmasını engellemek için özelden kaldırmanız veya kaçmanız gerekir. SQL karakterleri.
Eklemeden örnek. satır işleme:
$input_text = htmlspecialchars($_GET["input_text"]); // Ara: "%" $input_text = mysql_escape_string($input_text);
Sonuç olarak şöyle bir sorgu alıyoruz:
... WHERE text_row LIKE "%".$input_text."%" ... // WHERE text_row LIKE "%%%"
Bu, tabandaki yükü önemli ölçüde artıracaktır.
Komut dosyamda, istemediğim karakterleri aramadan kaldıran bir işlev kullanıyorum:
function strip_data($metin) ( $tırnak = dizi ("\x27", "\x22", "\x60", "\t", "\n", "\r", "*", "%", "<", ">", "?", "!"); $goodquotes = dizi ("-", "+", "#"); $repquotes = dizi ("\-", "\+", "\#"); $metin = trim(strip_tags($metin)); $metin = str_replace($tırnaklar, "", $metin); $metin = str_replace($iyiteklifler, $teklifler, $metin); $metin = ereg_replace(" +" , " ", $metin); dönüş $metin; )
Tabii ki, yukarıdaki simgelerin tümü tehlikeli değildir, ancak benim durumumda bunlara gerek yoktur, bu yüzden bir arama yapıyorum ve değiştiriyorum.
Filtreleme kullanımına bir örnek:
$input_text = strip_data($_GET["input_text"]); $input_text = htmlözel karakterler($input_text); $input_text = mysql_escape_string($input_text);
Ayrıca aramadaki karakter sayısına en az 3'ten az olmamak üzere bir sınır koymanızı tavsiye ederim, çünkü. veritabanında çok sayıda kaydınız varsa, 1-2 karakter aramak veritabanı üzerindeki yükü önemli ölçüde artıracaktır.
Filtreleme. Hata #4.
Değişken değerler filtrelenmez $_ÇEREZ. Bazı insanlar bu değişken formdan geçirilemeyeceği için bunun bir güvenlik garantisi olduğunu düşünüyor.
Bu değişken, sitenin çerezlerini düzenleyerek herhangi bir tarayıcı tarafından taklit edilmesi çok kolaydır.
Örneğin, iyi bilinen bir CMS'de kullanılan site şablonunun kontrolü vardı:
if (@is_dir (MAIN_DIR . "/template/" . $_COOKIE["skin"]))( $config["skin"] = $_COOKIE["skin"]; ) $tpl->dir = MAIN_DIR . "/şablon/" . $config["cilt"];
Bu durumda değişkenin değerini değiştirebilirsiniz. $_COOKIE["cilt"] ve bunun sonucunda site klasörünün mutlak yolunu göreceğiniz bir hata oluşturun.
Veritabanına kaydetmek için çerezlerin değerini kullanırsanız, yukarıda açıklanan filtrelemelerden birini kullanın, aynısı değişken için de geçerlidir. $_SERVER.
Filtreleme. Hata #5.
Direktif dahil register_globals. Açıksa kapattığınızdan emin olun.
Bazı durumlarda, geçirilmemesi gereken bir değişkenin değerini iletebilirsiniz, örneğin, sitede gruplar varsa, o zaman grup 2 için $group değişkeni boş veya 0'a eşit olmalıdır, ancak sahte olması yeterlidir. kodu ekleyerek formu:

PHP betiğinde değişken $grup komut dosyasında varsayılan bir değerle bildirilmemişse 5'e eşit olacaktır.
Filtreleme. Hata #6.
İndirilen dosyaları kontrol edin.
Aşağıdakileri kontrol edin:
  1. Dosya uzantısı. Uzantıları olan dosyaların yüklenmesini devre dışı bırakmanız önerilir: php, php3, php4, php5, vb.
  2. Dosya, move_uploaded_file sunucusuna yüklendi mi?
  3. Dosya boyutu
muayene 1 numaralı hata.
Bir AJAX isteği için (örneğin: itibarı artırmak) bir kullanıcı adı veya kimliğin (itibarın artırıldığı) iletildiği, ancak PHP'nin böyle bir kullanıcının varlığını kontrol etmediği durumlarla karşılaştım.
Örneğin:
$user_id = intval($_REQUEST["user_id"]); ... REPLOG'A EKLE SET uid = "($user_id)", artı = "1" ... ... Kullanıcıları GÜNCELLE SET itibarı = itibar+1 WHERE user_id = "($user_id)" ...
Görünüşe göre veritabanında bizim için tamamen yararsız olan bir kayıt oluşturuyoruz.
muayene Hata #2.
Verilerle çeşitli işlemler (ekleme, düzenleme, silme) gerçekleştirirken, kullanıcının bu işleve erişim haklarını kontrol etmeyi ve Ek özellikler (html kullanımı etiketler veya doğrulama olmadan materyal yayınlama yeteneği).

Uzun bir süre, herhangi bir kullanıcının yönetim mesajını düzenleyebildiği bir forum modülünde benzer bir hatayı düzelttim.

muayene Hata #3.
Çoklu kullanırken php dosyaları basit bir kontrol yapın.
Dosyada index.php(veya başka bir ana dosyaya) diğer php dosyalarını eklemeden önce bu satırı yazın:
define("READFILE", true);
Diğer php dosyalarının başına şunu yazın:
tanımlı ("READFILE")) ( çıkış ("Hata, dosyanın yanlış yolu.
Ana sayfaya git."); }
Bu, dosyalara erişimi kısıtlayacaktır.
muayene Hata #4.
Kullanıcılar için karma kullanın. Bu, belirli bir işlevin XSS tarafından çağrılmasını önlemeye yardımcı olacaktır.
Kullanıcılar için bir karma derleme örneği:
$secret_key = md5(strtolower("http://site.ru/" . $üye["isim"] .sha1($parola) .date("Ymd"))); // $secret_key hash'imizdir
Ardından, tüm önemli biçimlerde, girişi kullanıcının mevcut karma değeriyle değiştirin:

Komut dosyası yürütme sırasında şunları kontrol edin:
if ($_POST["secret_key"] !== $secret_key) ( çıkış ("Hata: secret_key!"); )
muayene Hata #5.
SQL hatalarının çıktısını alırken, bilgiye erişim konusunda basit bir kısıtlama yapın. Örneğin, bir şifre belirleyin GET değişkeni:
if ($_GET["passsql"] == "password") ( ... SQL hata çıktısı... ) else ( ... Sadece hata bilgisi, detay yok... )
Bu, siteyi hacklemesine yardımcı olabilecek bilgisayar korsanı bilgilerinden gizlenecektir.
muayene Hata #5.
Dosya adlarını dışarıdan alarak dosya eklememeye çalışın.
Örneğin:
if (isset($_GET["dosya_adı"])) ( $_GET["dosya_adı"] .php"; içerir)
bir anahtar kullanın

PHP'de kullanıcının ad, yaş, e-posta vb. ekleyebileceği basit bir liste oluşturuyorum. Ayrıca bir silme seçeneği ekledim, ancak kullanıcı sil düğmesini tıkladığında bir onay mesajı eklemek istiyorum.

Google'ı denedim ama yalnızca jQuery ve JavaScript çözümleri buldum. Bunu sadece PHP ile yapmanın bir yolu var mı?

İsimYaş"; while($query2=mysql_fetch_array($query1)) ( echo " ".$query2["ad"].""; Eko " ".$query2["yaş"].""; Eko " Düzenlemek"; Eko " x"; } ?>

Sil.php

içinde

Bunu yalnızca PHP'de yapmak istiyorsanız, komut dosyanıza şöyle "adımlar" eklemeniz gerekir:

Adım1 (formu göster) -> adım2 (doğrulama sor) -> adım3 (doğrula)

Bunu yapmak için, formun içeriğini kaydetmek için oturumları ve bu adımı takip etmek için bir GET parametresini kullanabilirsiniz. Aksi takdirde en basit çözüm javascript kullanmaktır:

Eko" x"; // php içindeki js için çift tırnak kullanın!

İhtiyacın olan şey bu

while($query2=mysql_fetch_array($query1)) ( echo " ".$query2["ad"].""; Eko " ".$query2["yaş"].""; Eko " Düzenlemek"; Eko " x"; ) while($query2=mysql_fetch_array($query1)) ( echo " ".$query2["ad"].""; Eko " ".$query2["yaş"].""; Eko " Düzenlemek"; Eko " x"; }

ve bir javascript işlevi oluşturun

Fonksiyon onayıDelete(anchor) ( var conf = onayla("Bu kaydı silmek istediğinizden emin misiniz?"); if(conf) window.location=anchor.attr("href"); )

güven bana, bu iş 🙂

İletişim kutusunu tetiklemek için bir onClick olayı ekleyin ve javascript:return onaylayın("bunu silmek istediğinizden emin misiniz?");

Eko" x";

// onclick olayı ekle onclick="return deleteconfig()"

benim için çalış ama bunu değiştir:

onclick="javascript:confirmationDelete($(this));return false;"

onclick="confirmationDelete(this); false döndür;"

Aşağıda, bir onay alanı veren ve değişkeni PHP'den Javascript'e ve tekrar PHP'ye ileten yukarıdakilerin bir türevi bulunmaktadır.
Bunu, dosya listesinden bir dosyayı kaldırmak için bir radyo düğmesi seçmek için kullandım.
Javascript'te php $fileName adlı OnClick tetikleyici işlevine bakın, dosya adıyla onaylayın ve öyleyse $_GET için değişkenlerle href iletin

PHP/HTML kodu:

$dosyaAdı $dahili $boyut $modtime "; ?>

Makale kümelerle ilgili değil, çoğaltmayla parçalamayla ve hatta bulutlarla ilgili değil. Makale, kullanıcı sayısının ve isteklerinin çığ gibi büyüyebileceği oldukça güvenilir bir bilgi işlem mimarisi oluşturmakla ilgilidir. Ve web hizmetinin her isteği kabul etmesi, doğru ve sonuna kadar işlemesi (bazı bileşenlerin arızaları ve düşüşlerinden bağımsız olarak) ve müşteriye yanıt verme garantisinin olması bir işletme için kritik öneme sahiptir. Ve tabii ki, sistem yöneticileri için ekipman ve maaşlar için "alan" maliyetleri olmadan.

Başka bir deyişle, her şeyden önce düşünün - "buna ihtiyacım var mı?" Birinin ayda 100 sipariş cirosu olan konuşan hamster satan bir çevrimiçi mağazası varsa, muhtemelen hayır. Ve yüz binlerce ve milyonlarca kullanıcıyı barındırabilen, büyük miktarda hesaplama gerektiren, yüksek değerli verilerle çalışan, her iş sürecinin işlemselliğini garanti eden ve verilerin paralel işlenmesine ihtiyaç duyan bir işletme yürütmeyi planlıyorsanız, bu bu mu.

Finans sektörü, yüzbinlerce ürün yelpazesine sahip büyük online mağazalar, online müzayedeler, otel ve havayolu rezervasyon sistemleri, yeni bulut veya sosyal hizmetler, bir reklam kampanyasının başlamasından sonraki gün bir milyon kullanıcı tabanına ulaşmayı hedefliyor. web hizmetleriniz için son derece güvenilir bir sistemle potansiyel olarak ilgileniyor.

Bu materyalin kime hitap ettiği

1. Yüksek düzeyde yüklü ve hataya dayanıklı bilgi işlem hizmetleri oluşturmakla ilgilenen büyük web projelerinin geliştiricileri.

2. Kullanıcı tabanında "patlayıcı" bir büyüme bekleyen ve bilgi işlem kısmına yüksek talepler getiren yeni veya genişleyen işletmelerin sahipleri.

3. Mevcut durumdan memnun olmayan ve temel bir yeniden yapılanmayı düşünen büyük web projelerinin teknik liderleri ve yöneticileri.

“Hesaplamalar” hakkında neden bu kadar çok kelime var?

Çünkü büyük web projelerinin yakın geleceği “ Büyük veri” (“Büyük Veri”), sanallaştırma, enerji tasarrufu ve izleme ile birlikte 2011 yılında en çok konuşulan trendlerden biri olarak dikkat çeken ve 2013 yılından itibaren sektördeki yerini sağlam bir şekilde almış ve hatta akademik başarılarından biri haline gelmiş bir trenddir. büyük yabancı üniversitelerde dersler.

İş sorunlarını çözmek için işlenecek veri hacmi bugün sürekli artıyor ve gelecekte bu süreç daha da hızlanacak. On yıl önce kullanıcıya bir ürünle birlikte bir "çevrimiçi vitrin" göstermek yeterliyse, bir yıl önce kullanıcının sitedeki yolunu analiz etmek ve ona kesinlikle alakalı bir ürünü ("davranış teknolojileri" olarak adlandırılan) göstermek yeterliydi. ), bugün boy, kilo, yaş ve en sevdiğiniz köpeğin adı dahil olmak üzere kullanıcı hakkında her şeyi bilmek norm olarak kabul edilir.

Doğal rekabet yasaları, müşterilere rakiplerinizden daha fazla seçenek sunmak istiyorsanız, daha fazla veriye ve daha fazla hesaplamaya ihtiyacınız olduğunu belirtir. Ve er ya da geç, projeniz, uygun yaklaşımlar olmadan, onların içinde boğulabilir - tıpkı şimdi her yerde çeşitli projelerin boğulması gibi: biri desteğin karmaşıklığı nedeniyle, biri yalnızca kötü kod nedeniyle, biri modüllerin yüksek bağlantısı nedeniyle , güvenilmez bileşenlerin kullanımı nedeniyle biri.

Bu nedenle, bugün büyük hırslarla bir web projesi başlatan herkes, projelerini başlangıçta yalnızca bir program kodu olarak değil, aynı zamanda bir bilgi işlem ortamı olarak - kendi varoluş ve gelişme yasalarına sahip bir sistem olarak - düşünürlerse, rakiplerine göre daha fazla avantaj elde edeceklerdir. Ne kadar çok dikkat edersen bilgi işlem yönetimi başlangıçta, önümüzdeki yıllarda rakipleri geçme olasılığınız o kadar yüksek olacaktır.

Bu satırların yazarı, çok yüklü web hizmetleri oluşturmaya yönelik “klasik” modern yaklaşımın bir takım ciddi sakıncaları olduğuna ikna olmuştur. Neden görelim. İlk olarak, tipik bir modern şema düşünün:

Çok yüklü bir web hizmeti oluşturmaya yönelik klasik yaklaşım

1. Birçok sunucu rollere ayrılmıştır.

2. Sunucuların bir kısmı (Ön Uç rolü), statik kaynakları (görüntüler, CSS, JS dosyaları) döndürmek ve gelen trafiğin önünü aşağı akış düğümlerine “dağıtmak” için tasarlanmıştır. Ana yazılım genellikle Nginx'tir.

3. Aşağı akış düğümleri (Arka uç rolü) dinamik hesaplamalarla meşgul. Basitçe söylemek gerekirse, tipik bir Apache + PHP paketi olabilir.

4. Başka bir sunucu grubu, veri depolama için tasarlanmıştır. Bunlar MySQL, Memcache, Redis vb.

5. Web hizmeti kodunun kendisi (içinde bu örnek– PHP kodu), Apache + PHP'nin bulunduğu tüm düğümlere eşit olarak kopyalanır ve bir veya başka bir düğüme “düşen” istekleri eşit olarak işler.

6. Veritabanları, bir tür parçalama kullanılarak sunucu grupları üzerine “bulaştırılır” ve üzerlerindeki yük benzer şekilde dengelenir.

Dikkatli bir okuyucu, şemada DNS dengeleme ve CDN'den bahsedilmediğini fark edecektir, ancak yazar, şemayı aşırı karmaşıklaştırmamak için kasıtlı olarak atlamıştır. Bu parçaların çalışma prensibi yukarıdakine çok benzer.

Klasik yaklaşımın dezavantajları

1. Ana eğilim komplikasyondur. Projenin ömrü boyunca “klasik” şema giderek daha karmaşık hale geliyor. Her yeni sunucunun eklenmesiyle, onu trafik dağıtım şemasına girmeniz gerekir. Elbette büyük projelerde bir sunucuyu role sokmak “tek tuşla” bir işlemdir ancak yine de bu desteklenmesi gereken altyapıda bir artıştır. Ve dahası - veritabanı için mevcut kapasiteler artık yeterli olmadığında ve "yaşamanız" (hizmeti durdurmadan) gerektiğinde veritabanını yeni sunuculara aktarın veya dağıtın.

Ancak asıl sorun, trafik dağıtım kümesini artırmanın program kodunun karmaşıklığında bir azalmaya yol açmamasıdır. Kusursuz bir şekilde bir küme oluşturabilirsiniz, ancak kod aynı kalacaktır.

2. Dağıtım atomik değil. Sade bir ifadeyle, ortaya Yeni sürüm muharebe sunucularındaki proje biraz zaman alıyor. Dosyaları fiziksel olarak N-yirmi makineye indirmek, veritabanında değişiklik yapmak, önbellekleri (birçok farklı önbellek) sıfırlamak ve aynı zamanda tüm sunucularda istekleri “eski kod” ile işlemeyi bitirmek ve “ ile işlemeye başlamak” gerekir. yeni kod". Aksi takdirde, kullanıcının isteğinin bir kısmı eski şekilde, bir kısmı yeni bir şekilde işlendiğinde, bir kısmı “eski şemaya” göre, bir kısmı “yenisine göre” veri tabanına girdiğinde ve birçok küçük çakışma ortaya çıkabilir. yakında.

Bu nedenle, ideal olarak, herkes hizmeti güncelleme süresi boyunca (birkaç saniye veya onlarca saniye) duraklatmak ve ardından tekrar açmak ister. Gerçekte, saniyede en az 1000 istek akışıyla, hiç kimse küçük çarpışmaları "elle" düzenli olarak düzeltmeyi veya "yedinci nesle kadar" geriye dönük uyumluluğu destekleyen savunma programlamasıyla örtmeyi tercih ederek bunu yapmaz. Nasıl düzenli destek hakkında geriye dönük uyumluluk programcıların hayatını zorlaştırır (ve bir bütün olarak projenin maliyetini arttırır) - akıllı bir okuyucu kendi başına düşünebilir.

3. HTTP tarafından kullanılır. HTTP protokolü, ahlaki ve teknik olarak açıkça eskidir ve takip ederseniz (örneğin) mobil geliştirme- daha hafif protokollerin her yerde yerini aldığını biliyorsunuz. Ancak ana dezavantaj farklıdır: “tarayıcıdaki” HTTP protokolü döngünün tamamlanmasını gerektirir - sınırlı bir sürede yanıt gerektirir. Bu, hizmetin yanıtı kesinlikle tarayıcının izin verdiği küçük zaman aşımı süresi içinde hesaplamasını ve hazırlamasını zorunlu kılar. hizmet ise şu an aşırı yüklendi - istek sonsuza kadar kaybolacak.

Bu nedenle, "tipik web projelerinde", yalnızca mimariyi karmaşıklaştırmakla kalmayıp aynı zamanda gereksiz "boşa çekme" ile hizmeti aşırı yükleyen Uzun yoklama veya diğer bazı periyodik istekler gibi çeşitli hilelere başvururlar.

4. İstek başına komut dosyası başlatma. Bu, köklü bir geleneğe göre her isteğe yanıt olarak yeniden başlatılan HTTP ve PHP gibi betik dillerinin kullanılmasının bir sonucudur. Evet, evet, saniyede 1000 isteğin her birine yanıt olarak, PHP betiği yeniden başlayacak, tüm değişkenleri yeniden başlatacak ve veritabanıyla yeniden bağlantı kuracaktır. Pratikte, bir isteğin işlenmesi 0,005 saniye sürer ve komut dosyası 0,05 saniye düzeninde başlatılır - on kat daha uzun!

Başka bir deyişle, sunucularınızın %90'ı istemci isteklerini işlemekle meşgul değil, komut dosyalarının işe yaramaz bir şekilde başlatılmasıyla meşgul. Paraya çevirmeyi deneyin. Bu nedenle, OPcode önbelleğe alma, kalıcı veritabanı bağlantıları, Memcache veya Redis gibi yerel önbellekler gibi bu hoş olmayan etkiyi dengelemek için tasarlanmış birçok geçici çözüm icat edilmiştir.

5. Monolitik uygulama. Uygulamayı modüllere nasıl bölerseniz bölün, kodu karmaşık bir dizin yapısına ne kadar yaymaya çalışırsanız çalışın, hangi tembel otomatik yüklemeyi kullanırsanız kullanın, tek bir kriter vardır: Yüklemek için uygulamanın tamamını yüklemeniz gerekiyorsa. en az bir değişiklik, monolitik bir uygulamanız var. Başka bir şey verilmez.

Monolitik uygulamaların dezavantajları literatürde geniş bir şekilde açıklanmıştır. Bunlardan başlıcalarından kısaca bahsedilebilir: En küçük özelliğin bile bir saat içerisinde üretimde olmasını istiyorsanız, tüm üretim zincirini bir saate sığdırmanız gerekir. Problem tanımlama, uygulama, geriye dönük uyumluluk kontrolü, test yazma, dokümantasyon yazma, manuel test departmanından geçme, hataları düzeltme - hepsi bir saat içinde.

Çünkü uygulamanın tamamını her saatin tam 00 dakikasında yüklerseniz, her saatin sonunda getirmelisiniz. tüm uygulama istikrarlı bir duruma.

6. Web arayüzü arka uç tarafından oluşturulur. Tipik bir durumda, dış görünüş Proje sayfalarının (ve buna bağlı olarak HTML kodu), kural olarak, her isteğe yanıt olarak Arka Uç tarafında oluşturulur. Bu, aşırı, haksız bir kaynak ve para harcamasıdır.

7. Bölümlerin siyasi bölümü. Departman sistem yöneticileri PHP kodunun çalıştığı bir grup sunucu üzerinde gelen trafik cephesinin “bulaşmasını” sağlamaktan sorumludur. PHP kodundan programlama departmanı sorumludur. PHP kodunun belirli bir isteği işlemek için zamanı yoksa, bundan kimin sorumlu olduğu açık değildir - ya sunucuya çok fazla trafiğe “izin veren” ve aşırı yükleyen yönetici ya da olmayan bir program yazan programcı. -optimal komut dosyası. Veritabanı yavaşlamaya başlarsa, o zaman kimin aşırı olduğu da belirsizdir: zamanında "parçalamak" için farkına varmayan yönetici veya aynı zamanda çözebilecek programcı.

Örnekler size abartılı ve “gerçek hayattan değil” gibi geliyorsa, yazarın 200 sunucuda barındırılan bir projede çalıştığını ve 60 tanesinin veritabanı tarafından işgal edildiğini unutmayın. Bu projeye hizmet etmek için kaç kişinin çalıştığını hatırlamak korkutucu.

Ana dezavantaj

Yukarıdaki düşünceyi tekrarlıyoruz: Yazara göre klasik şemanın ana dezavantajı, teknik uzmanların yanlış şeyi optimize etmesidir. Gelen taleplerin önünü optimize ediyorlar, aslında özü optimize etmek yerine, onu büyük bir makine grubuna “bulaştırıyorlar” - bilgi işlem bölümünü. Ve göründüğünden çok daha kolay.

teorik ideal

Ah, keşke yapabilseydik:

1. Bir grup pahalı sunucudan kurtulun ve bir veya iki küçük grup kullanın.

2. Nginx->Apache->PHP şemasını, paraya mal olan tüketilen kaynaklar açısından vahşi “ek yükü” ile terk edin.

3. Aynı nedenle PHP betiklerini başlatmanın korkunç maliyetini ortadan kaldırın.

4. Arka uçta sayfaları “oluşturma” ihtiyacını reddedin. Bir web hizmetinin kararsız bir İnternet bağlantısıyla veya internet bağlantısı olmadan çalışabilmesi (örneğin, mobil ağ yolda).

5. HTTP zaman aşımı “döngüsü”nden kurtulun, yanıtı istemciye yalnızca yanıt hazır olduğunda ve teslimat garantisiyle iletin.

6. Projeyi küçük parçalar halinde, durmadan ve tek bir müşteri talebini kaybetmeden güncelleyin.

7. Projenin bir parçası (bazı bileşenler) "düştüyse" veya hata ayıklama amacıyla geçici olarak kapatıldıysa, kaybolan istekler konusunda endişelenmeyin.

Gerçek dışı mı? Kolayca!

Mükemmelliğe doğru ilk adımlar

Önce öğrenelim ne yapılmalı, bu nedenle tartışacağız - nasıl.

1. Tüm sistemi SOA olarak tasarlayın ESB (kurumsal mesajlaşma veri yolu) ile (servis odaklı mimari), monolitik yaklaşımı terk ederek, iş mantığının her bağımsız parçası ayrı bir “hizmet” tarafından işlenir ve birbirleriyle bağımsız bir değişim veriyolu üzerinden iletişim kurarlar.

2. Eşzamanlılığı bırakın. Örneğin, eşzamanlı bir "istek - işleme - yanıt" şemasında bu, katı sonlandırma denetimi olmayan ve kolayca kesilebilen bir HTTP döngüsüdür. Asenkron olarak üç ayrı süreç vardır: istek (gönderildi ve onaylandı), işleme (arıza durumunda yeniden deneme), yanıt teslimi (garantili).

3. Projeyi iki uygulamaya bölün- Ön Uç ve Arka Uç. Bir web hizmeti söz konusu olduğunda, ön uç (genellikle) bir JavaScript uygulamasıdır. Sonuç olarak, uygulamaların eşzamansız olarak çalışması ve birbirinden ayrılması, iki yönlü bir iletişim protokolü üzerinden mesaj alışverişi yapmasıdır.

4. HTTP'yi devre dışı bırak WebSocket lehine. WebSocket protokolü, HTTP'ye kıyasla harika bir performansa sahiptir, "zaman aşımlı döngülere" sahip değildir ve herhangi bir veriyi (ikili veriler dahil) her iki yönde de aktarmanıza izin verir.

5. Çalışan sorgular için bir "depolama" sağlayın. İstemciden talep alındığında, müşteriye “Onayla” deyin ve bu talebi kaydedin. Arka uç, önceki işleme döngüsünden kurtulur olmaz, isteği ona iletin. İstek, arka uç düğümleri arasında "yürüdüğü" sürece, onu düğüme "girdiği" andan itibaren tutun ve düğümden "çıktığı" anda gerçekleştirin. Böylece, bir düğüm "düştüğünde", sistem isteği kaybetmeyecek ve hemen işleme geri gönderecektir. İşlemin sonunda sonucu müşteriye gönderin ve müşteri “Onayla” diyene kadar saklayın.

6. Paralel hesaplamayı sağlayın iş mantığı açısından paralel olarak gerçekleştirilebilecek işlemler için, kesinlikle sıralı olarak yapılması gerekenler için sıra. Bu paragraf “Kaptan Apaçıklık” iddiasıyla değil, herhangi bir iş sürecinin çok iş parçacıklı koda “körü körüne konamayacağını” göstermek için yazılmıştır.

7. “Cinler” lehine komut dosyası oluşturmayı bırakın. Bir arka plan programı, bir kez başlayan ve daha sonra sürekli olarak bellekte "takılan" bir süreçtir. Her zaman çalıştığı için, her istekte yeniden başlatmak için zaman harcamasına gerek yoktur. İleriye baktığımızda, PHP arka plan programının (PHP'nin modern sürümlerini kullanırken) normal bir PHP betiğinden temel bir farkı olmadığını söyleyeceğim.

SOA Tasarım Yaklaşımı

SOA - Hizmet Odaklı Mimari - yeni moda değil. Yazılım geliştirmeye yönelik bu modüler yaklaşım, geçen yüzyılda IBM tarafından başlatılmıştır ve şu anda sektör liderleri tarafından, özellikle .NET ve JAVA'daki "kurumsal düzeyde" ürünlerde desteklenmekte ve teşvik edilmektedir.

Web servislerini programlamaya yönelik klasik yaklaşımda PHP dilleri ve benzeri - tasarım, modellerden, özelliklerinden ve üzerlerindeki işlemlerden başlar. Modeller gerçek dünya nesnelerini temsil eder ve işlemler nesneler üzerindeki eylemleri temsil eder. Bununla birlikte, uygulamanın gösterdiği gibi, gerçek dünya çok daha çok yönlü ve karmaşıktır ve olayların dili ve bunlara verilen tepkilerle çok daha etkili bir şekilde tanımlanır (daha fazla ayrıntı için, açıklama ve örneklerle birlikte 1593 numaralı gönderiye bakın).

Gerçek dünya, aynı anda (programlama açısından - “paralel”) ve çoğunlukla katılımımız olmadan gerçekleşen ve çeşitli reaksiyonların meydana geldiği veya oluşmadığı olaylardan oluşur. Tepkiler, sırayla, aşağıdaki olayları üretebilir. SOA, olaylarla, bunlar arasındaki ilişkilerle ve bunlara verilen tepkilerle çalışmak en uygun olduğu için “gerçek dünya programlaması” için idealdir. Ayrıca, doğru yaklaşım PHP gibi “tek iş parçacıklı” bir programlama dili kullansanız bile, mimarinin organizasyonuna ve olaylara ve bunlara verilen tepkilere paralel olarak gerçekleşecektir.

İş mantığınızda hangi olayların meydana geldiğine, birbirleriyle nasıl ilişkili olduklarına veya birbirlerini takip etmeleri gerektiğine, belirli olaylara tepki olarak hangi reaksiyonların olması gerektiğine ve bunları veya bu olayları tam olarak kimin işleyeceğine bağlı olarak bir SOA ürünü tasarlamanız gerekir. diğer olaylar. Veri modellerinin ve üzerlerindeki eylemlerin uygulanması arka planda kaybolur ("hizmet" içinde kapsüllenir) ve "hizmetler" listesi ve bunlar arasındaki etkileşim planı (hizmetler arası API) ön plana çıkarılır.

Uygulama: ilk yaklaşım

1. Bağımsız bir uygulama olarak ön uç. Herhangi bir popüler JavaScript-MVC çerçevesi, uygulama için uygundur. Ancak uygulamadan not ediyorum, “JavaScript, MVC ve framework” kelimelerini bir cümlede birleştirdiğinizde ağrımaya başlarsanız, sizin için zor olacaktır. Uygulama, tüm “ekranlarını” arka uca bakmadan çizebilmeli, kullanıcıya navigasyon (“ekranlar” arasındaki geçişleri) arka uca bakmadan da verebilmeli ve arka uç ile çift yönlü bir iletişim kanalını destekleyebilmelidir.

2. WebSocket bağlantısının arka uç için giriş noktası. NodeJS'de, uzun yoklama ve ajax için geri dönüş isteklerini de destekleyen bir dizi hazır çözüm vardır. ideolojik olarak modası geçmiş tarayıcılar. Gelecekte bu düğüme salt HTTP üzerinden de erişilebileceğini not ediyorum, eğer diğer kişilerin hizmetleriyle bazı ağ geçitleri yazmanız gerekiyorsa, ancak basitleştirmek için ayrı bir “saf HTTP” düğümü yazabilirsiniz.

Giriş noktası, ön uç uygulamayla (başka bir deyişle, tarayıcıyla) iki yönlü bir iletişim kanalı sağlayacak, ondan gelen istekleri kabul edecek ve yanıtları geri gönderecektir.

3. Çalışan istekleri sistemde saklamak. Bunun için popüler AMQP sunucusu, mesaj kuyrukları ve bunlar arasında yönlendirme sağlayan en uygun sunucudur. İstemciden bir sonraki istek gelir gelmez, onu "gelen" kuyruğa alın. Ayrıca, isteğin içeriğini analiz edecek ve onu sistem genelinde "yönlendirmeye" gönderecek (aslında onu belirli algoritmalara göre bir veya daha fazla kuyruğa aktarmak anlamına gelen) daemon tarafından bu kuyruktan çıkarılacaktır. İş mantığının kendi bölümüyle ilgilenen her arka plan programı, "kendi" gelen kuyruğundan şu veya bu mesajı alacak, işleyecek ve yanıtı "giden" kuyruğa yerleştirecektir.

Popüler RabbitMQ komisyoncusunun terminolojisinde, giden kuyruklar kavramının olmadığını not ediyorum. Mesajlar, aracının kendisinin yönlendirme kurallarına göre belirli kuyruklara aktarıldığı borsada (değişim aracı) yayınlanır. Burada şartlı anlama için öyle yazılmıştır ki, cevap doğrudan talep edene gönderilmez.

4. iblis kontrolü(denetçi). Basit bir PHP betiğini arka plan programı olarak çalıştırmak için yürütülebilir kodu while(true) (...) içine sarmanız ve yazmanız yeterlidir. Komut satırı"php your-script.php" gibi bir şey. Ancak bunun için temelde aynı şeyi yapacak, ancak aynı zamanda ortamın gerekli durumunu sağlayacak, sürecin durumunu izleyecek ve daha faydalı şeyler yapacak herhangi bir uygun süpervizör kullanmak daha iyidir.

Gerçek hayatta, PHP arka plan programı biraz daha karmaşıktır: kontrol sinyallerini ve yeniden yapılandırma mesajlarını almalı, bellek sızdırmamalı, veritabanına bağlantıları sürdürmeli (veya geri yüklemeli) - ama genel olarak bundan daha karmaşık değildir. alışık olduğunuz PHP betikleri .

Gerçeğe bir adım daha yakın: SOA'da olay odaklı yaklaşım

Modüler uygulamalar oluşturmaya yönelik bazı (yazarın görüşüne göre, modası geçmiş) yaklaşımlar, uzak bir proje bileşenindeki belirli yöntemlere veya prosedürlere doğrudan çağrı anlamına gelen RPC (Uzaktan Yordam Çağrısı) ilkesine dayanmaktadır. Bu yaklaşım, genellikle gönderen ve yürüten düğümler arasında doğrudan ve sağlam bir bağlantı anlamına geldiğinden, SOA'nın tüm avantajlarını tamamen yok eder. Karmaşık bir ürünün tasarımında ve uygulanmasında, eninde sonunda sahip olma maliyetini (üründeki düzeltmeler ve değişiklikler) belirleyecek olan mimarinin ve kodun karmaşıklığı olduğundan, gevşek bağlı bileşenler ilkesine mümkün olduğunca bağlı kalınmalıdır. lansmanından sonra).

SOA'daki olay güdümlü yaklaşım, bileşenlerin (hizmetlerin) birbirleriyle asenkron olaylar (Event kelimesinden "olaylar") göndererek iletişim kurduğunu varsayar. Olay, bir adı (adı) ve bir dizi parametresi olan bir mesajdır (örneğin, AMQP terminolojisinde). Bir olayın sisteme bir şey olduğunu söylemesi veya sisteme "soru sorması" amaçlanır. Genel durumda, olaylar adressiz “sisteme” (daha doğrusu ortak ESB veriyoluna) gönderilir - yani, belirli düğümlere veya sanatçılara teslimat için belirli niyetler olmadan.

Aksine, belirli düğümler (bileşenler, hizmetler) yanıt vermeye hazır oldukları belirli olaylar için ortak veri yolunu dinler. Bu, hizmetin bazı olayları duymaya ve uygun eylemi gerçekleştirmeye hazır olduğu anlamına gelebilir. Veya hizmetin biraz bilgisi vardır (örneğin, kullanıcılar hakkında bilgi içeren bir veritabanına sahiptir) ve bir "talebe" yanıt olarak bunları sağlamaya hazırdır. Her iki durumda da, bir olaya yanıt vermenin sonucu, diğer ilgili servisler tarafından da duyulabilen yeni bir olay (farklı bir ad ve farklı parametrelerle) oluşturmaktır.

Genelin uygun organizasyonu ile ESB lastikleri, hizmetler birbirini beklemeden olayları eşzamansız olarak gönderir ve alır. Bu, gönderen hizmetin herhangi bir sayıda olayı ESB'ye gecikme olmadan gönderebileceği ve aşağıdaki görevleri çözmeye geçebileceği anlamına gelir. Bunu, mevcut işlem döngüsünde yanıt beklemek anlamına gelen klasik HTTP ile karşılaştırın ve faydalarını göreceksiniz. Ve alıcı hizmetler, önceki olayın işlenmesinin tamamlanmasının hemen ardından, birbirinden bağımsız olarak, eşzamansız olarak yeni olayları da alacaktır.

kodda SOA olay modeli

Kısacası, kodunuza işlevli sınıflar (yöntemler) olarak değil, bu olaylara yanıt olarak meydana gelen olaylar ve eylemler olarak bakmanız gerekir. Ayrıca, eylemlerin sonuçları da olaylardır. Tartışılan mimari ile ilgili olarak, yerel olayların belirli bir PHP betiği içinde meydana gelen olaylar olduğunu ve uzak olayların bu komut dosyasına AMQP kuyruğundan gelen (veya sonuç olarak oraya gönderilen) olaylar olduğunu söyleyebiliriz. Tüm kodunuzu bu şekilde ele alırsanız, hemen şaşırtıcı ve çok önemli bir etkiye yol açacaktır:

Yerel ve uzak olaylar aynıysa, yerel ve uzak olay işleyicileri de aynıdır!

Neden bu kadar önemli? Ekibinizin programcıları, şu veya bu olayın nerede işleneceğini düşünmeden normal PHP kodu yazmaya devam ettiğinden - tam orada bu veya komşu bir PHP betiğinde veya sistemin diğer ucunda bir yerde, başka bir arka plan programında, en azından diğer programlama dilinde. Genel bir API ile bir proje yapıyorsanız, herhangi bir üçüncü taraf katılımcı kendi kodlarını etkinliklerinize "imzalayabilir" (ve bunları işleyebilir) veya tam tersi - olaylarını gerektiği gibi işlemeniz için size kendi kodlarını gönderebilir. istekler (ve Amazon gibi bir kullanım başına ödeme SAAS iş modeli kullanıyorsanız bunun için ödeme alın).

Klasik büyük web projelerinin ana dezavantajı dediğimiz şeyi hatırlayın - sürekli büyüyen karmaşıklık ve dolayısıyla sahip olma maliyeti, destek ve değişiklikler maliyeti. Olaya dayalı bir SOA mimarisi olması durumunda - karmaşıklık sürekli azalıyor, “karmaşık düğümler” kolayca bağımsız hizmetlere (bu durumda şeytanlar) bölünebildiğinden, sistemin ilkeleri değişmeden kalır ve performansı yalnızca artar.

Mevcut süreçleri kaybetmeden yeni bir sürüm dağıtın

Artık monolitik bir sisteminiz olmadığından, tüm sistemi dağıtmanıza gerek yoktur. Ayrıca, bir bileşenin (hizmet, arka plan programı) devreye alınması elbette makul sınırlar dahilinde herhangi bir zaman alabilir. Önemli olan, bileşenin dağıtım süresi (birkaç saniye veya birkaç on saniye) sırasında, tüm projenin hizmeti bir an için kesmemesidir. Nasıl yapılır?

Güncellenmesi gereken hizmeti kapatmanız yeterlidir. Kodunu ve veritabanı yapısını güncelleyin (gerekirse), ardından yeniden çalıştırın. Bu hizmete yönelik tüm mevcut istekler, hizmet gelene kadar AMQP kuyruğunda bekleyecektir. Hizmetler küçük olduğundan (iş mantığının yalnızca küçük bir bölümünü çözmek için gereken az miktarda kod) - bu, tüm monolitik bir uygulamayı dağıtmaktan çok daha hızlıdır. Ancak her durumda, hiçbir kayıp olmayacak.

Web arayüzü ile ilgili sorunlar

Hızlı, duyarlı bir web arayüzü, yüksek yüklü bir proje için ön koşuldur. Klasik uygulama yaklaşımıyla web arayüzünün genel olarak neden “yavaşlayabildiğini” görelim:

1. Arayüz, aşırı yüklenmiş ve yavaş yapan arka uçta çizilir. Sayfalar arası geçiş yavaş. AJAX ile bile bloklar çok yavaş yeniden çiziliyor.

2. Arayüzün kaynak kodu (HTML, CSS, JS) gereksizdir ve özellikle bu, kullanıcının arayüzde gezinmesi sırasında her sayfanın yüklenmesi sırasında yapılırsa, iletişim kanalları üzerinden yavaş bir şekilde iletilir.

3. Arayüz, zayıf cihazlarda (öncelikle mobil cihazlarda) yavaş olan büyük miktarda optimize edilmemiş JavaScript mantığı içerir.

Bu sorunları çözmeye çalışalım:

Hızlı ve duyarlı bir web arayüzü nasıl yapılır

1. İlk ve en önemlisi, kaynak arayüz müşteriye bir defadan fazla iletilmez. Bunu başarmanın tek medeni yolu, tam teşekküllü bir JavaScript uygulaması yapmaktır. İstemciye bir kez indirilir (bu durumda, güzel bir animasyonlu ön yükleyici gösterebilirsiniz) ve ardından hizmetle çalıştığı tüm süre boyunca müşterinin indirmeyi beklemesi gerekmez.

2. Web arayüzünün "ekranları" arasındaki tüm geçişler yapılmalıdır. bir JavaScript uygulamasının içinde, ve hiçbir durumda arka uca ayrı istekler olarak. Bunun için bir terim var, "tek sayfa web uygulaması", navigasyonun esas olarak "ekranlar" arasında geçiş yaparak yapıldığı, içerik ise adres çubuğu dinamik olarak değişir ve klasik "sayfa geçişlerinin" tam hissini yaratır.

3. Arka uca mesaj (olay) gönderme ve yanıt alma birbirinden ve kullanıcı navigasyonundan ayrılmış(eşzamansız). Yukarıda bahsedilen WebSocket, doğası gereği böyle bir uygulamayı "tavsiye eder". Tüm uzun işlemler, özellikle yapılmadığı sürece, arabirimi de engellememelidir.

Bu nedenle, kullanıcının uygulamanın ilk indirilmesi için yalnızca bir internet bağlantısına ihtiyacı vardır (birkaç saniye). Ayrıca, geçici bir bağlantı eksikliği olduğunda bile hizmetle çalışabilir (örneğin, bir mobil cihazdan metroda, şehir dışında, yurtdışında aşırı kalabalık bir otelde vb.) - uygulama istekleri kaydedecek ve deneyecektir. İnternet'te göründüğü anda onları göndermek ve böylece aynı şekilde cevaplar almak.

Doğal olarak bu, geliştiriciyi kodu optimize etme ve küçültme ihtiyacından kurtarmaz. Ancak, uygulamanın gösterdiği gibi (örneğin, Trello hizmeti), bu görev diğerlerinden daha zor değildir.

Mobil cihazlar için web hizmetleri geliştiricilerinden şüphe duyanlar için not: yazarın uygulamasına göre, 2013'te websocket aktarımındaki tek sayfalı JavaScript uygulamaları iPad'de başarıyla çalışıyor.

Kullanıcının birden fazla cihazdan çalışması

İşten, hizmetinizi işten kullanıyor masaüstü bilgisayar, eve giderken bir iPhone çıkarır ve evde tableti açar. Kullanıcı, arayüzden hizmete bir komut gönderdiyse, işleme sonuçları hakkında bir yanıt beklemektedir. İşlem (ne zaman) somut bir zaman aldıysa, yanıtın o anda kullanıcının kullandığı cihaza (punto için üzgünüm) gönderilmesi gerektiğini anlamak kolaydır. yanıt teslimi, istek sırasında değil.

Sorun, kullanıcının bunu kullanmayı bırakıp bırakmadığını (tekrar özür dilerim) kesin olarak söylemenin imkansız olmasıdır. belirli cihaz. Belki tarayıcıyı kapatmıştır. Belki pili ölmüştür. Belki bağlantısı olmayan metro tüneline gitti ve yarım dakika içinde tekrar görünecek. Çok fazla seçenek var ve yazar bilinmiyor En iyi yol tanımlar. Ancak, burada yararlı bulabileceğiniz şeyler:

1. Kullanıcının tüm cihazlarını ve her birindeki son etkinliğin zamanını (arka uçta) kaydedin.

2. Kullanıcıya bildirilmesi gereken sistem olaylarını, yalnızca aktif cihazlara iletilmesi gerekenler ve “yayın” (tüm cihazlara) iletilmesi gerekenler olarak sınıflandırın.

3. Girin ekstra katman soyutlamalar - kullanıcı için ilginç olan belirli olayları yakalayacak ve onlardan mesajlar oluşturacak bir hizmet. Böylece, işlemin başarısıyla ilgili aynı mesajı birkaç türde kolayca yayınlayabilirsiniz: mobil cihaz, biraz daha özgün - tarayıcıya, ayrıntılı bir mesaj - tarafından e-posta.

4. Her bir ayrı iletişim kanalında (web arayüzü, mobil cihaz, posta) her kullanıcıya göndermek için kuyruklar sağlayın. Standart AMQP işlevi, mesajların belirli bir süreden daha uzun süre orada kalmamaları ve sistemi tıkamamaları için mesaj sona erme zaman aşımlarında da size yardımcı olacaktır. Bir kullanıcı belirli bir kanal üzerinden çevrimiçi olduğunda, bu belirli türden yeni, bekleyen mesajlar kendilerine iletilecektir.

Yazar, aynı sistem temelinde, gecikmeli bir bildirim gönderme (belirli bir tarihten önce gönderilmeyecek) ve hatta gerçek kağıt periyodik yazışmalar (eylemler, ödemeler vb.) ), ancak bu ayrı bir makalenin konusu.

Ana şeyi vurgulayacağım: Teslim edilen mesajları Facebook veya Vkontakte'de alışık olduğunuz bir tür bildirim olarak düşünmeyin. Teslim edilen mesajlar sorgu sonuçları kullanıcı! Arayüzdeki tüm kullanıcı eylemleri, arka uca bir tür istek ima ederek, tek bir birleşik iletişim kanalı aracılığıyla tek tip bir "teslim edilen mesajlar" biçiminde yanıtlar alır. Ve sonra web uygulaması algoritması şu veya bu mesajla ne yapılması gerektiğini anlar - metin içeren bir bildirim çizin, tabloya bir satır ekleyin, arayüzde bir şeyi değiştirin, vb.

Paralel ve Sıralı Hesaplama

Yavaş bir arka ucunuz varsa, hızlı bir ön uç web arayüzü tasarlamak işe yaramaz. Hayır, bu akışlarla ilgili değil, çatallarla ilgili değil, Erlang ile ilgili değil. Her yeni başlayan/orta seviye programcı için mevcut olan olağan PHP'de kalıyoruz.

Neden paralelliğe ihtiyacımız var? Genel olarak tek iş parçacıklı dillerin eksikliklerinden bahsetmesek bile, paralellik görevin hesaplanmasını önemli ölçüde hızlandırır, bu da donanım kaynakları (donanım) gereksinimlerini önemli ölçüde azalttığı ve çalışmadan kullanıcı memnuniyetini artırdığı anlamına gelir. arayüz (sonuçları daha hızlı alırlar).

Web projenizde oldukça karmaşık herhangi bir iş sürecini ele alın ve bir adımlar zinciri olarak çizin. Sistemde istekten yanıta kadar bir dizi eylem alacaksınız. Büyük olasılıkla, önce bazı kontroller, ardından ana görevin yürütülmesi, ardından ikincil alt görevler ve son olarak sonucun çıktısı olacaktır. Yakından bakın: eylemlerden herhangi biri paralel olarak gerçekleştirilebilir mi?

Size bir örnek vereyim: Diyelim ki bir kullanıcı, tarife planına ek ücretli bir seçenek olarak dahil olan bir hizmeti satın almak istiyor. Seçenek sayısı sınırlıdır. Seçenek başarıyla açılırsa, tarayıcıda kullanıcıya bir bildirim göndermeniz, yinelenen bir e-posta göndermeniz, fatura hesabından para çekmeniz ve müşteri departmanını bilgilendirmeniz gerekir. Bir zincir çizin:

1. Sistem, seçeneği etkinleştirmek için bir istek aldı.
2. Kullanıcıya yetki veriyoruz ve tarife planını öğreniyoruz.
3. Bu seçeneğin etkinleştirilmesinin mümkün olup olmadığını kontrol ediyoruz. tarife planı kullanıcı.
4. Kullanıcının hesabında yeterli para olup olmadığını kontrol edin.
5. Bu seçeneğin diğer ayarlarla çakışıp çakışmadığını kontrol edin.
6. Her şey yolundaysa, seçeneği etkinleştirin.
7. Tarayıcıya bir bildirim gönderiyoruz.
8. Posta ile bir bildirim göndeririz.
9. Faturada parayı yazın.
10. Müşteri departmanını bilgilendiririz.

Dikkatli bir okuyucu, eylem dizisinde hata bulabilir, ancak yazar bunun yaklaşık bir örnek olduğunu size hatırlatacaktır.

Ne görüyoruz? Tüm adımları sırayla gerçekleştirmek için hiçbir neden olmadığını unutmayın. 3,4,5'i üç iş parçacığına ve sonunda - 7,8,9,10'u dört iş parçacığına “paralelleştirmek” çok daha doğru olurdu.

Akışlar ve çatallar hakkında mı düşünüyorsunuz? Boşuna, SOA'nız var!

SOA'da paralel hesaplama nasıl yapılır

Makaleyi bu noktaya kadar kaydırmış olan okuyucular için, bunun SOA'da aynı görevi paralel hale getirmekle ilgili olmadığını açıklayacağım - bunun için, genel durumda, arka plan programını N örnekte çalıştırmak ve ilgilenmek yeterlidir. veritabanına erişim çekişmesi.

Bu örnekte, farklı servisler tarafından gerçekleştirilen ve paralel olarak yürütmek istediğimiz üç-dört-birkaç farklı görevimiz var. Bunları paralel işleme göndermek zor değildir: "kullanıcı adı kullanıcısı X seçeneğini etkinleştirebilir mi?" bir olay göndermek yeterlidir ve bu olaya abone olan tüm servisler onu yakalar, kontrollerini gerçekleştirir ve ortaya çıkan olayları gönderir.

Sorun tam olarak, devam etmek için çalışmalarının toplam sonucuna ihtiyacımız olduğunda ortaya çıkan bu olayları toplamaktır. Örneğin, yukarıdaki listede 3+4+5 sonucuna ihtiyacımız var ve 7+8+9+10 yoksayılabilir.

Aslında, işlemlerin zorunlu olarak tamamlanması için yüksek gereksinimlerle, her zinciri sonuna kadar kontrol etmeniz gerekir, ancak bunu daha sonra tartışacağız.

Doğal olarak, arka plan programımız "bekleyip bekleyecek", kaynakları tüketecek ("boşta" olarak adlandırılır), o zaman böyle yüksek yüklü bir hizmet oluşturamazsınız. Mesele şu ki, iblis diğer görevleri çözüyor ve diğer müşterilerin diğer isteklerine hizmet ederken, üç ayrı “iş parçacığı” (3,4,5) onların alt görevlerini çözüyor. Ortaya çıkan olayların keyfi bir sırada gelebileceği gerçeği de zorluklara eklenir. Ancak, tüm bunlar kolay ve basit bir şekilde çözüldü:

Yazarın bildiği kadarıyla, bugün mevcut olan kullanıma hazır AMQP uygulamalarının hiçbiri, yalnızca bir sonuç elde etmek için birkaç olayı beklemeye ve "yapıştırmaya" izin vermiyor. Bu yüzden kendin halletmek zorundasın, şöyle:

1. AMQP'ye bir olay göndermeden önce, hızlı hafıza(herhangi bir uygun bellek içi depolamayı kullanın) hizmetin almayı beklediği sonuç olaylarının adlarının yanı sıra toplamı ile gönderilmesi gereken olayın adının ("R" diyelim) bir listesi. Sonuçlar.

2. Bundan sonra hizmet, mevcut olayın işleme döngüsünü sona erdirir ve sonraki görevler için serbest bırakılır.

3. Listeden hafızaya kaydettiğimiz herhangi bir olay gelir gelmez, servis onu “paketini açar” ve içeriğini olay adına atfederek hafızada saklar. Aynı zamanda, bu listede yanıt alınamayan başka olaylar olup olmadığını kontrol eder. Eğer öyleyse, döngü o noktada sona erer.

4. Aksi takdirde, yani listedeki tüm olaylar bir yanıt alırsa, servis bunları birbirine yapıştırır ve yapıştırılmış sonucu daha önce hatırlanan “R” olayı adı altında gönderir. Bundan sonra, hafızadan tasarruf etmek için sonuçları içeren liste hafızadan silinir - artık gerekli değildir.

5. Aynı hizmet veya bir başkası (sistem tasarımcısının takdirine bağlı olarak), paralel işlemenin tüm sonuçlarıyla birlikte ortaya çıkan “R” olayını alır. Bundan sonrası belli.

Açıklamadan size bunun uzun bir zaman olduğu anlaşılıyorsa, o zaman açıklayacağım - saniyede binlerce ve on binlerce olaydan bahsediyoruz (!) Ortalama bir sunucuda.

Bellek içi depolama kullanımı, hizmet dursa (düşme, güncelleme) bile mevcut iş sürecinin kaybolmayacağını ima eder. Servis yeniden başlatıldıktan sonra ESB'den olayları almaya ve yukarıda açıklanan algoritmaya göre işlemeye devam edecektir.

SOA'da işlemsellik, işlemlerin geri alınması (geri alma) ve başarısızlık senaryoları

SOA'da, akran olayları ESB'de "yürüdüğü" için, "buradaki bu yanıtın" "oradaki istek" anlamına geldiğini belirtmek için bir tür göstergeye ihtiyacınız vardır. Burada tekerleği yeniden icat etmeye gerek yok - herhangi bir popüler protokolün teknik özelliklerinde, korelasyon_id gibi bir ada sahip bir parametre bulacaksınız. Tipik olarak, bu bir dizedir. Bu iş sürecine ait mesaj zincirini tanımlamak için girişten çıkışa kadar her bir bireysel iş sürecinin tüm olaylarının parametrelerinde yer almalıdır. Web arayüzünün yanından bakıldığında, web uygulaması mevcut aktif (gönderilmiş) istekleri saklar ve korelasyon_id ile her bir özel yanıtın hangi isteğe yanıt geldiğini "anlar".

Terminolojiyi ele alalım: işlemsellik, bir sistemin, mantıklı olan ve yalnızca tamamen tamamlanabilen ortak bir işlem olarak birkaç eylemi gerçekleştirme özelliğidir. Paralel iş parçacıklı dağıtılmış bir sistemde birkaç işlemi atomik olarak gerçekleştirmek fiziksel olarak imkansız olduğundan, sistem sözde başarısızlık senaryoları ve geri dönüşler sağlar.

Başarısızlık senaryosu, genellikle bir hata oluştuğunda yapılacak bir eylemdir. Geri alma, bu bağlamda, sonuçta bir hataya yol açan bir dizi önceki eylemi "geri almak" için gerçekleştirilmesi gereken bir eylemler komut dosyasıdır. Kabaca söylemek gerekirse, geri almalar, sistemdeki normal iş süreçlerinin tersi olan süreçlerdir.

Geri dönüşler her zaman gerekli değildir ve her zaman mümkün değildir. Örneğin, kullanıcıya bir seçenek bağladıysanız ve ardından faturalandırmaya "düştüyseniz", seçenek tekrar devre dışı bırakılabilir. Peki, o zaman otomatik olarak veya kullanıcıdan ikinci bir komutla tekrar deneyebilirsiniz. Ve içeriği fiziksel olarak sildiyseniz ve sonraki işlemlerden bazıları işe yaramadıysa ... Durum belirsiz.

Bu nedenle, başarısızlık senaryolarına ve geri dönüşlere akıllıca yaklaşılması gerekir. Yazar aşağıdaki yolu önerebilir: Her zaman başarısız komut dosyaları yazın, ancak her zaman geri alma yazmayın. Hatalarınız anında izlemeye yansıyacak - ve destek ekibi durumu elleriyle hızlı bir şekilde düzeltebilecek, deneyim kazanabilecek ve programcılar için teknik gereksinimleri formüle edebilecek. Sıfırdan benzersiz bir şekilde doğru bir geri alma yazarken çok, çok zor olabilir.

Bununla birlikte, geri almaların, geri almaların, hatalı durumların ele alınmasının diğer her şeyle aynı iş süreçleri olduğu anlaşılmalıdır. Ayrıca sistemden geçen olaylara da dayanırlar. Belirli görevlerde uygulamak için her olayda ve her işleyicide başlangıçta iki zıt yol (ileri ve geri, başarılı ve başarısız) belirlemeniz iyi olur.

SOA ölçeklendirme

Herhangi bir sistemin er ya da geç genişletilmesi gerekecektir. SOA durumunda, bu kolay ve doğal bir şekilde yapılır:

1. Yinelenen Giriş Noktası. Bu, makalenin başında düşündüğümüz aynı WebSocket ağ geçidini ifade eder. Sınırsız sayıda çoğaltılabilir, çünkü onunla müşteri arasındaki iletişim birleşiktir ve sistemin iç kısımlarından ayrılır ve onunla sistem arasındaki iletişim de müşteriyle olan iletişimden ayrılır.

2. Yinelenen Örnekler(örnekler) hizmetler. Bir veritabanı gerektirmeyen veya yalnızca onlardan "okunan" hizmetler sorunsuz bir şekilde çoğaltılır. Ve RabbitMQ'nun normal işlevselliği, mesajları bir veya başka bir durumda rastgele gelecek olan N örneğini aynı kuyruğa abone olmanıza izin verecektir. Harici uygulamalarla (veritabanları, üçüncü taraf yazılımlar) ilgilenen hizmetleri çoğaltırken, bu uygulamaların birkaç paralel istemciden işlem isteklerini nasıl sağladığını göz önünde bulundurmanız gerekir.

3. Veri depolarının çoğaltılması. Burada, bildiğiniz herhangi bir parçalamayı kullanmakta özgürsünüz. 10 milyon kullanıcıyla uğraşıyorsanız ve bu size çok fazla görünüyorsa, 10 milyon tabana bölün (örneğin, kullanıcının oturum açmasındaki CRC32'ye veya başka bir döngüsel deneme yöntemine göre). Bir hizmetin veritabanı sürekli büyüyor ve daha karmaşık hale geliyorsa, onu iki hizmete bölün.

4. Bir AMQP Aracısını Çoğaltma ve bellek içi depolama. Yazarın uygulamasından RabbitMQ ve Redis rollerini mükemmel bir şekilde yerine getiriyor. Birden fazla DC rafında ekipmanınız varsa, ağ bağlantısı hatalarına toleranslı bir tavşan modu seçin.

5. Tam makine çoğaltma. İTİBAREN modern teknolojiler sanallaştırma (KVM) ve konfigürasyon (Chef), “aynı makineyi yükseltme” görevi tek bir tuşa basmaya geliyor.

Ön uç ve arka uç arasındaki trafiğin şifrelenmesi

SSL üzerinden bir WebSocket bağlantısının düzenlenmesi önerilir. Bunun da ötesinde, HTTP[S] dışında "herhangi bir tuhaf trafiği" engelleyen geriye dönük ofis sağlayıcılarına karşı "sızmayı" artırır.

Ancak, bu size yetersiz geliyorsa, her istemci oturum açma işleminde (bir çift ön uçta, ikincisi arka uçta) anahtar çiftlerinin oluşturulmasını düzenleyebilir, ortak anahtarları değiştirebilir ve tüm trafiği normal RSA ile daha da şifreleyebilirsiniz.

DDOS ve benzeri suistimallere karşı koruma

Konu SYN taşması ve yüzlerce gigabit ile kanal taşması söz konusu olduğunda, yazar kasıtlı olarak "düşük seviyeli" koruma sorusunu atlıyor, çünkü bu konuda yüzlerce özel literatür kitabı yazıldı. Saldırgan sisteminizi binlerce olayla (SOA + ESB) “floklamanın” bir yolunu bulduğunda, zaten içeride olan sistemi mantıksal seviyelerinde nasıl koruyacağınız hakkında konuşalım.

1. İlk kural: Geçerliliği onaylanana kadar hiçbir şey işlenmemelidir. Giriş olarak BASE64'e sarılmış küçük bir JSON metni bekliyorsanız, bir megabayttan daha uzun gelen dize açıkça atılmalıdır - paketi açmaya çalışmayın. "Latin olmayan" karakterler içeren bir dize benzerdir. Dizeyi açtığınızda, hemen json_decode yapmaya çalışmayın, önce parantezlerin sayısını ve paritesini kontrol edin. Ve benzeri.

Paranoya gibi görünüyor, ancak aksi takdirde kolayca “bellekten dolabilir”, yani hizmetin başarısız olmasına neden olarak, mevcut tüm RAM'i almaya zorlayabilirsiniz.

2. Gelen mesajları işleyen servis, belleğe, veri tabanına ve diğer depolara herhangi bir şey yazmamalıdır. Nedeni aynı. Önce mesajın bir bütün olarak geçerli olduğundan emin olun ve ancak o zaman sistemin "daha derinlerine" girmesine izin verin.

3. "Veritabanına gitmeye" zorlanabilecek bir hizmet, genellikle önbelleğe alma ile korunmalıdır. Basit bir örnek, bir kullanıcı yetkilendirme hizmetidir. Korumalı değilse, bir saldırgan art arda binlerce yetkilendirme isteği gönderebilir ve böylece veritabanını aşırı yükleyebilir.

4. Sisteme girişte, “şüpheli” kaynaklardan, örneğin “kara listeden” IP adreslerinden gelen talepleri reddeden bir hizmete ihtiyacınız var.

Kulağa klasik tavsiye gibi geliyor, peki sorun ne? Asıl sorun şu ki, sisteme girişe bir analizör-filtreleme hizmeti koyarsak (klasik web projelerinde olduğu gibi), tüm sistemin performansı bu analizör-filtrenin performansından daha yüksek olmayacaktır. Her bir raporu “gerçek zamanlı” şüphe açısından incelemek son derece pahalıdır. Bunu olaydan sonra yapabilirsin ve yapmalısın ve işte nasıl:

1. Sistemdeki tüm mesajları “dinleyecek” bir servis yapın. RabbitMQ'da bu, "#" yönlendirme anahtarına abone olunarak elde edilir.

2. Bu hizmete "şüpheli" göndericileri teşhis edebileceği belirli kuralları öğretin. Örneğin, bir zaman aralığında çok fazla benzer mesaj gönderen veya tekrarlayan mesaj gönderen veya aynı kullanıcı adına farklı IP adreslerinden mesaj gönderen gönderenler... Çok fazla seçenek var, aç. hayal gücü. Aynı zamanda, böyle bir hizmetin ne kadar hızlı çalıştığı önemli değildir (elbette makul sınırlar dahilinde) - tüm sistemin hızını etkilemez.

3. Hizmet, şu veya bu gönderenin şüpheli olduğuna karar verir vermez bu olayı sisteme gönderir ve işine devam eder.

4. Girişe çok basit ve hızlı bir arka plan programı koyun - görevi şüpheli göndericileri "aptalca" engellemek olan bir filtreleme hizmeti. Analiz yok, ayrıştırma yok, ekstra maliyet yok. Kimin şüpheli olduğunu tahmin etmek kolaydır: hizmet, önceki paragrafta açıklanan olaylardan onlar hakkında bilgi edinecek ve bunları dahili kara listesine ekleyecektir.

İlk bölümün sonu. Devam: SOA: dağıtılmış mimari ve bakımı.

Bilişim projelerinde mentorluk yapıyorum. Bu, bir sahip veya lider iseniz, yeni zirvelere çıkmanıza yardımcı olabileceğim anlamına gelir. Süreçleri temizleyin, ekibin motivasyonunu anlayın, araçları uygulayın ve belirli hedeflere ulaşın. Nasıl iş yapılacağını öğretmiyorum, sadece yolunuzdaki cömertçe dağılmış tırmıktan kurtulmanıza yardımcı oluyorum. .