StackOverflow ile ilgili ilginç bir sorunla ilgileniyoruz.
Bir sürü küçük "yakında yapılması gerekenler" görevimiz var. Bir örnek, "İlgili Sorular" listelerini güncellemektir. Geçmişte yaptığımız şey, bu görevleri bazı kullanıcıların sayfa yüklerine geri döndürmektir.
Bu asla ideal değildi, ama gerçekten farkedilmiyordu. SO 1000.000 soru işaretini geçtiğine göre, şanssız kullanıcılar bunu hissetmeye başlıyor.
Doğal çözüm, bu görevleri arka plana itmektir. Bunu yapmanın iki geniş yolu var.
Temel olarak, birkaç (- ThreadPool , IIS'ye müdahale etmemek için) iş parçacıklarını döndürüyoruz ve onlara attığımız bazı koleksiyonları hizmet ettiriyoruz Funcs içine.
Buradaki büyük profesyonel sadeliktir. Hiçbir şeyi sıralamak konusunda endişelenmemiz gerekmiyor, ayrıca bazı harici hizmetlerin hazır ve yanıt verdiğinden emin olmak zorunda da değiliz.
Ayrıca tüm ortak kodumuza erişebiliriz.
Con, arka plan iş parçacıkları kullanmamamız gerektiğidir. Bildiğim itirazlar açlıktan IIS (ThreadPool kullanıyorsanız) ve iş parçacıkları rastgele (AppPool geri dönüşümü nedeniyle) ölüyor.
Rastgele iş parçacığı ölümünü sorun olmayan bir hale getirmek için mevcut altyapımız var (bir görevi saptamak mümkün, temelde) ve iş parçacıklarının sayısını sınırlamak (ve ThreadPool olmayan iş parçacıklarını kullanmak) da zor değil.
Burada gerçekten ele alınmadığı için = StackOverflow'a taşındı .
Bazı üçüncü taraf çözümleri veya özel çözümler.
Temel olarak, bir süreç için süreç sınırı boyunca bir görevi marshal eder ve unuturuz. Muhtemelen bazı kodları bağlıyoruz veya ham SQL + bir bağlantı dizesiyle sınırlıyız.
Pro onun bunu yapmak için "doğru yolu" olmasıdır.
Eksileri ya yapabileceğimiz konusunda çok kısıtlı olduğumuz ya da bu hizmeti kod tabanımızla senkronize tutmak için bir sistem üzerinde çalışmamız gerekecek. Ayrıca "IIS'de" seçeneğiyle ücretsiz olarak elde ettiğimiz tüm izleme ve hata kayıtlarımızı bir şekilde bağlamamız gerekecek.
Hizmet yaklaşımının başka yararları veya sorunları var mı?
Özetle, 1 numaralı yaklaşımı işe yaramaz hale getiren öngörülemeyen ve aşılmaz sorunlar var mı ve eğer 2 numaralı yaklaşım için bakmamız gereken iyi bir üçüncü taraf hizmeti var mı?
Birkaç hafta önce bir benzer sor SO sordum. Bir somun Shell'de, bir süredir yaklaşımım bir Windows Hizmeti geliştirmek oldu. Benim web app hizmetime marshal istekleri NServiceBus (esas olarak kapaklar altında MSMQ) kullanabilirsiniz. WCF kullanıyordum ama WCF üzerinde düzgün çalışmak için dağıtılmış bir işlem almak her zaman eşek bir ağrı gibi görünüyordu. NServiceBus hile yaptı, veriyi işleyebilir ve bir işlemde görevler oluşturabilirim ve hizmetimin o sırada çalışıp çalışmadığından endişe etmem. Basit bir örnek olarak, bir e-posta (örneğin bir kayıt e-postası) göndermem gerekirse, kullanıcı hesabını oluşturur ve bir işlemde (e-postayı göndermek için) Windows Hizmetime bir sinyal gönderirim. Servis tarafındaki mesaj işleyici mesajı alır ve buna göre işlem yapar.
ASP .NET 4.0 ve AppFabric piyasaya sürüldüğünden, yukarıdaki mekanizmaya birkaç alternatif alternatif vardır. Yukarıda bahsettiğim soruya geri dönersek, artık AppFabric'in AppInitialize'ına (net.pipe aracılığıyla) ve ASP .NET 4.0'ın web uygulamaları olarak Windows Hizmetlerini geliştirmeyi uygun bir alternatif haline getiren Otomatik Başlatma özelliğine sahibiz . Bunu birkaç nedenden dolayı yapmaya başladım (en büyük olanı artık kıçta bir acı değil):
Bu rotaya giderseniz (orijinal yazıma kopyalayıp yapıştırmak için beni affet) kesinlikle arka plan mantığını ayrı bir web uygulamasında çalıştırmayı düşünürüm. Bunun birkaç nedeni vardır:
Bunu yapmak marshaling yönüne geri döner. WCF, NServiceBus/RabbitMQ/ActiveMQ vb., Vanilya MSMQ, RESTful API (Think MVC) tüm seçeneklerdir. Windows Workflow 4.0 kullanıyorsanız, web uygulamanızın tüketebileceği bir Ana Bilgisayar uç noktasını açığa çıkarabilirsiniz.
Hizmetleri için web hosting yaklaşımı hala benim için oldukça yeni, sadece zaman doğru seçim olup olmadığını söyleyecektir. Şimdiye kadar çok iyi. Bu arada, AppFabric'i kullanmak istemiyorsanız (tuhaf bir nedenden dolayı Windows Server Web Edition desteklenmediğinden ben yapamadım), Gu'nun gönderisinde belirtilen Otomatik Başlatma özelliği güzel çalışıyor. Yine de applicationhost.config dosyasından uzak durun, bu yazıdaki her şeyin IIS konsolu (ana sunucu düzeyindeki Yapılandırma Düzenleyicisi) üzerinden kurulması mümkündür.
Not: Başlangıçta bu mesajda birkaç bağlantı daha yayınlamıştım ama ne yazık ki, bu bu alışverişe ilk yazım ve sadece bir bağlantı destekleniyor! Temelde iki kişi daha vardı, onlara Google "Windows Hizmetlerine Ölüm ... Yaşasın AppFabric!" ve "auto-start-asp-net-apps". Bunun için üzgünüm.
Aslında Windows'da arka plan hizmetlerini çalıştırmanın üçüncü bir yolu var ve UNIX dünyasında çok yaygın. Üçüncü yol, altyapınızın bir parçasını çalıştıran bir CRON
işidir. Windows'da bu task scheduler
ve kodun zamanlanmış olarak çalıştırılması için çok yaygındır. Bunu kullanmak için, önceden tanımlanmış bir programda yürütülen bir komut satırı uygulaması oluşturacaksınız. Bunun avantajı, işlem bir hizmet gibi devam ederse endişelenmenize gerek olmamasıdır, çünkü bir nedenle başarısız olursa, bir dahaki sefere başlayacaktır.
Belirli görevleri birleştirmeye gelince, gerçekten bu görevleri kalıcı bir ikili depolamada depolamanız gerekir. Komut satırı uygulaması onları depolama alanından alır ve yürütene kadar. Bu geçmişte Cassandra veritabanı Cassandra veritabanı belirli kullanıcılar için arka plan görevleri doldurmak için bir oturum devlet sağlayıcı olarak veritabanı kullanarak) yaptım komut satırı bunları seçin ve kullanıcı için çalıştırın.
Bu tipik bir marşaling çözümü olmayabilir, ama benim için çok iyi çalıştı ve çok zarif bir çözüm olduğu ortaya çıktı, çünkü planlanan görevler kapanmalardan, ağ sorunlarından kurtuldu ve herhangi bir makine merkezi olarak olduğu için görevi yürütebilirdi saklanmış.
Utanmaz tanıtım, ama bu benim projem ve kısaca ayrıntılı olarak verdiğim çözüm, projeyi neden oluşturduğum: http://github.com/managedfusion/fluentcassandra/
Bu, web grubunuzla birlikte yatay olarak ölçeklenen ve zaten bildiğiniz web teknolojisi yığını .
Şöyle çalışır:
http://mydomain.com/system/cron
.Yaşasın! Artık her 30 saniyede bir çağrılacak bir rotanız var. İsteğin işlenmesi 5 dakika sürerse, hiç kimse umursamaz, çünkü bu bir kullanıcının sayfa isteğinin bir parçası değildir.
cron
eylemi çok basit görünüyor: belirli bir frekansta yürütülecek yöntemlerin bir listesi var. Bir istek geldiğinde, yürütülmesi gereken bir yöntem olup olmadığını görür ve uygun yöntemi çağırır. Bu, veritabanınızdaki zamanlamayı, muhtemelen siteniz için çok sayıda başka önemli yapılandırma verisine sahip olabileceğiniz anlamına gelir.
Daha da önemlisi (sizin için) bu, işlerinizin sabit bir programda çağrılması gerekmediği anlamına gelir. Bir yöntemi ne zaman yürüteceğinizi belirlemek için istediğiniz mantığı yazabilirsiniz.
Not: Herhangi bir sorunuz veya endişeniz varsa, lütfen bir yorum ekleyin. Özür dilerim.
Mevcut başvurumda bunu yapmanın neredeyse her yolunu denedim ve kullandım. Şu anda yaptığınız aynı şeyi yapmaya başladım, verileri doldurmak ve daha sonra önbelleğe almak için bir kullanıcı isteğine geri piggy. Bunun da kötü bir fikir olduğunu fark ettim (özellikle birden fazla web sunucusuna ölçeklendirdiğinizde daha fazla kullanıcı isabet alıyor).
Ayrıca ASP.NET uygulamasında bir URL isabet zamanlanmış bir iş vardı - bu iyi bir çözümdür ama 1 web sunucusu ölçeklendirmek dakika kırmak başlar.
Şu anda her ikisi de harika bir küçük kütüphane olan Quartz.NET kullanarak iki farklı yöntem kullanıyorum. Birincisi, ASP.NET ile süreç içinde çalışan Quartz.NET, global.asax içinde kurulur ve her birkaç dakikada bir çalışır. ASP.NET önbellek ASP.NET bir parçası olarak çalıştırılan tek nedeni bant dışında güncelleştirmek için bunu kullanın.
İkincisi, DaemonMaster adlı Quartz.NET'i sarmak için bir kütüphane yazdım - bir DLL bir dizine bırakmayı ve bir Windows hizmetinde çalıştırmayı kolaylaştırır. Bir Windows Hizmeti ile çalışmanın can sıkıcı kısımlarından bazıları ve ayrıca Quartz.NET api'sini de temizler.DaemonMaster aracılığıyla çalışan hizmetler iki farklı aromadan oluşur, ilki her gece veya her X dakikasında çalışması gereken işlerdir. diğer işler ASP.NET uygulamasından gelen verilere dayalı olarak kuyruktan çıkar.ASP.NET uygulaması RabbitMQ üzerindeki JSON nesnelerini bırakır ve RabbitMQ hizmetleri anketini işler ve verileri işler.
Buna dayanarak bir Windows hizmeti ile gitmenizi öneririm (ve DaemonMaster'a göz atın) ve gerekirse ASP.NET uygulamasından hizmetlere veri aktarmak için RabbitMQ gibi bir kuyruk kullanın - tüm bu çözümlerden en iyi şekilde çalıştı . Önbellek yüklüyorsanız, ASP.NET'te çalıştırmanız mantıklıdır, aksi takdirde ben öyle düşünmüyorum.
Bunu doğru şekilde yapardım ve bir "kuyruğu" izleyen bir Windows Hizmeti çalıştırıyordum. "Kuyruk" diyorum çünkü MSMQ ile programlama, gözbebeklerinize sıcak pokerler yapıştırmaya benziyor.
Rails Gecikmeli :: İş basitliğine aşık oldum ve benzer bir şey kolayca .NET'te yapılabilir.
Temel olarak herhangi bir tür SomethingOperation
(Perform()
yöntemine sahip bir şey) eklersiniz. Sonra sadece ilgili parametreleri serileştirin, bir öncelik verin, bir çeşit varsayılan yeniden deneme davranışı verin ve bir veritabanına doldurun.
Hizmetiniz bunu izler ve kuyruktaki işleri çalıştırır.
Hizmet Veri Yolu/İleti Kuyruğu/Hizmet yaklaşımından oldukça memnunuz. Temel mimari budur.
Web sitesi kuyruğa mesaj gönderir
bus.Send(new ProjectApproved()); // returns immediately
Windows hizmeti mesajı kendi zamanında alır ve işler
public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
public void Consume(ProjectApproved Message)
{
// Do something "offline"
}
}
Avantajı, kullanıcıların bağlı olduğu ön uç hizmeti için herhangi bir gecikme olmamasıdır. Windows hizmeti kapatılabilir ve ana siteye kesintisiz olarak yükseltilebilir. Artı son derece hızlı.
Tüm verilerinizi mesajda saklayamıyorsanız, her zaman saklayabilir ve daha sonra alabilirsiniz. Sınıflarınızı değişiklik yapmadan saklamak çok kolay olduğu gibi bir belge depolama mekanizması kullanmanızı öneririm: RavenDB veya MongoDB .
Web sitesi kuyruğa mesaj gönderir
// Save your object
store.Save(completeProject);
// Send a message indicating its ready to be processed
bus.Send(new ProjectApproved() { ProjectId = completeProject.Id });
Windows hizmeti mesajı kendi zamanında alır ve işler
public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
public void Consume(ProjectApproved Message)
{
// Retrieve your object back
var completeProject = store.Get(Message.ProjectId);
}
}
İşleri basitleştirmek için şunu kullanıyoruz: Rhino ESB ve Topshelf . Yapılandırma son derece basittir ve bunu mevcut bir uygulama için yerine koymak çok az zaman aldı.
Bu ikisinin bir kombinasyonunun neden uygun bir seçenek olmadığını merak ediyorum. Şu anda işleri sayfa görüntülemelerinde tetikliyorsunuz, bazı şanssız özler sayfanın gelmesi için 10 saniye beklemektedir. En azından şu anki yönteminizi anlıyorum.
Ancak, site büyüdükçe bu işlerin daha uzun sürmesi ve sitedeki kullanıcı deneyiminin rayından çıkmasını istemiyorsunuz. Birkaç gün boyunca (ya da belki de çok) şanssız kullanıcılar için bile değil, bu yüzden şimdi arka planda işleri planlamayı düşünüyorsunuz.
Düzenli aralıklarla yürütülen bir arka plan işinin neden bir ziyaretçiyi taklit edemediğini anlamıyorum. Şimdi bir Windows programcısı değilim, ancak Linux dünyasında düzenli aralıklarla çalışan bir cron işi kuracaktım ve 2 satırlık bir kodu olacaktı.
#!/bin/bash
wget -O /dev/null http://stackoverflow.com/specially_crafted_url
Her iki sistemin artılarını birleştirir. Arka planda yapılır. Kullanıcıları etkilemez. Hala işi başlatmak için bir sayfa görünümü kullanıyor. Bu yaklaşımın daha önce kullanıldığını gördüm. Eski yolların basit yolları ile yoldan daha karmaşık yollar arasında orta yol olma eğilimindedir.
Güncelleme
İş sunucularını web sunucularında çalıştırarak yük dengeleme sorununu çözebileceğinizi düşünüyorum. İş koşucusu bir URL'yi iş kuyruğundan çıkarır ve şu şekilde çalıştırır:
wget -O /dev/null http://localhost/specially_crafted_url
İş/mesajlaşma kuyruklarının doğası gereği, işler iş koşucuları arasında eşit olarak dağıtılır, yani special__rafraf_url sonunda web sunucularınız arasında dağıtılır.
Ben saf hizmet yaklaşımı ile con kod servis içine dağılmış ve çekirdek app uzakta olduğunu düşünüyorum.
Kodu bir arada tutan ve hizmeti basitleştiren büyük arka plana duyarlı olmayan arka plan işleriyle yaptıklarımız:
Daha da basit, sadece bir konsol uygulamasında arama yapın ve bir "hizmet" haline getirmek için Görev Zamanlayıcı veya VisualCron kullanın.
Resque Güzel. Ya da Kthxbye Sonuçta elde edilen değer tamamlandığında size bildirilmesi gerekiyorsa.
Hem Redis/Ruby tabanlı tho.
Dürüst olmak gerekirse, hizmete dayalı bir yaklaşım yapıyorsanız, mevcut platformunuzla süper entegre olması gerekmez, ki bunun bir artı olduğunu düşünüyorum. Umarım (bir çeşit izleme ile) ve işleri tamamlayacak bir kur ve unut sistemi olabilir. Ben sadece onun veritabanı bilgileri güncelleme/değiştirme beri aynı platformda çalıştırılması gerektiğinden emin değilim.
Bu tür bir işi ayrı bir varlıkta yetiştirdiyseniz, çok daha azıyla çok daha fazla şeyden kaçabileceğinizden emin olabilirsiniz, özellikle de diş açma sorunlarıyla uğraştığınız için. Hem Resque ve Kthxbye , işletim sisteminin eşzamanlılığı işlemesine izin vermek için işlemi ayrı işlemlere taşıyın.
TopShelf'i beğendim. Basitliği korur, ancak yine de bir Windows Hizmeti olarak doğru şekilde çalışır. Temel olarak bir Konsol Uygulaması oluşturun, yaklaşık 15-20 satır kod ekleyin, ardından bir hizmet olarak yüklenir.
Web sunucusunda çalışan ve çeşitli görevlerinizi yapan bir bakım URL'sine düzenli olarak çarpan çok basit bir Windows hizmetine sahip olmaya ne dersiniz? Herhangi bir talepte ne kadar çalıştığını azaltın.
Burada görünen eğilimi yakalayacağım ve IIS modeline gitmeyi önereceğim. Kendim kullandım ve gerçekten iyi çalışıyor. Gerçekten iyi bir iş parçacığı havuzu sınıfı uygulamak o kadar da zor değil (yıllar içinde, iş parçacığı havuzu sınıfı dinamik iş parçacığı oluşturma ve imha, iş yeniden denemek ve benzeri desteklemek için genişletti). Avantajları:
Bence, bir IIS içi çözüm, işi rasgele sayfa görünümlerine bindirmekten "bir sonraki adım" dır.
Görev Sırası Java API Genel Bakış
Görev Kavramları
App Engine arka plan işlemede, görev küçük bir iş biriminin tam açıklamasıdır. Bu açıklama iki bölümden oluşur:
Çevrimdışı Web Kancaları Olarak Görevler
Neyse ki, İnternet zaten bir HTTP isteği ve yanıtı şeklinde böyle bir çözüm sunuyor. Veri yükü, web formu değişkenleri, XML, JSON veya kodlanmış ikili veriler gibi HTTP isteğinin içeriğidir. Kod başvurusu URL'nin kendisidir; asıl kod, sunucunun yanıtı hazırlarken yürüttüğü mantıktır.
Bir MSMQ Kuyruğu dinleyen bir WAS barındırılan WCF hizmeti kullanırsınız.
Pro'nun
Web uygulamasından tek yönlü mesajları tetikleyin ve unutun
MSMQ/WCF daraltma ve yeniden deneme
Garantili teslimat; D
Ölü Mektup yönetimi
Dağıtılmış işlem
WAS/MSMQ aktivasyonu
Eksileri
WCF'deki MSMQ özellikleri MSMQ kullanımını gerçekten güzel kılar. Evet, konfigürasyondan kanacaksınız, ancak faydalar fedakarlıktan ağır basacaktır.
Web uygulamaları geliştirirken buna birkaç kez rastladım. Görevi yerine getiren bir Windows konsol uygulaması oluşturarak ve görevi gerçekten yapmak için sık sık çalışan zamanlanmış bir görev oluşturarak çözüyoruz.
Rx ve aşağıdakine benzer bir şey kullanarak bir arka plan iş parçacığına (veya birçok arka plan iş parçacığı) çalışmayı şöntleyebilirsiniz:
var scheduler = new EventLoopScheduler( SchedulerThreadName );
_workToDo = new Subject<Action>();
var queueSubscription = _workToDo.ObserveOn( scheduler ).Subscribe( work => work() );
_cleanup = new CompositeDisposable( queueSubscription, scheduler );
Kullanmak:
var work = () => { ... };
_workToDo.OnNext( work ); // Can also put on error / on complete in here
Tüm bunları bir sınıfın içinde sadece bir tanesinin bulunduğu bir ev sahibine (tekil olarak adlandırın, ancak düzgün bir şekilde yapın - yaşam tarzını belirlemek için IoC kabını kullanın).
EventLoopScheduler (tek bir iş parçacığı çalıştırır) kullanmak yerine özel bir zamanlayıcı yazarak iş parçacığı havuzu vb boyutunu denetleyebilirsiniz.
İkisinide yap
Soru yoluna, şu anda kullanıcı isteklerinde piggyback yaptığınız işi yapan isteğe bağlı bir parametre ekleyin:
Büyük bir sitede arka plan görevlerine hizmet verme
Her sunucuda çalışan ve IIS günlük paylaşımlı ikili dosyayı açan ve dosyanın geçerli sonuna okuyan bir konsol uygulaması oluşturun.) Güncellemeleri toplamak için ileriye doğru okumak üzere bir dosya sistemi izleyicisi veya zamanlanmış aralık kullanın IIS günlüğü temizledi.
Görüntülenmekte olan sayfaları belirlemek için bu bilgileri kullanın.
Bir web istemcisi nesnesiyle localhost üzerindeki url'nin "extrastuff" sürümünü çağırmak için ayrıştırılan günlükteki sayfa URL'lerini kullanın.
Her günlük döneminin sonunda dosyaları değiştirmek için bazı kodlar ekleyin veya her günlük dönemindeki işlemi yeniden başlatın.
Bu tür şeyleri birkaç kez uyguladım. Windows'da, çeşitli zamanlarda bir şey yapan bir python komut satırı programı ayarladım.Bu program aynı zamanda bir bağlantı noktasında bir xmlrpc arabirimi ortaya koyar. xmlrpc arabirimlerini sorgular.Yüklenmemişlerse, bunları başlatmaya çalışırlar.
Avantajı, çalışan işin cron veya zamanlama bağlı olmamasıdır. Her saniye çalışan bir işlem işim var, ancak yapacak işi olup olmadığına bağlı olarak yeni bir işe başlama arasında daha uzun bekleyecek. Ayrıca, sonuca göre akıllıca hareket etmek için kullanılabilir. 500 hatası mı aldınız? Gerçekten uzun bir gecikmeniz mi var? Başka bir şey yap. Başka bir servise bildir. Vb.
Ve aynı sistem küçük değişiklikler ile unix üzerinde çalışır.
Senin için bir cevabım yok, ama sorun bir çan çaldı - bazı rastgele adamları hatırlıyorum bir kez bir podcast'de tartışıyor .
Spolsky: Blogda sorduğunuz sorulardan birinin, genel olarak bakım yinelenen görevleri nasıl ele almanız gerektiği olduğunu fark ettim?
Atwood: Evet.
Spolsky: Bu adil bir karakterizasyon mu? Her web sitesinde, bir web sayfasının yüklenmesi sırasında yürütmek istemediğiniz bazı görevler bulunur, ancak bir tür yineleme ile yürütmek istersiniz.
Atwood: Ya, arka plan görevleri bir şey.
Spolsky: Ya, ne buldun?
Atwood: Aslında Twitter'da sordum, çünkü sadece hafif bir şey istedim. Gerçekten bir windows servis yazmak istemiyordu. Grup kodunun dışında olduğunu hissettim. Ayrıca aslında işi yapan kod aslında bir web sayfasıdır, çünkü bana göre bir web sitesinde mantıklı bir çalışma birimi bir web sayfasıdır. Yani, gerçekten web sitesine geri dönüyoruz, tıpkı web sitesindeki başka bir istek gibi, bu yüzden onu yerinde kalması gereken bir şey olarak gördüm ve Twitter'da bana önerilen küçük yaklaşım temel olarak uygulama önbelleğine sabit bir son kullanma tarihi ile bir şey eklemekti, o zaman geri çağrınız var, böylece süresi dolduğunda işi yapan belirli bir işlevi çağırır, sonra aynı son kullanma tarihi ile önbelleğe geri eklersiniz. Birazcık, belki "getto" doğru kelimedir.