Menu
Jest wolny
rejestracja
Dom  /  NA/ Nieodwołalny sukces php. Prosty przykład z wykorzystaniem PHP i AJAX

Nieodwołalny sukces php. Prosty przykład z wykorzystaniem PHP i AJAX

Pozdrowienia, drogi przyjacielu!

"Czym jest dla ciebie sukces w życiu ???"

Proszę, pomyśl o tym, zatrzymaj się na chwilę.

Dobra, teraz pozwól, że ci pomogę. Co nie jest sukcesem, pisałem w poprzedniej liście mailingowej. Odrzućmy te koncepcje od razu.

Sukces to spokój ducha.
Sukces to bycie szczęśliwym.
Sukces to realizacja samego siebie i ujawnienie swojego potencjału.
Sukces to spełnienie w życiu.
Sukces to robienie tego, co kochasz, co cię rozpala i możesz to robić przez całą dobę.
Sukces to oddanie się innym i uczynienie świata lepszym miejscem i szczęśliwszymi ludźmi.
.

Sukces jest nierozerwalnie związany ze stanem umysłu. Nasza dusza przyszła na ten świat, aby cieszyć się i realizować siebie, a my (nasz umysł, nasze ciało, nasza świadomość) musimy ją w tym wspierać. Kiedy nasza dusza tworzy i realizuje się, czujemy się szczęśliwi. Kiedy czujemy i widzimy, że tworzenie naszej duszy i to, co robimy, przynosi wielkie korzyści innym ludziom, odczuwamy błogość. To się nazywa sukces. Sukces to spełnienie życia.

Jakakolwiek realizacja talentów duszy możliwa jest tylko dzięki innym ludziom... Dusza nie tworzy dla siebie. Tworzy dla innych - aby im pomagać i urzeczywistniać życie innych oraz dać im cząstkę swojego szczęścia. Szczęśliwy człowiek przekazuje kawałek swojego szczęścia innym, nieszczęśliwy przekazuje swoje nieszczęście innym. Unikaj nieszczęśliwych ludzi!

Jeśli nagle w tej chwili wszyscy ludzie znikną, samorealizacja staje się niemożliwa - po co pisać książki, bo nikt ich nie przeczyta, po co tworzyć nowe modele ubrań, bo nikt ich nie założy, po co sens budowy nowych domów, w których nikt nie będzie mieszkał?

Oczywiście to nie ma sensu.

Tutaj się pojawia podwójna natura sukcesu: dusza tworzy i realizuje się, a także pomaga innym ludziom stać się szczęśliwszymi.
Najbardziej precyzyjna definicja sukces, który mógłbym dać, brzmiałby tak: sukces to realizacja Twoich prawdziwych talentów, które czynią nasz świat lepszym, doskonalszym, a ludzie szczęśliwszymi.

Chcę, żebyś głęboko zdał sobie sprawę, że ludzie, którzy żyją tylko dla siebie i zbierają majątek tylko dla siebie, są nieszczęśliwi... Gromadzą to bogactwo, aby wypełnić duchową pustkę, która powstała w wyniku bezsensownego życia. Ale tę pustkę można wypełnić tylko miłością, przynoszącą wartość innym ludziom. Dusza jest szczęśliwa, gdy bez nadmiaru oddaje się, aby uczynić ten świat lepszym miejscem. A jaki jest sens w tych wszystkich bogactwach, które człowiek zgromadził, kiedy odchodzi, ponieważ nie jesteśmy trwali. Dusza zaczyna tworzyć wartość, realizować się, a potem wraca do „domu”. Jeśli nie tworzy tej wartości, ale robi coś innego, źle się czuje. Czuje, że przyszła na ten świat i nie robi tego, czego chce. A powodem tego jest nasz umysł - zaślepiony "sukcesem" w ogólnym rozumieniu tego słowa. Goni za iluzją, a kiedy to osiąga, jeśli w ogóle, uświadamia sobie bezsensowność tego, co osiągnął.

A czym jest sukces w ogólnym rozumieniu?
- bogactwo (pieniądze, rzeczy materialne)
- chwała, władza, popularność
- status

Ale spójrz, to wszystko pochodzi z ego. Człowiek chce poczuć swoje znaczenie, ale nie rozumie, że bogactwo, sława, status są iluzją. Są jak woda morska, która bez względu na to, ile wypijesz, nigdy nie zaspokoi pragnienia. Dlatego ludzie całe życie i gonią za nimi. Myślą, że zarobię tyle pieniędzy i będę szczęśliwy, że pójdę do poziomu dochodu 100 000 $ rocznie i wtedy będę szczęśliwy, jak wyjdę na scenę i zaśpiewam będę bądź szczęśliwy, wyjdę za mąż, będę miał dzieci... możesz to sprawdzić, ale mogę powiedzieć ze 100% pewnością, że nie będziesz szczęśliwy. Co więcej, twój poziom szczęścia stanie się jeszcze niższy. Odsuwasz się od swojego powołania i zdając sobie z tego sprawę, dusza staje się jeszcze bardziej nieszczęśliwa. Im więcej bogactwa, sławy, statusu otrzymujesz, tym większą kontrolę nad życiem przejmuje umysł i tym bardziej odsuwa się rolę duszy. Ale prawdziwe szczęście pochodzi z duszy!!!

Sukces to harmonia między duszą a umysłem. Rolą rozumu jest: pomóc dusza do samorealizacji. Niewłaściwie ustalamy priorytety. Na pierwszym miejscu stawiamy krótkotrwałe ciało i rzeczy materialne, a na końcu nieśmiertelną duszę i niewyczerpane bogactwo. Biblia mówi: „gromadź bogactwo w niebie, a nie na ziemi”. Nasze ciało jest wehikułem duszy... Dusza jest połączona z Wyższym Umysłem i tylko ona jest w stanie zrozumieć, co jest potrzebne temu światu. Wszechświat zachęca ludzi, którzy podążają własną drogą... Twoja ścieżka jest najmniej energochłonna, aw naszym świecie wszystko płynie po ścieżce najmniejszego oporu. Zawsze mówię, że sukces to normalny bieg wydarzeń. Awaria to odstępstwo od normy. Jeśli teraz nie odnosisz takich sukcesów, jak chcesz, to nie robisz tego, co powinieneś zrobić. Dusza i umysł są w sprzeczności. A im więcej tej niezgody, tym bardziej nieszczęśliwa jest osoba.

Ale nie myśl, że mówię, że człowiek nie potrzebuje rzeczy materialnych. Bardzo nawet konieczne. A oto dlaczego: gdy ktoś nie ma pieniędzy, jest zmuszony iść do pracy i angażować się w swego rodzaju „głupotę”. Człowiek spędza 10 godzin dziennie, aby zarobić pieniądze, ale robiąc to, sam się nie realizuje. Szef kuchni to osoba, która realizuje się Twoim kosztem. (Opowiadam, jak to się dzieje w większości przypadków. Większość ludzi nienawidzi swojej pracy, ale pracują, ponieważ potrzebują pieniędzy, aby przetrwać).

Rzeczy materialne tworzą komfort dla duszy. Rzeczy materialne wyposażają ten świat dla duszy. Dusze o wiele przyjemniej jest tworzyć arcydzieła w miejscach, które ją inspirują. Dużo lepiej jest namalować obraz w domu nad morzem niż w „szambie”. Dusza potrzebuje spokoju i pociechy, aby tworzyć. Ale jaki może być spokój, jeśli rodzina nie ma wystarczającej ilości pieniędzy i każdego dnia mąż i żona kłócą się o to.

Dusza potrzebuje czasu na wyrażenie siebie. Dopiero po pewnym czasie wartość stworzoną przez duszę można sprzedać i sprzedać setki, a nawet tysiące razy drożej niż człowiek dostaje w pracy. Ale stworzenie takiej wartości wymaga czasu. Osobiście zajęło mi 5 miesięcy, aby wypracować skromny dochód. Po 8 miesiącach moja strona zaczęła generować dochód, z którego biedna rodzina mogła już żyć. I zaledwie 17 miesięcy później moja strona zaczęła generować dochód, który już zastąpi dochód z bardzo dobrze płatnej pracy.

Wymiana pracy zajęła 17 miesięcy. Ale teraz jestem wolny! Robię to, co kocham, a to dopiero początek. Nie ma granic dla moich marzeń - a zatem dla mnie nie ma granic. Kiedy zajmujesz się swoim biznesem, twoje dochody są ograniczone tylko twoją wyobraźnią i niczym więcej. Kto w pracy zarabia 1 000 000 $ rocznie? Tak, może być. Ale rób swoje, nawet to nie jest kaplica.
Materiał jest ważny, ale tylko w celu zaspokojenia potrzeb życiowych.

Będę szczery: nie otrzymując dochodu, trudniej jest tworzyć i tworzyć arcydzieła... Umysł nieustannie mówi: „to, co robisz, jest dobre, ale po co będziemy żyć?” A to pytanie stale i mocno odwraca uwagę od kreatywności. Zabiera nam szczęście. Aby wyłączyć ten dialog, twoja ulubiona rozrywka musi przynosić pieniądze. Oczywiście wtedy umysł zaczyna zadawać inne pytania, ale jak? więcej pieniędzy przynosi to, co kochasz, tym mniej bolesne i rozpraszające stają się te kwestie.

Często ludzie pracują w pracy, zarabiają, ale nadal mają hobby. Czym jest hobby?
Hobby to hobby, które nie generuje dochodu. Ale dlaczego nie zamienić hobby w pracę? Najszczęśliwsi są ci, których hobby to praca.... Nieustannie robią to, co kochają.
Wszystko, o czym mówię, o pracy, o pieniądzach, chcę przekazać dwie ważne myśli: 1) Dusza i umysł muszą być w harmonii
2) To, co niematerialne, powinno być zawsze na pierwszym miejscu

Należy skupić się tylko na tym, co niematerialne! Materiał do dołączenia w konsekwencji... Oto właściwe priorytety w życiu:
szczęście -> zdrowie -> bogactwo A wielu ludzi żyje według schematu
bogactwo-> zdrowie-> szczęście
A co gorsza, są ludzie, którzy żyją według wzorca
bogactwo-> bogactwo-> bogactwo

Nic dziwnego, że nie są szczęśliwi. Ci ludzie mają miliony, ale nie mają przyjaciół, mają problemy rodzinne. Mają problemy w relacjach z ludźmi. Ponieważ myślą, że wszyscy, którzy ich otaczają, są z nimi tylko ze względu na ich pieniądze i nic więcej. Nie wiem o tobie, ale nie chciałbym takiego szczęścia. Kiedy priorytety w życiu są ustawione prawidłowo, w rezultacie powstaje bogactwo. Nie ma sensu się na tym skupiać. Wysoki poziom szczęścia i zdrowia nieuchronnie prowadzi do wysokich dochodów.

Rzeczy materialne i nasze bogactwo mogą służyć jedynie jako dodatek do naszego szczęścia. Nie mogą służyć jako fundament. Jaki jest fundament, omówiliśmy już z tobą powyżej.

Zestaw par klucz/wartość, które dostosowują żądanie AJAX. Wszystkie parametry są opcjonalne... Dozwolone, ale nie zalecane jest ustawienie wartości domyślnej dla dowolnego parametru przy użyciu metody $ .ajaxSetup().
metoda $ .ajax () obsługuje następujące parametry:

    akceptuje(domyślnie: zależy od typ danych).

    Typ: zwykły obiekt.
    Zestaw par klucz/wartość, które są wysyłane do Zaakceptować nagłówek żądania. Ten nagłówek informuje serwer, jaki rodzaj odpowiedzi żądanie zaakceptuje w odpowiedzi. Zauważ, że wartość parametru określonego w typ danych(typ danych, którego oczekujemy od serwera) jest dopasowany do określonego w parametrze. Dodatkowo do poprawnego przetworzenia odpowiedzi z serwera niezbędny jest parametr konwertery określić funkcję, która zwraca przekonwertowaną wartość odpowiedzi. Na przykład: $ .ajax (( akceptuje: (mójtyp niestandardowy: " aplikacja / x-jakiś-niestandardowy-typ" } , // określ sposób przetwarzania odpowiedzi konwertery: ("tekst mycustomtype": funkcja ( wynik) { // zwróć przekształconą wartość odpowiedzi zwróć nowy wynik; )), // Oczekiwany typ danych ("mycustomtype") typ danych: "mójtyp niestandardowy"));

    asynchroniczny(domyślnie: prawda).

    Typ: logiczny.
    Domyślnie wszystkie żądania są wysyłane asynchronicznie, jeśli chcesz zorganizować żądania synchroniczne, ustaw ten parametr na false. Należy pamiętać, że żądania międzydomenowe i element, parametr typ danych co się liczy „jsonp” nie obsługują żądań synchronicznych. Pamiętaj, że korzystając z żądań synchronicznych, możesz tymczasowo zablokować przeglądarkę, wyłączając wszelkie akcje, gdy żądanie jest aktywne.

    przedWyślij... Typ: funkcja (jqXHR jqXHR, PlainObject ustawienia).
    Funkcja wywołania zwrotnego, która zostanie wywołana przed wysłaniem żądania AJAX. Funkcja ta pozwala na modyfikację obiektu jqXHR (w jQuery 1.4.x obiekt XMLHTTPRequest) przed jego wysłaniem. Obiekt jqXHR to dodatek rozszerzający obiekt XMLHttpRequest, obiekt zawiera wiele właściwości i metod pozwalających uzyskać więcej pełna informacja o odpowiedzi serwera, a także o tym, że obiekt zawiera metody Promise. Jeśli funkcja przedWyślij zwraca wartość false, to żądanie AJAX zostanie anulowane. Od wersji jZapytanie 1.5 funkcjonować przedWyślij zostanie wywołana niezależnie od typu żądania.

    Pamięć podręczna(domyślnie: prawda, for typ danych "scenariusz" oraz „jsonp” fałszywe).

    Typ: logiczny.
    Jeśli ustawiono na false, spowoduje to, że żądane strony nie będą buforowane przez przeglądarkę. Pamiętaj, że false działa poprawnie tylko z GŁOWA oraz DOSTWAĆ upraszanie.

    kompletny.

    Typ: funkcja (jqXHR jqXHR, Strunowy status tekstu).
    Funkcja, która jest wywoływana po zakończeniu żądania (funkcja jest wykonywana po zdarzeniach AJAX "powodzenie" lub "błąd"). Do funkcji przekazywane są dwa parametry: jqXHR(w obiekcie jQuery 1.4.x) Żądanie XMLHTTP) oraz wiersz odpowiadający statusowi wniosku ( "powodzenie", "niezmodyfikowany", "brak zawartości", "błąd", "koniec czasu", "anulować", lub „błąd parsera”). Od jQuery 1.5 parametr kompletny może przyjąć szereg funkcji, które będą kolejno wywoływane.

    zawartość.

    Typ: zwykły obiekt.
    Obiekt składający się z par ciąg/wyrażenie regularne, które definiują sposób, w jaki jQuery będzie analizować (parsować) odpowiedź w zależności od typu zawartości. Dodano w jQuery 1.5.

    Typ zawartości(domyślny: "aplikacja / x-www-form-urlencoded; zestaw znaków = UTF-8").

    Wpisz: Boolean lub String.
    Określa typ treści określony w żądaniu podczas wysyłania danych do serwera. Od jQuery 1.6 dozwolone jest podanie wartości false, w którym to przypadku jQuery nie przekazuje pola w nagłówku Typ zawartości w ogóle.

    kontekst.

    Typ: zwykły obiekt.
    Gdy wykonywane są wywołania zwrotne AJAX, ich kontekstem wykonania jest obiekt window. Parametr kontekst umożliwia ustawienie kontekstu wykonania funkcji w taki sposób, że $ (to) będzie odnosić się do określonego elementu DOM lub obiektu. Na przykład: $ .ajax (( adres URL: "test.html", kontekst: $ (". mojaKlasa"), // nowy kontekst wykonania funkcji powodzenie: funkcja () ( // jeśli żądanie się powiedzie, wywołaj funkcję$ (this) .html ("Wszystko w porządku"); // dodaj treść tekstową do elementu za pomocą class.myClass } } );

    konwertery

    Wartości domyślne:
    ("* tekst": okno.Ciąg, // dowolny typ tekstu"text html": prawda, // tekst w html "text json": jQuery.parseJSON, // tekst w JSON "text xml": jQuery.parseXML // tekst w XML) Typ: PlainObject.
    Obiekt zawierający typ danych do przekonwertowania i sposób jego przekonwertowania. Wartość każdego transformatora to funkcja, która zwraca przekształconą wartość odpowiedzi. Dodano w jQuery 1.5.

    crossDomain(domyślnie: false dla żądań w tej samej domenie, true dla żądań między domenami).

    Typ: logiczny.
    Jeśli chcesz wysłać żądanie międzydomenowe w tej samej domenie (na przykład żądanie jsonp), ustaw ten parametr na wartość true. Umożliwi to np. przekierowanie żądania do innej domeny z Twojego serwera. Dodano w jQuery 1.5.

    Typ: PlainObject lub String lub Array.
    Dane do przesłania na serwer. Jeśli nie są ciągiem, są konwertowane na ciąg zapytania. Do DOSTWAĆ ciąg zapytania zostanie dołączony do adresu URL. Aby zapobiec automatycznemu przetwarzaniu, możesz użyć parametru przetwarzać dane z wartością false. Jeżeli dane są przesyłane jako część obiektu, to muszą składać się z par klucz/wartość. Jeżeli wartość jest tablicą, to jQuery serializuje wiele wartości z tym samym kluczem (w zależności od wartości parametru tradycyjny co pozwala nam na użycie tradycyjnego typu serializacji opartego na metodzie $ .param).

    filtr danych.

    Typ: funkcja (ciąg dane, Strunowy rodzaj) => Wszystko.
    Funkcja jest wywoływana po pomyślnym wykonaniu żądania AJAX i umożliwia przetwarzanie „surowych” danych otrzymanych z odpowiedzi serwera. Dane muszą zostać zwrócone niezwłocznie po przetworzeniu. Funkcja przyjmuje dwa argumenty: dane- dane odebrane z serwera w postaci ciągu i rodzaj- typ tych danych (wartość parametru) typ danych).

    typ danych(domyślny: xml, json, scenariusz, lub html).

    Typ: ciąg.
    Definiuje typ danych, które chcesz otrzymać z serwera. Jeśli nie określono typu danych, jQuery spróbuje określić go na podstawie typu MIME z odpowiedzi ( XML typ MIM da w wyniku XML, począwszy od jQuery 1.4 json da przedmiot JavaScript, scenariusz wykona skrypt, a wszystko inne zostanie zwrócone jako ciąg).

    Typy podstawowe (wynik jest przekazywany jako pierwszy argument do funkcji zwrotnej) powodzenie):

    • "xml"- zwroty XML dokument, który można wyrenderować za pomocą jQuery.
    • "html"- zwroty HTML jako zwykły tekst, tagi

      Najłatwiejszy sposób na pracę AJAX Czy połączyć ramy jQuery, co faktycznie zrobiłem. jQuery zapewnia nam łatwą do zrozumienia i łatwą w użyciu składnię do wysyłania AJAX prośby, dlaczego nie skorzystać z tego?

      Tworzenie skryptów Js

      Składnia pliku validate.js to

      $ (dokument) .ready (function () (var email = ""; $ ("# email"). keyup (function () (var value = $ (this) .val (); $ .ajax ((type: "POST", url: "email.php", dane: "email =" + wartość, sukces: funkcja (msg) (if (msg == "ważny") ($ ("# wiadomość"). Html (" Można użyć tego e-maila.Ten e-mail jest już zajęty.");))));)); $ (" # prześlij "). kliknij (funkcja () (if (email ==" ") (alert (" Proszę wpisać dane do wszystkich e-maili ");) else ( $ .ajax ((type: "POST", url: "email.php", data: "add_email =" + email, sukces: function (msg) ($ ("# wiadomość"). html (msg);)) );)));));

      Obsługa php

      Ten skrypt otrzyma POCZTAżądanie od klienta, przetworzyć je i zwrócić wynik. AJAX odczytuje wynik i podejmuje na jego podstawie decyzję.
      Składnia pliku email.php to

      $ połączenie = mysqli_connect ("localhost", "email", "email", "email"); if (isset ($ _ POST ["email"]) && $ _POST ["email"]! = "") ($ e-mail = $ _POST ["email"]; $ e-mail = mysqli_real_escape_string ($ połączenie, $ e-mail); if (! filter_var ($ email, FILTER_VALIDATE_EMAIL)) (echo "invalid";) else ($ sql = "SELECT id FROM email WHERE email =" $ email ""; $ wynik = mysqli_query ($ połączenie, $ sql); if ( mysqli_num_rows ($ wynik) == 1) (echo "nieprawidłowe";) else (echo "prawidłowe";))) if (isset ($ _ POST ["add_email"]) && $ _POST ["add_email"]! = "" ) ($ email = mysqli_real_escape_string ($ połączenie, $ _ POST ["add_email"]); $ sql = "WSTAW W e-mail (email) WARTOŚCI (" $ email ")"; if (mysqli_query ($ połączenie, $ sql) )) ( echo Powodzenie";) jeszcze (echo" Błąd"; } }

      W naszym skrypcie php, najpopularniejszy kod, który przetwarza żądanie postu i wyświetla tekst na stronie. W rezultacie AJAX wysyła zapytanie skrypt php skrypt przetwarza go i generuje wynik, AJAX odczytuje wynik i zmienia stronę w czasie rzeczywistym.

      AJAX przekazuje żądanie POST do skryptu za pomocą tego fragmentu kodu:

      $ .ajax ((type: "POST", url: "email.php", data: "email =" + wartość, sukces: funkcja (msg) (if (msg == "ważny") ($ ("# wiadomość ") .html (" Można użyć tego e-maila."); email = wartość;) else ($ (" # wiadomość "). html (" Ten e-mail jest już zajęty."); } } });

      type — typ żądania, POST lub GET. W naszym przypadku POST;
      url - adres skryptu, na który wysyłane jest żądanie;
      dane - dane, które są przekazywane w żądaniu;
      sukces - co zrobić w wyniku pomyślnej realizacji wniosku. W naszym przypadku funkcja jest wywoływana;

      W samym skrypcie sprawdzanie obecności wiadomości e-mail w bazie danych odbywa się za każdym razem, gdy w polu e-mail zostanie wpisany znak. W skrypcie $ ("# e-mail"). Keyup (funkcja () ()); który sprawdza naciśnięcie klawisza w polu o id = "email".
      Jak widać, kod jest dość prosty i nie wymaga szczególnie dużych umiejętności do zrozumienia, wszystko wiąże się z obsługą zdarzeń keyup() – naciśnięcie klawisza, kliknięcie () – kliknięcie elementu. Śledzony przez AJAXżądanie i odpowiedź ze skryptu. Tak więc, używając php i ajax, możesz uzyskać prawie nieskończone możliwości tworzenia interaktywnych stron.
      Ten kod nie twierdzi, że jest wysokiej jakości, ale jeśli go opracujesz, dodasz poprawne walidacje na poziomie klienta i serwera, wpiszesz css, możesz go całkiem wykorzystać w swoich projektach.
      Jeśli masz jakieś pytania, nie wahaj się pisać komentarzy.
      Miłego dnia i do zobaczenia wkrótce 🙂

      Wiza: Blaue Karte UE

      W momencie składania dokumentów 29 lat

      Z Astrachania

      Miasto ambasady: Moskwa

      Uczelnia, specjalność: Astrachański Państwowy Uniwersytet Techniczny, Kompleksowe wsparcie bezpieczeństwo informacji systemy zautomatyzowane

      Języki: angielski średniozaawansowany

      Jak to się wszystko zaczeło:

      Chęć przeniesienia się gdzieś istnieje od dawna. Uwzględniono jednak głównie ciepłe kraje z morzem. Dwukrotnie poważnie badali glebę z myślą o przeprowadzce do Czarnogóry lub Bułgarii. W rezultacie w ostatniej chwili, z tego czy innego powodu, zmienili zdanie. Ostatni raz we wrześniu 2014 roku po poważnych przygotowaniach do sprzedaży auta.

      W październiku przypadkowo zobaczyłem ogłoszenie o poszukiwaniu programistów z relokacją do Niemiec. Wtedy nie miałem pojęcia o istnieniu Niebieskiej Karty i uważałem Niemcy za kraj o niezwykle twardej polityce migracyjnej wobec obywateli spoza UE.

      Z pewnym sceptycyzmem i nieufnością napisałem do Skype'a. Po drugiej stronie ekranu odpowiedziała rekruterka (Alina), która zajmuje się selekcją personelu IT, a następnie relokacją do Niemiec. W momencie naszej pierwszej komunikacji miała miejsce rekrutacja programistów do dużego sklepu internetowego z siedzibą w Berlinie. Wysłałem swoje CV i czekałem.

      Po chwili Alina powiedziała, że ​​jej kolega z Niemiec porozmawia ze mną, aby ocenić poziom języka i adekwatności. Rozmowa to raczej rozmowa z dwoma logicznymi zadaniami, trwała 30 minut na Skype. Potem kazano mi czekać. Mniej więcej tydzień później zaplanowano pierwszą rozmowę techniczną. Rozmowa techniczna odbyła się również przez Skype z jednym z programistów firmy. Moim zdaniem poszło całkiem nieźle, ale tydzień później powiedziano mi, że się nie nadaję. Nawiasem mówiąc, żaden kandydat z Aliny nie przeszedł z pewnych powodów.

      Jak to wszystko się potoczyło:

      Trochę zdenerwowany, ale życie toczy się dalej. A kilka dni później Alina powiedziała, że ​​mają nowego klienta ze Stuttgartu, który szuka programistów i zaplanowano dla mnie wywiad. Pierwszą część wywiadu dzielimy z szefem działów IT i HR. Ogólna rozmowa o doświadczeniach, mnie i firmie, dużo śmiechu i żartów po obu stronach. Najwyraźniej mój humor przypadł mi do gustu, więc kilka dni później umówiono mnie na rozmowę techniczną z potencjalnym kierownikiem liniowym. Ta część wywiadu trochę mnie zaskoczyła, bo jak ujął to później jeden z kandydatów, była to jak „rozmowa dwóch programistów przy piwie”. Później tego samego wieczoru otrzymałem zaproszenie na osobistą rozmowę w biurze.

      W tym czasie nie miałem otwartej wizy Schengen. Niezbędne dokumenty zebrano w trybie pilnym. W Niemieckim Centrum Wizowym w Moskwie złożyłem wniosek o pilna wiza a już następnego dnia wziąłem paszport z wizą. złożyłem wniosek o wiza biznesowa na zaproszenie który został mi wysłany z Esslingen to tylko list, w którym jestem zaproszony do komunikacji i w którym jest jasno określone, że wszystkie kwestie finansowe związane z lotami, transferami, wyżywieniem i zakwaterowaniem są podejmowane przez firmę.

      Komunikacja osobista w biurze odbywała się z trzema głównymi liderami IT firmy. Pierwsza część to znowu tylko komunikacja o doświadczeniu, umiejętnościach i ogólnym zrozumieniu niektórych zagadnień. Drugi jest przy komputerze. Szczerze, bardzo, bardzo proste zadania na poziomie „junior z doświadczeniem testowym” :). Zadania zostały wykonane. Następnie obiad i od razu oferta w postaci dwóch egzemplarzy umów o pracę (stanowisko Starszy programista PHP) podpisany przez firmę. Zastanowiłem się trochę i powiedziałem, że odpowiem w ciągu tygodnia.

      Decyzja zapadła i zacząłem przygotowywać się do ubiegania się o wizę.

      Jak się przeprowadziliśmy:

      Firma zapłaciła za przelot dla mnie i mojej rodziny (żona i córka 2,5 roku), wynajęła dla nas mieszkanie na pierwsze trzy miesiące (w moim przypadku idealne miejsce z widokiem na centralny plac Marktplatz) i przydzieliła osobę do pomoc po raz pierwszy. To nie jest agent relokacyjny w czysta forma, ale za jej pośrednictwem rozwiązywaliśmy wszystkie pojawiające się problemy. Byłem pierwszym pracownikiem spoza UE w firmie, więc zadano mi wiele pytań. Teraz oprócz mnie w firmie pracuje jeszcze jeden facet z Kijowa (przyleciał miesiąc po mnie), a do przeprowadzki szykuje się deweloper z Odessy. Wszyscy oni też byli zatrudnieni nie bez pomocy Aliny.

      W tym miejscu chciałbym powiedzieć, że jestem bardzo wdzięczny Alinie, która rozwiązała wszystkie pytania, które miałem w procesie zatrudnienia. Miałam ogromne szczęście, że na wszystkich etapach zatrudnienia i późniejszej adaptacji była osoba, która była gotowa pomóc i rozwiązać niezbędną sprawę.

      Najpierw przyleciałem sam, dwa tygodnie później przyjechała moja rodzina. Po przyjeździe nikt się nie spotyka, przez pierwsze dni mieszkałem w hotelu, czekając na zwolnienie mojego mieszkania. Odebrali mnie z hotelu i przywieźli na miejsce 🙂

      Zabrali z rzeczy niezbędne minimum.

      Z ABH wszystko poszło bardzo szybko. Wszystkie takie kwestie były rozwiązywane wspólnie z pracownikiem firmy. ABH wyznaczył termin odpowiednio wcześnie po przyjeździe, złożyliśmy dokumenty i trzy tygodnie później otrzymaliśmy nasze karty eAT.

      Jak się ustatkowaliśmy:

      Na ten moment mieszkamy w Esslingen, niesamowicie pięknym i czystym mieście, zaledwie 15 minut od Stuttgartu. Nie odczuwamy jeszcze żadnego dyskomfortu z powodu nieznajomości języka, w większości przypadków możemy wytłumaczyć się po angielsku lub w skrajnych przypadkach za pomocą gestów. Jedynym problemem, który w tej chwili istnieje, jest wynajęcie mieszkania. Jest bardzo mało ofert, a popyt jest niesamowicie wysoki. Sytuacja mieszkaniowa w Stuttgarcie jest trochę łatwiejsza, ale chciałbym zostać w Esslingen.

      Krótkie podsumowanie z przybliżonymi datami:

      Połowa października 2014- zobaczyłem ogłoszenie o wyszukiwaniu programistów

      Koniec października - połowa listopada - wywiady z pierwszą firmą

      Połowa listopada - Koniec listopada - Rozmowy kwalifikacyjne z moją obecną firmą, otrzymaj ofertę na rozmowę twarzą w twarz

      20 stycznia - 1 lutego 2015 g.- ubieganie się o wizę krajową, uzyskiwanie paszportów z wizami

      ). Chmura jest przeznaczona do uruchamiania różnych skryptów PHP zgodnie z harmonogramem lub za pośrednictwem interfejsu API. Zazwyczaj skrypty te przetwarzają kolejki, a obciążenie jest „rozkładane” na około 100 serwerów. Wcześniej skupiliśmy się na tym, jak zaimplementowana jest logika sterowania, która odpowiada za równomierne rozłożenie obciążenia na taką liczbę serwerów i generowanie zadań zgodnie z harmonogramem. Ale oprócz tego musieliśmy napisać demona, który byłby w stanie uruchamiać nasze skrypty PHP w CLI i monitorować stan ich wykonania.

      Pierwotnie został napisany w C, tak jak wszystkie inne demony w naszej firmie. Mieliśmy jednak do czynienia z faktem, że znaczna część czasu procesora (około 10%) została zmarnowana, w rzeczywistości na próżno: jest to uruchomienie interpretera i ładowanie „rdzenia” naszego frameworka. Dlatego, aby móc zainicjować interpreter i nasz framework tylko raz, zdecydowano się na przepisanie demona w PHP. Nazwaliśmy to PHP głaz syd (analogicznie do Phproxyd - PHP Proxy Daemon, demona C, który mieliśmy wcześniej). Akceptuje żądania uruchomienia poszczególnych klas i wykonuje fork() dla każdego żądania, a także wie, jak raportować stan wykonania każdego z uruchomień. Ta architektura jest pod wieloma względami podobna do modelu serwera WWW Apache, gdy cała inicjalizacja odbywa się raz w „kreatorze”, a „dzieci” już obsługują żądanie. Jako dodatkowy bonus otrzymujemy możliwość włączenia pamięci podręcznej kodu operacji w CLI, co będzie działać poprawnie, ponieważ wszystkie dzieci dziedziczą ten sam obszar pamięci współdzielonej, co proces główny. Aby zmniejszyć opóźnienia w przetwarzaniu żądania startu, możesz z wyprzedzeniem fork () (model prefork), ale w naszym przypadku opóźnienia fork () wynoszą około 1 ms, co jest dla nas w porządku.

      Ponieważ jednak aktualizujemy kod dość często, ten demon również musi być często uruchamiany ponownie, w przeciwnym razie kod, który jest do niego ładowany, może stać się nieaktualny. Ponieważ każdemu restartowi towarzyszyłoby wiele błędów formy resetowanie połączenia przez peer, w tym odmowy usługi dla użytkowników końcowych (demon przydaje się nie tylko w chmurze, ale także dla części naszego serwisu), postanowiliśmy poszukać sposobów na ponowne uruchomienie demona bez utraty nawiązane połączenia... Jest jedna popularna technika używana do robienia pełne wdzięku przeładowanie dla demonów: wykonywany jest fork-exec i deskryptor z gniazda nasłuchującego jest przekazywany do dziecka. Tym samym nowe połączenia są już akceptowane. Nowa wersja demona, a stare są "zmodyfikowane" przy użyciu starej wersji.

      W tym artykule przyjrzymy się bardziej skomplikowanej opcji. pełne wdzięku przeładowanie: stare połączenia będą nadal przetwarzane przez nową wersję demona, co jest ważne w naszym przypadku, ponieważ w przeciwnym razie uruchomi on stary kod.

      Teoria

      Zastanówmy się najpierw: czy możliwe jest to, co chcemy uzyskać? A jeśli tak, jak można to osiągnąć?

      Ponieważ demon działa pod Linuksem, który jest zgodny z POSIX, dostępne są dla nas następujące opcje:

      1. Wszystkie otwarte pliki i gniazda są numerami odpowiadającymi numerowi otwartego deskryptora. Standardowe wejście, wyjście i strumień błędów mają deskryptory odpowiednio 0, 1 i 2.
      2. Brak znaczących różnic między Otwórz plik, socket i potok nie są (na przykład, możesz pracować z gniazdami przy użyciu wywołań systemowych odczytu/zapisu i sendto/recvfrom).
      3. Po wykonaniu wywołania systemowego fork() wszystkie otwarte deskryptory są dziedziczone, zachowując ich numery oraz pozycje odczytu/zapisu (w plikach).
      4. Podczas wykonywania wywołania systemowego execve() wszystkie otwarte deskryptory są również dziedziczone, a ponadto są zachowywane. PID procesu i dlatego przywiązanie do swoich dzieci.
      5. Lista deskryptorów otwartych procesów jest dostępna w katalogu /dev/fd, który w Linuksie jest dowiązaniem symbolicznym do /proc/self/fd.
      Mamy więc wszelkie powody, by sądzić, że nasze zadanie jest osiągalne i bez większego wysiłku. Więc zacznijmy.

      Łatki PHP

      Niestety jest jeden drobny szczegół, który komplikuje naszą pracę: w PHP nie ma możliwości uzyskania numeru deskryptora pliku dla strumieni i otwarcia deskryptora pliku według numeru (zamiast tego otwierana jest kopia deskryptora pliku, co nie jest odpowiednie dla naszego demona, ponieważ bardzo uważnie monitorujemy otwarte deskryptory, aby nie tworzyć wycieków podczas restartu i podczas uruchamiania procesów potomnych).

      Najpierw zrobimy kilka małych poprawek do kodu PHP, aby dodać możliwość pobierania fd ze strumienia i tak, aby fopen (php: // fd / ) nie otworzył kopii uchwytu (druga zmiana jest niezgodna z obecnym zachowaniem PHP, więc możesz zamiast tego dodać nowy "adres", na przykład php: // fdraw / ):

      Kod poprawki

      diff --git a / ext / standard / php_fopen_wrapper.cb / ext / standard / php_fopen_wrapper.c index f8d7bda..fee964c 100644 --- a / ext / standard / php_fopen_wrapper.c +++ b / ext / standard / php_fopen_wrapper. c @@ -24,6 +24.7 @@ #jeśli HAVE_UNISTD_H #włącz #endif + # włącz #include "php.h" #include "php_globals.h" @@ -296.11 +297.11 @@ php_stream * php_stream_url_wrap_php (php_stream_wrapper * wrapper, char * path, ch "Deskryptory plików muszą być nieujemnymi liczbami mniejszymi niż% d" , drozmiartabeli); zwróć NULL; ) - - fd = dup (fildes_ori); - if (fd == -1) (+ + fd = fildes_ori; + if (fcntl (fildes_ori, F_GETFD) == -1) (php_stream_wrapper_log_error (wrapper, opcje TSRMLS_CC, - "Błąd kopiowania deskryptora pliku% ld; prawdopodobnie nie "t istnieje:" + "Deskryptor pliku% ld nieprawidłowy:" "[% d]:% s", fildes_ori, errno, strerror (errno)); return NULL;) diff --git a / ext / standard / streamsfuncs. cb / ext / standard / streamsfuncs.c index 0610ecf..14fd3b0 100644 --- a / ext / standard / streamsfuncs.c +++ b / ext / standard / streamsfuncs.c @@ -24,6 +24.7 @ @ #include " ext / standard / flock_compat.h" #include "ext / standard / file.h" #include "ext / standard / php_filestat.h" + # include "ext / standard / php_fopen_wrappers.h" #include "php_open_temporary_file .h"# include" ext / standard / basic_functions.h "#include" php_ini.h "@@ -484.6 +485.7 @@ PHP_FUNCTION (stream_get_meta_data) zval * arg1; php_stream * stream; zval * newval; + int tmp_fd; if (zend_parse_parameters_ARGSZEND () TSRMLS_CC, "r", & arg1) == AWARIA) (powrót; @@ -502.6 +504.9 @@ PHP_FUNCTION (stream_get_m eta_data) add_assoc_string (return_value, "wrapper_type", (char *) stream-> wrapper-> wops-> label, 1); ) add_assoc_string (return_value, "stream_type", (char *) stream-> ops-> label, 1); + if (SUCCESS == php_stream_cast (stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (unieważnione *) & tmp_fd, 1) && tmp_fd! = -1) (+ add_assoc_long (return_value, "fd_ mode", tmp_value, ") 1);


      Dodaliśmy pole fd do wyniku zwracanego przez funkcję stream_get_meta_data(), jeśli ma to sens (na przykład w przypadku strumieni zlib pole fd nie będzie obecne). Zastąpiliśmy również wywołanie dup() z przekazanego deskryptora pliku prostym sprawdzeniem. Niestety ten kod nie będzie działał bez modyfikacji w systemie Windows, ponieważ wywołanie fcntl() jest specyficzne dla POSIX, więc pełna poprawka powinna zawierać dodatkowe gałęzie kodu dla innych systemów operacyjnych.

      Demon bez możliwości restartu

      Najpierw napiszmy mały serwer, który może akceptować żądania w formacie JSON i dawać jakąś odpowiedź. Na przykład zwróci liczbę elementów w tablicy, które pojawiły się w żądaniu.

      Demon nasłuchuje na porcie 31337. Wynik powinien wyglądać mniej więcej tak:

      $ telnet localhost 31337 Trying 127.0.0.1 ... Połączony z localhost. Znak ucieczki to „^]”. ("hash": 1) # wprowadzone przez użytkownika "Żądanie miało 1 klucze" ("hash": 1, "cnt": 2) # wprowadzone przez użytkownika "Żądanie miało 2 klucze"

      Użyjemy stream_socket_server(), aby rozpocząć nasłuchiwanie na porcie i stream_select(), aby określić, które deskryptory są gotowe do odczytu/zapisu.

      Najprostszy kod implementacji (Simple.php)

      strumień) * / prywatne strumienie $ =; / ** @var string (client_id => odczyt bufora) * / private $ read_buf =; / ** @var string (client_id => bufor zapisu) * / private $ write_buf =; / ** Zasób @var (client_id => strumień, z którego ma być odczytywany) * / private $ read =; / ** Zasób @var (client_id => strumień gdzie pisać) * / private $ write =; / ** @var int Całkowita liczba połączeń * / private $ conn_count = 0; public function run () ($ this-> listen (); echo "Wejście do głównej pętli \ n"; $ this-> mainLoop ();) funkcja zabezpieczona listen () ($ port = self :: PORT; $ ip_port = " 0.0.0.0:$port "; $ adres =" tcp: // $ ip_port "; $ serwer = stream_socket_server ($ adres, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); if (! $ Server) (fwrite (STDERR, "stream_socket_server failed: $errno $errstr \ n"); exit (1);) $ this-> read = $ server; echo "Nasłuch na adresie $ \ n";) odpowiedź funkcji publicznej ($ stream_id, $ response) ( $ json_resp = json_encode ($ response); echo "stream $ stream_id". $ json_resp. "\ n"; $ this-> write ($ stream_id, $ json_resp. "\ n");) public function write ($ stream_id, $ buf) ($ this-> write_buf [$ stream_id] = $ buf; if (! isset ($ this-> write [$ stream_id])) ($ this-> write [$ stream_id] = $ this-> strumienie [$ stream_id];)) public function accept ($ server) (echo "Akceptacja nowego połączenia \ n"; $ client = stream_socket_accept ($ server, 1, $ peername); $ stream_id = ($ this-> conn_count + +); if (! $ client) (fwrite (STDERR, "Akceptacja nie powiodła się \ n"); return;) stream_set_read_buffer ($ client, 0); stream_set_write_buffer (klient $, 0); stream_set_blocking (klient $, 0); stream_set_timeout (klient $, 1); $ this-> read_buf [$ stream_id] = ""; $ this-> write_buf [$ stream_id] = ""; $ this-> read [$ stream_id] = $ this-> streams [$ stream_id] = $ client; echo "Podłączony strumień $ stream_id: $ peername \ n"; ) private function Disconnect ($ stream_id) (echo "Odłącz strumień $ stream_id \ n"; unset ($ this-> read_buf [$ stream_id], $ this-> write_buf [$ stream_id]); unset ($ this-> streams [ $ stream_id]); unset ($ this-> write [$ stream_id], $ this-> read [$ stream_id]);) funkcja prywatna handleRead ($ stream_id) ($ buf = fread ($ this-> streams [$ stream_id) ], 8192); if ($ buf === false || $ buf === "") (echo "pobrano EOF ze strumienia $ stream_id \ n"; if (puste ($ this-> write_buf [$ stream_id])) ) ($ this-> Disconnect ($ stream_id);) else (unset ($ this-> read [$ stream_id]);) return;) $ this-> read_buf [$ stream_id]. = $ buf; $ this-> processJSONRequests ($ stream_id);) funkcja prywatna processJSONRequests ($ stream_id) (if (! strpos ($ this-> read_buf [$ stream_id], "\ n")) return; $ request = explode ("\ n", $ this -> read_buf [$ stream_id]); $ this-> read_buf [$ stream_id] = array_pop ($ żądania); foreach ($ żądania jako $ req) ($ res = json_decode (rtrim ($ req), true); if () $ res! == false) ($ this-> response ($ stream_id, "Żądanie miało". count ($ res). "keys");) else ($ this-> response ($ stream_id, "Invalid JSON");)) ) private function handleWrite ($ stream_id) (if (! isset ($ this-> write_buf [$ stream_id])) (return;) $ write = fwrite ($ this-> streams [$ stream_id], substr ($ this-> write_buf [$ stream_id], 0, 65536));if ($ write === false) (fwrite (STDERR, "zapis nie powiódł się do strumienia # $ stream_id \ n"); $ this-> Disconnect ($ stream_id); return ;) if ($ write === strlen ($ this-> write_buf [$ stream_id])) ($ this-> write_buf [$ stream_id] = ""; unset ($ this-> write [$ stream_id]); if (pusty ($ this-> read [$ stream_id])) ($ this-> Disconnect ($ stream_id);)) else ($ this-> write_buf [$ stream_id] = substr ($ this-> write_buf [$ stream_id] , $ napisał);)) public function mainLoop() (while (true) ($ read = $ this-> read; $ write = $ this-> write; $except = null; echo "Wybieranie dla". count ($ read). „odczyty”, „count ($ write). „writes \ n”; $ n = stream_select ($ read, $ write, $ z wyjątkiem, ZERO); if (! $ n) (fwrite (STDERR, "Nie można stream_select () \ n"));) if (count ($ read)) (echo "Może czytać od". count ($ read). "streams \ n" ;) if (count ($ write)) (echo "Może pisać do". count ($ write). "streams \ n";) if (isset ($ read)) ($ this-> accept ($ read); unset ($ read);) foreach ($ read as $ stream_id => $ _) ($ this-> handleRead ($ stream_id);) foreach ($ write as $ stream_id => $ _) ($ this-> handleWrite ( $ stream_id);)))) $ wystąpienie = new Simple (); $ instancja-> uruchom ();


      Kod tego demona jest więcej niż standardowy, ale chciałbym zwrócić uwagę na jeden szczegół implementacji: przechowujemy wszystkie bufory odczytu i zapisu z powiązaniem z określonymi połączeniami i wykonujemy przetwarzanie żądania bezpośrednio w tym samym miejscu, w którym odczytujemy żądanie. Jest to ważne, ponieważ jedno z tych żądań może zostać zrestartowane, w którym to przypadku nie dojdzie do przetwarzania kolejnych żądań. Ponieważ jednak nie przeczytaliśmy jeszcze żądań, następnym razem stream_select() z tych samych deskryptorów zwróci ten sam wynik. Dzięki temu nie stracimy ani jednego żądania, jeśli uruchomimy ponownie bezpośrednio z programu obsługi poleceń (z wyjątkiem przypadku, gdy do tego samego połączenia zostanie wysłanych kilka poleceń naraz, a jednym z tych poleceń będzie restart).

      Jak więc umożliwić ponowne uruchomienie demona?

      Demon z restartem i zapisem nawiązanych połączeń

      Nasz najprostszy przykład nie wiedział, jak zrobić nic pożytecznego, więc napiszmy jeszcze demona, o którym mówiliśmy na samym początku. Chcemy otrzymać coś takiego (polecenia są wysyłane do demona w postaci "nazwa_komendy [dane-JSON]", odpowiedź ma postać JSON):
      $ telnet localhost 31337 Trying 127.0.0.1 ... Połączony z localhost. Znak ucieczki to „^]”. # natychmiast poproś demona o ponowne uruchomienie restartu # odpowiedź zostanie wysłana przez już zrestartowany demon "Ponownie uruchomiono pomyślnie" # uruchom test klasy run ("hash": 1, "params":, "class": "TestClass1") # uruchomione pomyślnie ("error_text": "OK") # zrestartuj demona ponownie (jego dziecko TestClass1 nadal działa) restart "Ponownie uruchomiono pomyślnie" # sprawdź status zadania: nadal działa check ("hash": 1) ("tekst_błędu" : "Nadal działa") # odczekaj 5 sekund i sprawdź ponownie: klasa TestClass1 zadziałała pomyślnie check ("hash": 1) ("retcode": 0) # demon pamięta wszystkie uruchomienia, więc musisz bezpłatnie sprawdzić ("hash" ": 1) ("retcode": 0) free ("hash": 1) ("error_text": "OK") restart "Ponownie uruchomiono pomyślnie" # Zaktualizowałem kod, więc za drugim razem widzimy inną odpowiedź na restart restart ("error_text": "Ponownie uruchomiono pomyślnie") bye Połączenie zamknięte przez obcy host.

      Pomysł na restart jest prosty: utworzymy plik ze wszystkimi niezbędnymi informacjami, a po uruchomieniu spróbujemy go odczytać i przywrócić otwarte deskryptory plików.

      Najpierw napiszmy kod do zapisu do pliku restartu:

      Echo "Tworzenie pliku restartu ... \ n"; if (! $ res = $ this-> getFdRestartData ()) (fwrite (STDERR, "Nie udało się uzyskać ponownego uruchomienia danych FD, wychodzenie, łagodny restart nie jest obsługiwany \ n"); exit (0);) / * Zamknij wszystkie dodatkowe deskryptory plików, których nie znamy, w tym opendir() deskryptor :) * / $ dh = opendir ("/ proc / self / fd"); $ fds =; while (false! == ($ plik = readdir ($ dh))) (if ($ plik === ".") continue; $ fds = $ plik;) foreach ($ fds jako $ fd) (if (!!) isset ($ this->known_fds [$ fd])) (fclose (fopen ("php: // fd /". $ fd, "r +"));)) $ content = serialize ($ res); if (file_put_contents (self :: RESTART_DIR. self :: RESTART_FILENAME, $ content)! == strlen ($ content)) (fwrite (STDERR, "Nie można w pełni napisać pliku restartu \ n")); unlink (self :: RESTART_DIR. siebie :: RESTART_FILENAME);)

      Kod do pobrania tablicy danych (funkcja getFdRestartData ()) jest pokazany poniżej:

      $ res =; foreach (self :: $ restart_fd_resources as $ prop) ($ res [$ prop] =; foreach ($ this -> $ prop as $ k => $ v) ($ meta = stream_get_meta_data ($ v); if (! isset ($ meta ["fd"])) (fwrite (STDERR, "Brak metadanych fd w strumieniu dla zasobu $ v (klucz $ k w $ prop), got". var_export ($ meta, true). "\ n") ; return false;) $ res [$ prop] [$ k] = $ meta ["fd"]; $ this->known_fds [$ meta ["fd"]] = true;)) foreach (self :: $ restart_fd_props as $ prop) ($ res [$ prop] = $ this -> $ prop;) return $ res;
      Kod uwzględnia, że ​​mamy 2 rodzaje właściwości:

      1. Właściwości zawierające zasoby z połączeniami: $ restart_fd_resources = ["odczyt", "zapis", "strumienie"].
      2. Właściwości zawierające bufory i inne informacje o połączeniu, które mogą być "serializowane" nieprzetworzone: $ restart_fd_props = ["read_buf", "write_buf", "conn_count"].
      Zapamiętujemy również wszystkie fd zapisane w pliku restartu i zamykamy wszystkie inne (jeśli są), ponieważ w przeciwnym razie możemy przeciec deskryptory plików.

      Następnie musimy załadować ten plik na początku i dalej używać otwartych deskryptorów, jakby nic się nie stało :). Kod dla dwóch funkcji (ładowanie pliku restartu i wczytywanie informacji o deskryptorach plików) jest pokazany poniżej:

      If (! File_exists (self :: RESTART_DIR. Self :: RESTART_FILENAME)) (return;) echo "Znaleziono ponownie plik, próbuję go adoptować \ n"; $ content = file_get_contents (self :: RESTART_DIR. self :: RESTART_FILENAME); unlink (self :: RESTART_DIR. self :: RESTART_FILENAME); if ($ content === false) (fwrite (STDERR, "Nie można odczytać pliku restartu \ n"); return;) $ res = unserialize ($ content); if (! $ res) (fwrite (STDERR, "Nie można cofnąć serializacji zawartości pliku restartu"); return;) foreach (self :: $ restart_props as $ prop) (if (! array_key_exists ($ prop, $ res)) (fwrite (STDERR, "Brak właściwości $ prop w pliku restartu \ n"); Continue;) $ this -> $ prop = $ res [$ prop];) $ this-> loadFdRestartData ($ res);

      Funkcja loadFdRestartData () rozszerzająca z powrotem tablicę deskryptorów plików:

      $ fd_resources =; foreach (self :: $ restart_fd_resources as $ prop) (if (! isset ($ res [$ prop])) (fwrite (STDERR, "Właściwość" $ prop "nie występuje w zasobach fd restartu \ n"); ) $ pp =; foreach ($ res [$ prop] as $ k => $ v) (if (isset ($ fd_resources [$ v])) ($ pp [$ k] = $ fd_resources [$ v];) else ($ fp = fopen ("php: // fd /". $ v, "r +"); if (! $ fp) (fwrite (STDERR, "Nie udało się otworzyć fd = $ v, wyjście \ n") ; exit (1);) stream_set_read_buffer ($ fp, 0); stream_set_write_buffer ($ fp, 0); stream_set_blocking ($ fp, 0); stream_set_timeout ($ fp, self :: CONN_TIMEOUT); $ fd_resources [$ v] = $ fp ; $ pp [$ k] = $ fp;)) $ this -> $ prop = $ pp;) foreach (self :: $ restart_fd_props as $ prop) (if (! isset ($ res [$ prop])) ( fwrite (STDERR, "Właściwość" $ prop "nie występuje we właściwościach fd restartu \ n"); Continue;) $ this -> $ prop = $ res [$ prop];)
      Ponownie ustawiamy wartości read_buffer i write_buffer dla deskryptorów otwartych plików oraz ustawiamy limity czasu. Co dziwne, po tych manipulacjach PHP dosyć spokojnie wykonuje accept() na tych deskryptorach plików i kontynuuje odczyt/zapis do nich normalnie, mimo że nie wie, że są to gniazda.

      Na koniec musimy napisać logikę uruchamiania i monitorowania stanu wykonania pracowników. Ponieważ nie ma to związku z tematem artykułu, pełna implementacja demona została opublikowana w repozytorium github, do którego link znajduje się poniżej.

      Wniosek

      W tym artykule opisano więc implementację demona, który komunikuje się za pomocą protokołu JSON i jest w stanie uruchamiać dowolne klasy w osobnych procesach z monitorowaniem procesu ich wykonywania. Do prowadzenia zajęć indywidualnych wykorzystuje się model widelec () na żądanie, w związku z tym, aby przetworzyć żądanie, nie trzeba ponownie uruchamiać interpretera i ładować frameworka, podczas gdy możliwe staje się użycie pamięci podręcznej opcode w CLI. Ponieważ demon musi być restartowany za każdym razem, gdy aktualizowany jest kod, konieczne jest zapewnienie mechanizmu płynnego restartu tego demona (w naszej firmie kod jest czasami aktualizowany co kilka minut w postaci „hotfixów”).

      Ponowne uruchomienie następuje poprzez wykonanie wywołania systemowego execve(), w wyniku czego wszystkie dzieci pozostają dołączone do rodzica (ponieważ PID procesu nie zmienia się podczas execve()). Ponadto zapisywane są wszystkie deskryptory otwartych plików, co pozwala na dalsze przetwarzanie żądań od użytkowników w już otwartych połączeniach. Wszystkie bufory sieciowe, informacje o uruchomionych dzieciach i otwartych deskryptorach są zapisywane w osobnym pliku restartu, który jest odczytywany przez nową instancję demona, po czym praca jest kontynuowana w standardowej pętli zdarzeń.

      Pełny kod implementacji można zobaczyć w serwisie GitHub pod następującym adresem URL.