bir Akka akışını doldurmak için bir yukarı akış hizmetine geçirmek


9

Ben sonra bir geri dönün ve geri akka aracılığıyla istemciye itmek gerekir bir OutputStream, veri itmek için bir upstream hizmet (Azure Blob Service) çağırmak gerekir. Akka (ve sadece sunucu uygulaması kodu) olmadan, ben sadece ServletOutputStream olsun ve masmavi hizmetin yöntemine geçmek.

Tökezlemeye çalışabileceğim en yakın şey ve açıkça bu yanlış, böyle bir şey

        Source<ByteString, OutputStream> source = StreamConverters.asOutputStream().mapMaterializedValue(os -> {
            blobClient.download(os);
            return os;
        });

        ResponseEntity resposeEntity = HttpEntities.create(ContentTypes.APPLICATION_OCTET_STREAM, preAuthData.getFileSize(), source);

        sender().tell(new RequestResult(resposeEntity, StatusCodes.OK), self());

Fikir blobClient.download (os) çağırarak bir çıkış akışı almak için bir yukarı hizmet çağırıyorum;

Görünüşe göre lambda işlevi çağrılır ve geri döner, ancak daha sonra başarısız olur, çünkü veri veya başka bir şey yoktur. Sanki bu lambda fonksiyonunu yapmam gerekiyordu, ama belki de işi yapan bir nesneyi döndürürüm? Emin değil.

Kişi bunu nasıl yapar?


Davranışı nedir download? Verileri akışa aktarıyor osve yalnızca veri yazıldıktan sonra geri dönüyor mu?
Alec

Yanıtlar:


2

Buradaki asıl sorun, Azure API'sinin geri basınç için tasarlanmamış olmasıdır. Çıkış akışının Azure'a daha fazla veri için hazır olmadığını bildirmesi mümkün değildir. Başka bir deyişle: Azure verileri tüketebileceğinizden daha hızlı iterse, bir yerde çirkin bir arabellek taşması hatası olması gerekir.

Bu gerçeği kabul ederek, yapabileceğimiz bir sonraki en iyi şey:

  • Source.lazySourceVerileri yalnızca aşağı akış talebi olduğunda (yani kaynak çalıştırılıyor ve veriler isteniyorsa) indirmeye başlamak için kullanın .
  • downloadÇağrıyı başka bir iş parçacığına koyun, böylece kaynağın döndürülmesini engellemeden yürütmeye devam edin. Bir kez bunu yapmak için bir yoludur Future(Java en iyi uygulamaların ne olduğundan emin değilim, ama her iki şekilde de iyi çalışmalıdır). Başlangıçta önemli olmasa da, dışında bir yürütme bağlamı seçmeniz gerekebilir system.dispatcher- hepsi downloadengelleme olup olmadığına bağlıdır .

Bu Java kodu hatalı biçimlendirilmişse önceden özür dilerim - Akka'yı Scala ile kullanıyorum, bu yüzden bunların hepsi Akka Java API ve Java sözdizimi referansına bakmaktan kaynaklanıyor.

ResponseEntity responseEntity = HttpEntities.create(
  ContentTypes.APPLICATION_OCTET_STREAM,
  preAuthData.getFileSize(),

  // Wait until there is downstream demand to intialize the source...
  Source.lazySource(() -> {
    // Pre-materialize the outputstream before the source starts running
    Pair<OutputStream, Source<ByteString, NotUsed>> pair =
      StreamConverters.asOutputStream().preMaterialize(system);

    // Start writing into the download stream in a separate thread
    Futures.future(() -> { blobClient.download(pair.first()); return pair.first(); }, system.getDispatcher());

    // Return the source - it should start running since `lazySource` indicated demand
    return pair.second();
  })
);

sender().tell(new RequestResult(responseEntity, StatusCodes.OK), self());

Fantastik. çok teşekkürler. Örneğin küçük bir düzenlemesi: Futures.future (() -> {blobClient.download (pair.first ()); return pair.first ();}, system.getDispatcher ());
MeBigFatGuy

@MeBigFatGuy Doğru, teşekkürler!
Alec

1

OutputStreamBu durumda bir "değer gerçekleşmiş" dir Sourceve sadece akışı çalıştırılan bir kez oluşturulan (veya çalışan bir akışı içine "hayata") olacaktır. SourceAkka HTTP'ye teslim ettiğiniz için çalıştırmak sizin kontrolünüz dışındadır ve daha sonra kaynağınızı çalıştıracaktır.

.mapMaterializedValue(matval -> ...)genellikle somutlaştırılmış değeri dönüştürmek için kullanılır, ancak somutlaştırmanın bir parçası olarak çağrıldığından, bunu anladığınız gibi, bir mesajı mesajın gönderilmesi gibi yan etkiler yapmak için kullanabilirsiniz. korkak görünse bile. Akışın materyalizasyonunu tamamlamayacağını ve bu lambda tamamlanıncaya kadar koşmayacağını anlamak önemlidir. Bu, eğerdownload() farklı bir iş parçacığındaki bazı işleri iptal etmek ve hemen geri dönmek yerine engelliyorsa .

Ancak başka bir çözüm daha vardır: Source.preMaterialize()kaynağı gerçekleştirir ve size önceden belirlenmiş kaynağı tüketmek için kullanılabilecek maddileşmiş Pairdeğer ve yeni bir değer verir Source:

Pair<OutputStream, Source<ByteString, NotUsed>> pair = 
  StreamConverters.asOutputStream().preMaterialize(system);
OutputStream os = pair.first();
Source<ByteString, NotUsed> source = pair.second();

Kodunuzda düşünülmesi gereken birkaç ek nokta olduğunu unutmayın, en önemlisi blobClient.download(os)çağrı yapılana kadar engeller ve bunu aktörden çağırırsanız, bu durumda aktörünüzün dağıtıcıyı aç bırakmadığından ve durmadığından emin olmalısınız. uygulamanızdaki diğer aktörlerin yürütülmesini önleyin (bkz. Akka belgeleri: https://doc.akka.io/docs/akka/current/typed/dispatchers.html#blocking-needs-careful-management ).


1
Yanıtınız için teşekkürler. Bunun nasıl işe yarayacağını anlamıyorum? blobClient.download (os) çağrıldığında bayt nereye gider (eğer kendim çağırıyorsam)? Yazılmayı bekleyen terabayt bir veri oturduğunu düşünün. Bana öyle geliyor ki blobClient.download çağrısı, bu temelde bir IOUtils.copy benzeri işlem olacak şekilde sender.tell çağrısından çağrılmalıdır .. preMaterialize kullanarak nasıl olduğunu göremiyorum?
MeBigFatGuy

OutputStream bir dahili arabelleğe sahiptir, bu arabellek doluncaya kadar yazmaları kabul etmeye başlayacaktır, eğer asenkron akış aşağısı elemanlar kullanmaya başlamamışsa, o zaman yazma iş parçacığını bloke edecektir (bu yüzden engellemeyi işlemenin önemli olduğunu belirtmiştim).
johanandren

1
Ama i preMaterialize ve OutputStream olsun, o zaman blobClient.download (os) yapan benim kodum; doğru? Bu, devam etmeden önce tamamlanması gerektiği anlamına geliyor, ki bu imkansız.
MeBigFatGuy

Eğer indirme (os) bir iş parçacığının çatal değilse, engelleme ile başa çıkmak ve başka bir işlem durmaz emin olun. Bir yol işi yapmak için bir ipliği çatallamak, diğeri önce aktörden yanıt vermek ve ardından engelleme işini yapmak olacaktır, bu durumda aktörün diğer aktörleri aç bırakmadığından emin olmalısınız, sonunda bağlantıya bakın. cevabım.
johanandren

bu noktada sadece işe koyulmaya çalışıyorum. 10 baytlık bir dosyayı bile işleyemez.
MeBigFatGuy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.