Python Video Akışı ile Pornoyu Nasıl 20 Kat Daha Verimli Yaptım

Yayınlanan: 2022-03-11

giriş

Porno büyük bir endüstridir. İnternette en büyük oyuncularının trafiğine rakip olabilecek çok fazla site yok.

Ve bu muazzam trafikte hokkabazlık yapmak zor. İşleri daha da zorlaştırmak için, porno sitelerinden sunulan içeriğin çoğu, basit statik video içeriğinden ziyade düşük gecikmeli canlı video akışlarından oluşur. Ancak ilgili tüm zorluklar için, onları üstlenen python geliştiricileri hakkında nadiren okudum. Bu yüzden iş hakkındaki kendi deneyimlerimi yazmaya karar verdim.

Sorun ne?

Birkaç yıl önce, yalnızca porno endüstrisinde değil, dünyada (o sırada) en çok ziyaret edilen 26. web sitesinde çalışıyordum: dünya.

O sırada site, Gerçek Zamanlı Mesajlaşma protokolü (RTMP) ile porno video akış istekleri sunuyordu. Daha spesifik olarak, kullanıcılara canlı akışlar sağlamak için Adobe tarafından oluşturulan bir Flash Media Server (FMS) çözümü kullandı. Temel süreç şu şekildeydi:

  1. Kullanıcı, bazı canlı akışlara erişim istiyor
  2. Sunucu, istenen görüntüyü oynatan bir RTMP oturumuyla yanıt verir

Birkaç nedenden dolayı FMS, her ikisinin de satın alınmasını içeren maliyetlerinden başlayarak bizim için iyi bir seçim değildi:

  1. FMS çalıştırdığımız her makine için Windows lisansları.
  2. Ölçeğimiz nedeniyle birkaç yüz (ve her gün daha fazla) satın almak zorunda kaldığımız yaklaşık 4 bin dolarlık FMS'ye özel lisanslar.

Bütün bu ücretler artmaya başladı. Maliyet bir yana, FMS, özellikle işlevselliği açısından eksik bir üründü (bundan biraz daha fazlası). Bu yüzden FMS'yi hurdaya çıkarmaya ve sıfırdan kendi Python RTMP ayrıştırıcımı yazmaya karar verdim.

Sonunda hizmetimizi kabaca 20 kat daha verimli hale getirmeyi başardım.

Başlarken

İlgili iki temel sorun vardı: birincisi, RTMP ve diğer Adobe protokolleri ve biçimleri açık değildi (yani herkese açıktı), bu da onlarla çalışmayı zorlaştırıyordu. Hakkında hiçbir şey bilmediğiniz bir biçimdeki dosyaları nasıl tersine çevirebilir veya ayrıştırabilirsiniz? Neyse ki, çalışmamızı temel aldığımız kamusal alanda (Adobe tarafından değil, daha çok OS Flash adlı bir grup tarafından üretilmedi, artık feshedildi) mevcut bazı tersine çevirme çabaları vardı.

Not: Adobe daha sonra, Adobe tarafından üretilmeyen tersine çevirme wiki'sinde ve belgelerinde zaten açıklanandan daha fazla bilgi içermeyen "özellikler" yayınladı. (Adobe'un) spesifikasyonları saçma bir şekilde düşük kalitedeydi ve kitaplıklarını fiilen kullanmayı neredeyse imkansız hale getiriyordu. Ayrıca, protokolün kendisi bazen kasıtlı olarak yanıltıcı görünüyordu. Örneğin:

  1. 29 bit tamsayılar kullandılar.
  2. Küçük endian olan belirli (henüz işaretlenmemiş) bir alan dışında, her yerde büyük endian biçimlendirmesine sahip protokol başlıkları içeriyorlardı.
  3. 9k video karelerini taşırken hesaplama gücü pahasına verileri daha az alana sıkıştırdılar; bu, bir seferde bit veya bayt geri kazandıkları için çok az veya hiç anlam ifade etmiyordu - böyle bir dosya boyutu için önemsiz kazançlar.

İkincisi: RTMP, yüksek düzeyde oturum yönelimlidir, bu da gelen bir akışı çok noktaya yayınlamayı neredeyse imkansız hale getirir. İdeal olarak, birden fazla kullanıcı aynı canlı akışı izlemek isterse, onları yalnızca o akışın yayınlandığı tek bir oturuma geri gönderebiliriz (bu, çok noktaya yayın video akışı olacaktır). Ancak RTMP ile, erişim isteyen her kullanıcı için akışın tamamen yeni bir örneğini oluşturmamız gerekiyordu. Bu tam bir israftı.

Çok noktaya yayın video akışı çözümü ile FMS akış sorunu arasındaki farkı gösteren üç kullanıcı.

Çok Noktaya Yayın Video Akışı Çözümüm

Bunu göz önünde bulundurarak, tipik yanıt akışını FLV 'etiketlerine' yeniden paketlemeye/ayrıştırmaya karar verdim ('etiket' yalnızca bazı video, ses veya meta verilerdir). Bu FLV etiketleri, RTMP içinde çok az sorunla dolaşabilir.

Böyle bir yaklaşımın faydaları:

  1. Bir akışı yalnızca bir kez yeniden paketlememiz gerekiyordu (yeniden paketleme, yukarıda belirtilen spesifikasyonların ve protokol tuhaflıklarının olmaması nedeniyle bir kabustu).
  2. İstemciler arasında çok az sorunla herhangi bir akışı, onlara yalnızca bir FLV başlığı sağlayarak yeniden kullanabilirken, FLV etiketlerine yönelik bir dahili işaretçi (akışın neresinde olduklarını belirtmek için bir tür ofset ile birlikte) erişime izin verdi. içerik.

Geliştirmeye o zamanlar en iyi bildiğim dilde başladım: C. Zamanla bu seçim hantallaştı; bu yüzden C kodumu taşırken Python'un temellerini öğrenmeye başladım. Geliştirme süreci hızlandı, ancak birkaç demodan sonra hızlı bir şekilde kaynakları tüketme sorunuyla karşılaştım. Python'un soket kullanımı, bu tür durumları ele almak için tasarlanmamıştır: özellikle Python'da, kendimizi birden çok sistem çağrısı ve eylem başına bağlam anahtarları yaparken bulduk ve büyük miktarda ek yük ekledik.

Video Akış Performansını İyileştirme: Python, RTMP ve C'yi Karıştırma

Kodun profilini çıkardıktan sonra, performans açısından kritik işlevleri tamamen C ile yazılmış bir Python modülüne taşımayı seçtim. Bu oldukça düşük seviyeli bir şeydi: özellikle logaritmik bir büyüme düzeni sağlamak için çekirdeğin epoll mekanizmasını kullanıyordu. .

Asenkron soket programlamada, belirli bir soketin okunabilir/yazılabilir/hata dolu olup olmadığı konusunda size bilgi verebilecek olanaklar vardır. Geçmişte, geliştiriciler bu bilgiyi almak için select() sistem çağrısını kullandılar ve bu da kötü bir şekilde ölçekleniyordu. Poll(), seçimin daha iyi bir versiyonudur, ancak yine de her aramada bir sürü soket tanımlayıcısı iletmeniz gerektiği için o kadar da iyi değildir.

Epoll harikadır, çünkü tek yapmanız gereken bir soketi kaydetmektir ve sistem tüm cesur ayrıntıları dahili olarak ele alarak bu farklı soketi hatırlayacaktır. Bu nedenle, her bir çağrıda tartışma geçen bir ek yük yoktur. Ayrıca çok daha iyi ölçeklenir ve yalnızca önemsediğiniz soketleri döndürür; bu, diğer çözümleri kullanıyorsanız yapmanız gereken bit maskeli olayları olup olmadığını görmek için 100k soket tanımlayıcılarından oluşan bir listeyi gözden geçirmekten çok daha iyidir.

Ancak performans artışı için bir bedel ödedik: bu yaklaşım öncekinden tamamen farklı bir tasarım modeli izledi. Sitenin önceki yaklaşımı (doğru hatırlıyorsam) alma ve göndermeyi engelleyen tek parça bir süreçti; Olay odaklı bir çözüm geliştiriyordum, bu yüzden bu yeni modele uyması için kodun geri kalanını da yeniden düzenlemem gerekiyordu.

Spesifik olarak, yeni yaklaşımımızda, alma ve göndermeyi aşağıdaki gibi işleyen bir ana döngümüz vardı:

Python video akışı çözümü, RTMP, FLV 'etiketleri' ve çok noktaya yayın video akışının bir kombinasyonunu kullandı.

  1. Alınan veriler (mesaj olarak) RTMP katmanına iletildi.
  2. RTMP diseke edildi ve FLV etiketleri çıkarıldı.
  3. FLV verileri, akışları düzenleyen ve gönderenin düşük seviyeli arabelleklerini dolduran arabelleğe alma ve çok noktaya yayın katmanına gönderildi.
  4. Gönderici, her müşteri için son gönderilen indeksi olan bir yapı tuttu ve müşteriye mümkün olduğunca fazla veri göndermeye çalıştı.

Bu, yuvarlanan bir veri penceresiydi ve istemci almak için çok yavaş olduğunda çerçeveleri bırakmak için bazı buluşsal yöntemler içeriyordu. İşler oldukça iyi çalıştı.

Sistem düzeyinde, mimari ve donanım sorunları

Ancak başka bir sorunla karşılaştık: çekirdeğin bağlam anahtarları bir yük haline geliyordu. Sonuç olarak, anında yazmak yerine yalnızca her 100 milisaniyede bir yazmayı seçtik. Bu, daha küçük paketleri topladı ve bağlam anahtarlarının patlamasını önledi.

Belki de sunucu mimarileri alanında daha büyük bir sorun yatıyordu: Yük dengeleme ve yük devretme yeteneğine sahip bir kümeye ihtiyacımız vardı; sunucu arızaları nedeniyle kullanıcıları kaybetmek eğlenceli değil. İlk başta, belirlenmiş bir 'yönetmenin' talebi tahmin ederek yayıncı yayınlarını yaratmaya ve yok etmeye çalışacağı ayrı bir yönetmen yaklaşımıyla gittik. Bu olağanüstü bir şekilde başarısız oldu. Aslında, denediğimiz her şey oldukça başarısız oldu. Sonunda, yayıncıları kümenin düğümleri arasında rastgele paylaşarak trafiği eşitleyen nispeten kaba kuvvet yaklaşımını seçtik.

Bu işe yaradı, ancak bir dezavantajı vardı: genel durum oldukça iyi ele alınsa da, sitedeki herkes (veya orantısız sayıda kullanıcı) tek bir yayıncıyı izlediğinde korkunç bir performans gördük. İyi haber: Bu asla bir pazarlama kampanyasının dışında olmaz. Bu senaryoyu ele almak için ayrı bir küme uyguladık, ancak gerçekte, bir pazarlama çabası için ödeme yapan kullanıcının deneyimini tehlikeye atmanın anlamsız olduğunu düşündük - aslında, bu gerçekten gerçek bir senaryo değildi (ancak akla gelebilecek her şeyi ele almak güzel olurdu). durum).

Çözüm

Nihai sonuçtan bazı istatistikler: Kümedeki günlük trafik, zirvede yaklaşık 100.000 kullanıcıydı (%60 yük), ortalama olarak ~50k. İki kümeyi yönettim (HUN ve ABD); her biri yükü paylaşmak için yaklaşık 40 makine kullandı. Kümelerin toplu bant genişliği 50 Gb/sn civarındaydı ve bu değerden en yüksek yükteyken yaklaşık 10 Gb/sn'yi kullandılar. Sonunda, 10 Gbps/makineyi kolayca dışarı itmeyi başardım; teorik olarak 1 , bu sayı 30 Gbps/makine kadar yüksek olabilirdi, bu da yaklaşık 300 bin kullanıcının aynı anda bir sunucudan akış izlediği anlamına gelir.

Mevcut FMS kümesi 200'den fazla makine içeriyordu ve bunların yerini benim 15'im alabilirdi; bunlardan yalnızca 10'u herhangi bir gerçek işi yapabilirdi. Bu bize kabaca 200/10 = 20x iyileştirme sağladı.

Muhtemelen Python video akışı projesinden aldığım en büyük çıkarım, yeni bir beceri seti öğrenmek zorunda kalma ihtimalimin kendimi durdurmasına izin vermemem gerektiğiydi. Özellikle Python, kod dönüştürme ve nesne yönelimli programlama, bu çok noktaya yayın video projesini üstlenmeden önce çok alt profesyonel deneyime sahip olduğum kavramlardı.

Bu ve kendi çözümünüzü yuvarlamak büyük ödeme yapabilir.

1 Daha sonra, kodu üretime aldığımızda, düşük PCI bant genişlikleri nedeniyle 10 Gbit Ethernet kartlarını işleyemeyen eski sr2500 Intel sunucularını kullandığımız için donanım sorunlarıyla karşılaştık. Bunun yerine, bunları 1-4x1 Gbit Ethernet bağlarında kullandık (birkaç ağ arabirim kartının performansını bir sanal kartta toplayarak). Sonunda, performans sapmaları olmadan optik üzerinden 10 Gbps hizmet veren daha yeni sr2600 i7 Intel'lerden bazılarını aldık. Tüm öngörülen hesaplamalar bu donanıma ilişkindir.