Éxito infundado php. Un ejemplo sencillo del uso de PHP y AJAX. Cómo todo empezó

Visa: Blaue Karte UE

Al momento de presentar los documentos 29 años.

Desde Astracán

Ciudad de la embajada: Moscú

Universidad, especialidad: Universidad Técnica Estatal de Astrakhan, soporte integrado seguridad de información sistemas automatizados

Idiomas: Inglés intermedio

Cómo todo empezó:

Hace tiempo que tengo el deseo de mudarme a algún lugar. Es cierto que se consideraron principalmente países cálidos con mar. Dos veces sondeamos seriamente la posibilidad de mudarnos a Montenegro o Bulgaria. Como resultado, en el último momento, por una razón u otra, cambiaron de opinión. La última vez fue en septiembre de 2014, después de serios preparativos para vender el coche.

En octubre, vi accidentalmente un anuncio que buscaba programadores para trasladarse a Alemania. En aquel momento no tenía idea de la existencia de la Tarjeta Azul y consideraba que Alemania era un país con una política migratoria increíblemente estricta hacia los ciudadanos no pertenecientes a la UE.

Con cierto escepticismo y desconfianza, escribí por Skype. Al otro lado de la pantalla respondió una reclutadora (Alina), que se ocupa de la selección de personal de TI con posterior traslado a Alemania. En el momento de nuestra primera conversación, estábamos contratando programadores para una gran tienda online con sede en Berlín. Envié mi currículum y esperé.

Después de un tiempo, Alina dijo que su colega de Alemania hablaría conmigo para evaluar el nivel del idioma y su idoneidad. La entrevista fue más bien una conversación con dos problemas lógicos, duró unos 30 minutos por Skype. Después de lo cual me dijeron que esperara. Aproximadamente una semana después se programó la primera entrevista técnica. La entrevista técnica también fue vía Skype con uno de los desarrolladores de la empresa. En mi opinión, fue bastante exitoso, pero una semana después me dijeron que no era apto. Por cierto, ni un solo candidato de Alina aprobó por determinadas razones.

Cómo resultó todo:

Estaba un poco molesto, pero la vida continúa. Y unos días después, Alina dijo que tenían un nuevo cliente de Stuttgart que buscaba desarrolladores y que yo tenía programada una entrevista. La primera parte de la entrevista se comparte con el responsable de los departamentos de TI y RRHH. Conversación general sobre la experiencia, usted y la empresa, muchas risas y chistes por ambas partes. Al parecer, les gustó mi humor, por lo que unos días después me programaron una entrevista técnica con un potencial supervisor inmediato. Esta parte de la entrevista me sorprendió un poco, porque como dijo más tarde uno de los candidatos, fue como "una conversación entre dos programadores tomando un vaso de cerveza". Esa noche recibí una invitación para una entrevista personal en la oficina.

En ese momento no tenía una visa Schengen abierta. Se recogieron urgentemente los documentos necesarios. En el centro de visas alemán en Moscú solicité un visado urgente y al día siguiente recogí mi pasaporte con el visado. Solicité una visa de negocios basándose en una invitación que me enviaron desde Esslingen; es solo una carta en la que me invitan a comunicarme y en la que se indica claramente que la empresa se encarga de todos los asuntos financieros relacionados con vuelos, traslados, comidas y alojamiento. .

La comunicación personal en la oficina tuvo lugar con tres principales responsables de TI de la empresa. La primera parte es nuevamente simplemente comunicación sobre experiencia, habilidades y comprensión general de algunos temas. El segundo está en la computadora. Para ser honesto, estas son tareas muy, muy fáciles en el “nivel junior con experiencia en pruebas” :). Las tareas estaban hechas. Después de esto, almuerzo e inmediatamente una oferta en forma de dos copias de los contratos de trabajo (cargo Senior PHP Developer) firmados por la empresa. Me tomé un tiempo para pensar y dije que respondería dentro de una semana.

Se tomó la decisión y comencé a prepararme para solicitar una visa.

Cómo nos mudamos:

La empresa pagó el vuelo para mí y mi familia (esposa e hija de dos años y medio), nos alquiló un apartamento durante los primeros tres meses (en mi caso, un lugar ideal con vistas a la plaza central de la ciudad, Marktplatz) y nos asignó una persona para ayudar por primera vez. Esta no es una agencia de reubicación en estado puro, pero resolvimos todos los problemas que surgieron a través de ella. Fui el primer empleado de la empresa fuera de la UE, por lo que muchas preguntas se centraron en mí. Ahora, además de mí, en la empresa trabaja otro chico de Kiev (llegó un mes después que yo) y un desarrollador de Odessa se está preparando para mudarse. Todos ellos también fueron empleados con la ayuda de Alina.

Aquí me gustaría decir que estoy muy agradecido con Alina, quien resolvió todos los problemas que tuve durante el proceso de empleo. Tuve mucha suerte de que en todas las etapas del empleo y posterior adaptación hubiera una persona dispuesta a ayudar y resolver el problema necesario.

Al principio llegué sola y dos semanas después llegó mi familia. Nadie te recibe al llegar; los primeros días viví en un hotel, esperando que mi apartamento estuviera disponible. Me recogieron en el hotel y me llevaron al lugar :)

Sacaron de las cosas el mínimo necesario.

Con ABH todo fue muy rápido. Todos estos problemas se resolvieron conjuntamente con un empleado de la empresa. ABH fijó el plazo bastante pronto después de nuestra llegada, presentamos los documentos y tres semanas después recibimos nuestras tarjetas eAT.

Cómo nos instalamos:

En este momento Vivimos en Esslingen, una ciudad increíblemente hermosa y limpia, ubicada a sólo 15 minutos de Stuttgart. Aún no experimentamos ninguna molestia por no conocer el idioma; en la mayoría de los casos podemos comunicarnos en inglés o, en casos extremos, con gestos. El único problema que existe en este momento es el alquiler de un apartamento. Hay muy pocas ofertas y la demanda es increíblemente alta. La situación de la vivienda en Stuttgart es un poco más fácil, pero me gustaría quedarme en Esslingen.

Breve resumen con fechas aproximadas:

Mediados de octubre de 2014: vi un anuncio que buscaba programadores.

Finales de octubre - mediados de noviembre - entrevistas con la primera empresa

Mediados de noviembre - finales de noviembre - entrevistas con mi empresa actual, recibiendo una oferta para una entrevista en persona

20 de enero - 1 de febrero de 2015: solicitud de visa nacional, recepción de pasaportes con visas

). La nube está diseñada para ejecutar varios scripts PHP de forma programada o mediante una API. Como regla general, estos scripts procesan colas y la carga se "reparte" en aproximadamente 100 servidores. Anteriormente nos centramos en cómo se implementa la lógica de control, que se encarga de distribuir uniformemente la carga entre tal cantidad de servidores y generar tareas según un cronograma. Pero, además de esto, necesitábamos escribir un demonio que pudiera ejecutar nuestros scripts PHP en la CLI y monitorear el estado de su ejecución.

Fue escrito originalmente en C, como todos los demás demonios de nuestra empresa. Sin embargo, nos enfrentamos al hecho de que una parte importante del tiempo del procesador (alrededor del 10%) se desperdició esencialmente: iniciando el intérprete y cargando el "núcleo" de nuestro marco. Por lo tanto, para poder inicializar el intérprete y nuestro marco solo una vez, se decidió reescribir el demonio en PHP. Lo llamamos PHP roca syd (similar a Phproxyd - PHP Proxy Daemon, un demonio C que teníamos antes). Acepta solicitudes para ejecutar clases individuales y realiza una bifurcación() en cada solicitud, y también puede informar el estado de ejecución de cada una de las ejecuciones. Esta arquitectura es en muchos aspectos similar al modelo de servidor web Apache, cuando toda la inicialización se realiza una vez en el "maestro" y los "secundarios" participan en el procesamiento de la solicitud. Como beneficio adicional, tenemos la capacidad de habilitar el caché de código de operación en la CLI, lo que funcionará correctamente ya que todos los hijos heredan la misma área de memoria compartida que el proceso maestro. Para reducir los retrasos al procesar una solicitud de lanzamiento, puede realizar fork() con antelación (modelo prefork), pero en nuestro caso los retrasos para fork() son de aproximadamente 1 ms, lo que nos conviene bastante bien.

Sin embargo, dado que actualizamos el código con bastante frecuencia, este demonio también debe reiniciarse con frecuencia; de lo contrario, el código que se carga en él puede quedar obsoleto. Dado que cada reinicio estaría acompañado de muchos errores como conexión restablecida por par, incluida la denegación de servicio a los usuarios finales (el demonio es útil no solo para la nube, sino también para parte de nuestro sitio), decidimos buscar formas de reiniciar el demonio sin perder las conexiones ya establecidas. Hay una técnica popular que se utiliza para hacer recarga elegante para demonios: fork-exec finaliza y se pasa al niño un descriptor del socket de escucha. Así, ya se aceptan nuevas conexiones. nueva versión demon, y los antiguos se “modifican” usando la versión antigua.

En este artículo veremos una opción más complicada. recarga elegante: Las conexiones antiguas seguirán siendo procesadas por la nueva versión del demonio, lo cual es importante en nuestro caso, porque de lo contrario ejecutará el código antiguo.

Teoría Pensemos primero: ¿es posible lo que queremos conseguir? Y si es así, ¿cómo lograrlo?

Dado que el demonio se ejecuta en Linux, que es compatible con POSIX, tenemos disponibles las siguientes opciones:

  • Todos los archivos y sockets abiertos son números correspondientes al número del descriptor abierto. La entrada, salida y flujo de error estándar tienen los descriptores 0, 1 y 2, respectivamente.
  • No hay diferencias significativas entre abrir documento, socket y pipe (tubería) no lo son (por ejemplo, puede trabajar con sockets usando llamadas al sistema de lectura/escritura y sendto/recvfrom).
  • Haciendo llamada al sistema fork() todos los descriptores abiertos se heredan, conservando sus números y posiciones de lectura/escritura (en archivos).
  • Cuando se ejecuta la llamada al sistema execve(), todos los identificadores abiertos también se heredan y, además, se conserva el PID del proceso y, por lo tanto, la afinidad con sus hijos.
  • La lista de descriptores de procesos abiertos está disponible en el directorio /dev/fd, que en Linux es un enlace simbólico a /proc/self/fd.
  • Por lo tanto, tenemos todas las razones para creer que nuestra tarea puede lograrse y sin mucho esfuerzo. Entonces, comencemos. Parches de PHP Desafortunadamente, hay un pequeño detalle que complica nuestro trabajo: en PHP no hay forma de obtener el número del descriptor de archivo para las secuencias y abrir el descriptor de archivo por número (en su lugar, una copia del descriptor de archivo). se abre, lo que para nuestro demonio no es adecuado, ya que monitoreamos con mucho cuidado los identificadores abiertos para no crear fugas durante los reinicios y al iniciar procesos secundarios).

    Primero, haremos un par de pequeños parches al código PHP para agregar la capacidad de obtener fd de una secuencia y asegurarnos de que fopen(php://fd/) no abra una copia del identificador (el segundo cambio es incompatible con el comportamiento actual de PHP, por lo que puede agregar una nueva “dirección” en su lugar, por ejemplo, php://fdraw/):

    Código de parche

    diff --git a/ext/standard/php_fopen_wrapper.c b/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 @@ #if HAVE_UNISTD_H #include #endif +#include #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 "Los descriptores de archivos deben ser números no negativos menores que %d", dtablesize); devolver NULO; ) - - fd = dup(fildes_ori); - if (fd == -1) ( + + fd = fildes_ori; + if (fcntl(fildes_ori, F_GETFD) == -1) ( php_stream_wrapper_log_error(wrapper, opciones TSRMLS_CC, - "Error al duplicar el descriptor de archivo %ld; posiblemente no 't "t existe: " + "Descriptor de archivo %ld no válido: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL diff --git a/ext/standard/streamsfuncs; c b/ext/standard/streamsfuncs.c índice 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; zval *newval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FALLO) ( return; @@ -502.6 +504.9 @@ PHP_FUNCTION(stream_get_meta_data) add_assoc_string(return_value, "wrapper_type", ( char *)stream->wrapper ->wops->etiqueta, 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, (void*)&tmp_fd, 1) && tmp_fd != -1) ( + add_assoc_long(return_value, "fd", tmp_fd); + ) add_assoc_string(return_value, "modo ", flujo->modo, 1);


    Agregamos el campo fd al resultado devuelto por stream_get_meta_data() si tiene sentido (por ejemplo, para transmisiones zlib el campo fd no estará presente). También reemplazamos la llamada dup() del descriptor de archivo pasado con una simple verificación del mismo. Desafortunadamente, este código no funcionará sin modificaciones en Windows, ya que la llamada fcntl() es específica de POSIX, por lo que el parche completo debe contener ramas de código adicionales para otros sistemas operativos. Un demonio sin la capacidad de reiniciar. Primero, escribamos un pequeño servidor. que podrá aceptar solicitudes formato JSON y dar alguna respuesta. Por ejemplo, devolverá la cantidad de elementos de la matriz que figuran en la solicitud.

    El demonio escucha en el puerto 31337. El resultado debería ser algo como este:

    $ telnet localhost 31337 Intentando 127.0.0.1... Conectado a localhost. El carácter de escape es "^]". ("hash":1) # entrada del usuario "La solicitud tenía 1 clave" ("hash":1,"cnt":2) # entrada del usuario "La solicitud tenía 2 claves"

    Usaremos stream_socket_server() para comenzar a escuchar en un puerto y stream_select() para determinar qué identificadores están listos para leer/escribir.

    El código de implementación más simple (Simple.php)

    Instrucciones