کمک در تحلیل کد یک پروژه الکسیر ( هوا شناسی به واسطه پروسز )

درود خدمت اساتید محترم . من اول به واسطه یک کتاب خوب یک اپ هواشناسی نوشتم یعنی کد هاشو کپی پست کردم و باهاش رفتم جلو . که بهش اسم شهر با کشور می دید و براتون می یاد می گه آب هواش چقدره تا اینجا مشکل اصلا نبود . حال نویسنده کتاب برای حرفه ای تر کردن این اپ و درخواست چندین شهر اومد از پروسز ها استفاده کرد

کل کد ها اینجا هستند

که وقتی از ترمینال می یارم بالا اول یک لیست درست می کنم به صورت زیر

cities = ["Singapore", "Monaco", "Vatican City", "Hong Kong", "Macaus", "Amol","Tehran","Esfahan","Zanjan"]

و به وسیله کامنت زیر نتیجه می یاد :

Metex.temperatures_of cities 

حالا من دقیقا کجاشو درک نکردم ( البته متوجه شدم ولی در نبردم :smile: )

اول نویسنده اومد در فایل worker که در لینک بالا هم قرار دادم کد های زیر رو ارسال کرد

 def loop do
    receive do
        {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)})
        _ -> IO.puts "don't know how to process this message"
    end
    loop
  end

چیزی که من متوجه شدم فقط در همین حد هست که یک پروسز اینجا چک می شه وجود داره یا نه ولی در اینجا این تابع اصلا وردی نداره که داره sender_pid و location داره استفاده می کنه اصلا دقیق متوجه نشدم چرا این آقا این رو نوشته و ازش داره استفاده می کنه

بعد اومد در فایل یگری به نام coordinator

def loop(results \\ [], results_expected) do
    receive do
      {:ok, result} ->
        new_results = [result|results]
        if results_expected == Enum.count(new_results) do
          send self, :exit
        end
        loop(new_results, results_expected)
        :exit -> IO.puts(results |> Enum.sort |> Enum.join(", "))
        _ -> loop(results, results_expected)
    end
  end

اینجا چیزی که فهمیدم اول چک می کنه آیا این پروسز موفقیت آمیز درست شده بعد نتیجه رو می گیره با نتیجه کاربر یکیش می کنه و دوبار اون رو سورت می کنه و نمایش می ده در ترمینال بیشتر از این واقعا متوجه نشدم فقط می دونم چک کننده هست .

در آخر که فایل اصلی فراخوان کننده metex هست

 def temperatures_of(cities) do
    coordinator_pid =
      spawn(Metex.Coordinator, :loop,[[], Enum.count(cities)])

      cities |> Enum.each(fn city ->
        worker_pid = spawn(Metex.Worker, :loop,[])
        send worker_pid, {coordinator_pid, city}
      end)
  end

در اینجا یک پروسز درست می کنه از تابع loop داخل ماژول coordinator که فکر کنم کد های بالایی که کمی توضیح دادم چاپ می کنند شهر هارو در ترمینال و بعد یک پروسز درست می کنند از تابع loop در فایل worker در حقیقت می دونم چطور داره کار می کنه ولی از روی کد خودم بخوام بنویسم نمی تونم . چطور داره اینجور رفتار می کنه

اگر دوستان می تونند توضیحی در مورد این فایل ها بدند ممنون می شم کل پروژه همین سه فایل رو داره با تشکر

با چه قسمتش دقیقامشکل دارید؟

{sender_pid, location} 

این یک pattern هست
process ها هرکدام یک mailbox به شکل queue دارن و یک id,
send یک فانکشن مخصوص برای فرستادن پیام به process
process گیرنده در فانکشن loop که queue mailbox و چک میکنه باید receive و تعریف کنه
در receive همیشه چند pattern مینوسند که اگر پیام به pattern مچ بشه یه کاری اون process عمل میکنه

1 پسندیده

درود خدمت شما . تشکر از پاسختون . ببینید لطفا من بیشتر در این کد دچار مشکل شدم

 def loop do
    receive do
        {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)})
        _ -> IO.puts "don't know how to process this message"
    end
    loop
  end

اصلا چرا اینو نوشته فقط چک کنه پروسه ای وجود داره ؟ بعد تابع temperature_of رولود کنه که چی بشه ؟ آخه جایی بعدا استفاده نمی شه . چطوری از temperature_of می یاد اطلاعات می گیره جایی نمایش می ده ( فایل worker ) فکر می کنم با این تفاسیر من کلشو درک نکردم سه بخشی که مربوط به پروسز هست دو تابع loop و temperature_of در فایل metex

Worker اینجا کارو تقسیم میکنه بین process ها
تابع نوشته که هروقت یک pid گرفت با اسم یک شهربره دمای شهرو بگیره به process فرستنده برگردونه
به این روش جمع اوری اطلاعات به صورت parallel
Scatter gather گفته میشه
چون http request طول میکشه برای هر شهر یک process جدا درست کرده و worker میره جواب دما رو میگره و به اون process برمیگردونه این طوری به جای این که به زمان 5 تا request صبر کنیم همه به صورت همزمان اجرا میشن

1 پسندیده

ببخشید امروز دارم مزاحمتون می شم یکمی گنگه برام در کل این موضوع

اینجوری که متوجه شدم ( لطفا ببنید همینطور هست یا من بد متوجه شدم )

در این تابع از کاربر شهر هارو می گیره و یک مورد به فایل worker می فرسته و باز خودش جوابو می گیره درسته ؟

def temperatures_of(cities) do
    coordinator_pid =
      spawn(Metex.Coordinator, :loop,[[], Enum.count(cities)])

      cities |> Enum.each(fn city ->
        worker_pid = spawn(Metex.Worker, :loop,[])
        send worker_pid, {coordinator_pid, city}
      end)
  end

این جوابی که می گیره که برگشت از تابع

  defp temperature_of(location) do

result = url_for(location) |> HTTPoison.get |> parse_response

case result do
  {:ok, temp} -> "#{location} : #{temp} °C"
  :error -> "#{location} not found"
end

  end

هست حالا اینو کجا می فرسته که به کدوم فایل که داره چاپ می کنه ؟ تو ترمینال . !!

پس کار این فایل چی هست ؟ چه ورودی هایی می گیره !!

defmodule Metex.Coordinator do

  def loop(results \\ [], results_expected) do
    receive do
      {:ok, result} ->
        new_results = [result|results]
        if results_expected == Enum.count(new_results) do
          send self, :exit
        end
        loop(new_results, results_expected)
        :exit -> IO.puts(results |> Enum.sort |> Enum.join(", \n"))
        _ -> loop(results, results_expected)
    end
  end

end

اگر خیلی پرتم فکر کنم توضیح بدید وقتتون گرفته می شه

خواهش میکنم Coordinator این طوری کار میکنه وقتی جواب و گرفت در یک لیست ذخیره میکنه تا زمانی که لیست جوابها اندازه لیست کل شهرها نیست منتظر جواب بعدی مشه و به loop ادامه میده ولی به محض اینکه تمام جوابها رو گرفت یعنی لیست جواب ها انداره لیست شهر ها شد از process خارج میشه نکته اینه که برای pattern خروج اینو تعریف کرده که قبل خروج تمام جواب ها رو print کنه

1 پسندیده

پس بر اساس صحبت شما اینجور متوجه شدم . جوابی که داخل فایل worker درست می شه در لیستی که داخل فایل Coordinator فانکشن loop ذخیره می شه و در آخر قبل از اتمام چاپ می شه ؟ درسته ؟

1 پسندیده

بله دقیقا

2 پسندیده

اگر الان کاملا متوجه نشدی مهم نیست با استفاده از Task در فصلهای اینده این کد به مراتب ساده میشه

2 پسندیده

خیلی خیلی ممنونم تشکر از شما . یک بار قبلا تسک رو مطالعه کردم متاسفانه کد هاش برام قابل درک نبود دوباره برگشتم دارم هم رو از اول می خونم . الانم شما زحمت کشیدید کمک بسیار بزرگی کردید حداقل کد رو به صورت کلی متوجه شدم همینشم خیلی خیلی خوبه
تشکر تشکر

2 پسندیده

درود . من یک تغییراتی در لینک بالا دادم

در فایل worker دوتا تابع اضافه کردم

def loadbytasks(location) do
task = Task.async(fn -> hipi(location) end)
Task.await(task)
  end

  def hipi(cities) do
cities |> Enum.map(fn cities -> temperature_of(cities)end)
  end

و تو ترمینال اینجوری می زنم : iex -S mix

cities = ["Singapore", "Monaco", "Vatican City", "Hong Kong", "Macaus", "Amol","Tehran","Esfahan","Zanjan"]

بعد می زنم

Metextask.Worker.loadbytasks cities  

آیا کار من درست هست چون جواب می یاره به هر صورت جواب چک کردم درسته ولی خوب نمی دونم آیا راهش درسته برای استفاده از تسک یا خیر؟

#به روز رسانی
البته من فکر می کنم کد اول که با پروسز هست یک خورده سریع تر هست یا من دارم اشتباه می کنم چشمی این حرفو زدم
انگار زمان بندیشون با این کد در ترمینال فرق نداره

cities |> Enum.map(fn cities -> Metextask.Worker.temperature_of(cities)end)

فکر کنم کدم مشکل داره چون سرعت

Metextask.temperatures_of cities  

خیلی بالاتر از کد های بالاست چه با تسک چه بدون تسک