Menú
Está libre
Registro
hogar  /  EN/ Éxito irrevocable php. Un ejemplo simple de PHP y AJAX

Éxito irrevocable php. Un ejemplo simple de PHP y AJAX

¡Saludos, querido amigo!

"¿Qué es el éxito en la vida para ti?"

Por favor, piénsalo, detente un momento.

Bien, ahora déjame ayudarte. Sobre lo que no es el éxito, escribí en el boletín anterior. Descartaremos estas nociones de una vez.

El éxito es la armonía espiritual.
El éxito es ser feliz.
El éxito es la realización de uno mismo y la revelación del propio potencial.
El éxito es la realización en la vida.
El éxito es hacer lo que amas que te enciende y puedes hacerlo las 24 horas del día.
El éxito es darte a los demás y hacer de este mundo un lugar mejor y a los demás más felices.
.

El éxito está indisolublemente ligado al estado de ánimo. Nuestra alma vino a este mundo para disfrutar y realizarse, y nosotros (nuestra mente, nuestro cuerpo, nuestra conciencia) debemos apoyarla en esto. Cuando nuestra alma crea y se realiza a sí misma, nos sentimos felices. Cuando sentimos y vemos que la creación de nuestra alma y lo que hacemos es de gran beneficio para otras personas, sentimos dicha. Esto es lo que se llama éxito. El éxito es la plenitud de la vida.

Cualquier realización de los talentos del alma es posible solo gracias a otras personas.. El alma no crea para sí misma. Ella crea para los demás, para ayudarlos y hacer que la vida de los demás sea plena y darles una parte de su felicidad. Una persona feliz transmite una parte de su felicidad a los demás, una persona infeliz transmite su infelicidad a los demás. ¡Evita a las personas infelices!

Si de repente todas las personas desaparecen en un momento, la autorrealización se vuelve imposible: ¿de qué sirve escribir libros, porque nadie los leerá?, ¿de qué sirve crear nuevos modelos de ropa, porque nadie los usará?, ¿de qué sirve? el punto de construir nuevas casas, en las que nadie va a vivir?

Obviamente esto no tiene sentido.

Aquí se manifiesta la naturaleza dual del éxito: el alma crea y se realiza a sí misma, y ​​también ayuda a otras personas a ser más felices.
La mayoría definición precisa El éxito que podría dar sonaría así: el éxito es la realización de los verdaderos talentos de uno que hacen que nuestro mundo sea mejor, más perfecto y que las personas sean más felices.

Quiero que te des cuenta profundamente de que las personas que viven solo para sí mismas y acumulan riquezas solo para sí mismas son infelices. Recolectan estas riquezas para llenar el vacío espiritual que se ha formado como resultado de una vida sin sentido. Pero este vacío solo se puede llenar con amor, aportando valor a otras personas. El alma es feliz cuando se entrega sin exceso a hacer de este mundo un lugar mejor. Y de qué sirve toda esa riqueza que una persona ha acumulado cuando fallece, porque no somos duraderos. El alma viene a crear valor, a realizarse y luego vuelve a "casa". Si no crea este valor, sino que se dedica a otra cosa, se siente mal. Siente que ha venido a este mundo y no está haciendo lo que quiere. Y la razón de esto es nuestra mente: está cegada por el "éxito" en la comprensión general de esta palabra. Persigue lo ilusorio, y cuando logra esto, si es que lo logra, comprende la falta de sentido de lo que ha logrado.

¿Qué es el éxito en términos generales?
- riqueza (dinero, cosas materiales)
- fama, poder, popularidad
- estado

Pero mira, todo viene del ego. Una persona quiere sentir su importancia, pero no entiende que la riqueza, la fama, el estatus son una ilusión. Son como el agua de mar, que por mucho que bebas, nunca saciarás tu sed. Porque la gente toda su vida y persiguiéndolos. Piensan que si gano tanto dinero y seré feliz, si llego al nivel de ingresos de $ 100,000 al año, entonces seré feliz, si subo al escenario y canto, seré feliz, conseguiré casado, tendré hijos... Usted puede comprobarlo, pero puedo decir con 100% de certeza que no será feliz. Además, tu nivel de felicidad será aún menor. Te estás alejando de tu llamado, y al darte cuenta de esto, el alma se vuelve aún más infeliz. Cuanta más riqueza, fama, estatus obtienes, más control sobre la vida toma la mente y más se relega el papel del alma. Pero la verdadera felicidad viene del alma!!!

El éxito es la armonía entre el alma y la mente. El papel de la mente es ayuda alma para autorrealizarse. Estamos priorizando de la manera incorrecta. Ponemos el cuerpo efímero y las cosas materiales en primer lugar, y ponemos el alma inmortal y las riquezas inagotables en último lugar. La Biblia dice: "Hazte rico en el cielo, no en la tierra". Nuestro cuerpo es el vehículo del alma.. El alma está conectada con la Mente Superior y solo ella puede comprender lo que se necesita para este mundo. El universo favorece a las personas que siguen su propio camino.. Tu camino es el que menos energía consume, y en nuestro mundo todo fluye por el camino de menor resistencia. Siempre digo que el éxito es el curso normal de los acontecimientos. El fracaso es una desviación de la norma. Si ahora no tiene el éxito que desea, entonces no está haciendo lo que estaba destinado a hacer. El alma y la mente están en desacuerdo. Y cuanto más esta discordia, más infeliz es la persona.

Pero no creas que estoy diciendo que una persona no necesita cosas materiales. es muy necesario Y he aquí por qué: cuando una persona no tiene dinero, se ve obligada a ir a trabajar y participar en algún tipo de "estupidez". Una persona dedica 10 horas al día para ganar dinero, pero al hacerlo no se da cuenta de sí misma. El jefe es la persona que se realiza a expensas de ti. (Estoy hablando de cómo sucede en la mayoría de los casos. La mayoría de las personas odian sus trabajos, pero trabajan porque necesitan dinero para sobrevivir).

Las cosas materiales crean consuelo para el alma. Las cosas materiales equipan este mundo para el alma. Es mucho más agradable para el alma crear obras maestras en lugares que la inspiran. Es mucho mejor pintar un cuadro en una casa junto al mar que en un "pozo de basura". El alma necesita paz y consuelo para poder crear. Pero, ¿qué tipo de paz puede haber si la familia no tiene suficiente dinero y todos los días el esposo y la esposa juran sobre esto?

El alma necesita tiempo para expresarse. Solo después de que haya pasado un tiempo, el valor creado por el alma puede venderse y venderse a cientos, o incluso miles de veces más caro de lo que una persona recibe en el trabajo. Pero se necesita tiempo para crear ese tipo de valor. Personalmente, me tomó 5 meses alcanzar unos escasos ingresos. Después de 8 meses, mi sitio comenzó a generar ingresos con los que una familia pobre ya podría vivir. Y solo después de 17 meses, mi sitio comenzó a generar ingresos, que ya reemplazarán los ingresos de un trabajo muy bien pagado.

Se necesitaron 17 meses para reemplazar el trabajo. ¡Pero ahora soy libre! Estoy haciendo lo que amo y esto es solo el comienzo. Para mis sueños no hay límites - y por lo tanto para mí no hay límites. Cuando te ocupas de tus propios asuntos, tus ingresos solo están limitados por tu imaginación y nada más. ¿Quién gana $1,000,000 al año en el trabajo? Sí, puede haber unidades. Pero haciendo lo tuyo, incluso esto no es una capilla.
Lo material es importante, pero sólo para satisfacer las necesidades de la vida.

Seré honesto: sin ingresos, es más difícil crear y crear obras maestras. La mente dice constantemente: “lo que haces está bien, pero ¿para qué vamos a vivir?”. Y esta pregunta distrae constante y fuertemente de la creatividad. Él nos quita la felicidad. Para apagar este diálogo, su pasatiempo favorito debe traer dinero. Por supuesto, la mente entonces comienza a hacer otras preguntas, pero ¿qué mas dinero trae una cosa favorita, menos enfermizas y distraídas se vuelven estas preguntas.

A menudo, las personas trabajan en el trabajo, ganan dinero, pero aún tienen pasatiempos. ¿Qué es un pasatiempo?
Un pasatiempo es una ocupación para el alma que no genera ingresos. Pero, ¿por qué no convertir un hobby en un trabajo? Las personas más felices son aquellas cuyo hobby es el trabajo.. Están haciendo lo que aman sin parar.
Todo lo que hablo, sobre el trabajo, sobre el dinero, quiero transmitirles dos pensamientos importantes: 1) El alma y la mente deben estar en armonía.
2) Lo intangible siempre debe estar primero

¡El enfoque debe estar solo en lo intangible! Material a entregar, como consecuencia. Estas son las prioridades correctas en la vida:
felicidad -> salud -> riqueza Y mucha gente vive de acuerdo con el esquema.
riqueza->salud->felicidad
Y lo que es peor, hay gente que vive del esquema
riqueza->riqueza->riqueza

No es de extrañar que no sean felices. Estas personas tienen millones, pero no tienen amigos, tienen problemas familiares. Tienen problemas con la gente. Porque piensan que todas las personas que los rodean están con ellos solo por su dinero y solo. No sé ustedes, pero yo no querría tanta felicidad. Cuando las prioridades en la vida se establecen correctamente, la riqueza surge como resultado. No tiene sentido centrarse en ello. Altos niveles de felicidad y salud conducen inevitablemente a altos niveles de ingresos.

Las cosas materiales y nuestra riqueza solo pueden servir como una adición a nuestra felicidad. No pueden servir como base. Cuál es la base, ya lo hemos discutido con usted anteriormente.

Un conjunto de pares clave/valor que personalizan la consulta AJAX. Todos los parámetros son opcionales.. Está permitido, pero no recomendado, establecer un valor predeterminado para cualquier parámetro utilizando el método $.ajaxSetup().
Método $.ajax() admite las siguientes opciones:

    acepta(predeterminado: depende de tipo de datos).

    Tipo: PlainObject.
    El conjunto de pares clave/valor que se envían a aceptar encabezado de solicitud. Este encabezado le dice al servidor qué tipo de respuesta recibirá la solicitud. Tenga en cuenta que el valor del parámetro especificado en tipo de datos(el tipo de datos que esperamos del servidor) se compara con el especificado en el parámetro. Además, para el correcto procesamiento de la respuesta del servidor, es necesario en el parámetro convertidores especifique una función que devuelva el valor de respuesta convertido. Por ejemplo: $.ajax(( acepta: (mitipopersonalizado: " aplicación/x-algún-tipo-personalizado" } , // especificar como procesar la respuesta convertidores: ("texto mycustomtype": función ( resultado) { // devuelve el valor de respuesta convertido devolver nuevo resultado; ) ) , // Tipo de datos esperado ("mycustomtype") tipo de datos: "mitipopersonalizado" ) );

    asíncrono(predeterminado: verdadero).

    Tipo: Booleano.
    De forma predeterminada, todas las solicitudes se envían de forma asíncrona, si necesita organizar solicitudes síncronas, establezca este parámetro en false . Tenga en cuenta que las solicitudes entre dominios y el elemento, el parámetro tipo de datos lo que importa jsonp no admite solicitudes en modo síncrono. Tenga en cuenta que al usar solicitudes sincrónicas puede bloquear temporalmente el navegador deshabilitando cualquier acción mientras la solicitud está activa.

    antesEnviar. Tipo: Función (jqXHR jqXHR, PlainObject ajustes).
    Una función de devolución de llamada que se llamará antes de que se realice la solicitud AJAX. La función le permite cambiar el objeto jqXHR (en jQuery 1.4.x el objeto XMLHTTPRequest) antes de enviarlo. El objeto jqXHR es un complemento que amplía el objeto XMLHttpRequest, el objeto contiene muchas propiedades y métodos que le permiten obtener más información completa sobre la respuesta del servidor, así como el objeto contiene métodos Promise. Si la función antesEnviar devuelve falso, entonces se cancelará la solicitud de AJAX. A partir de la versión jQuery 1.5 función antesEnviar se llamará independientemente del tipo de solicitud.

    cache(predeterminado: verdadero, para tipo de datos "guion" y jsonp falso ).

    Tipo: Booleano.
    Si se establece en false , obligará a que el navegador no almacene en caché las páginas solicitadas. Tenga en cuenta que false solo funcionará correctamente con CABEZA y OBTENER peticiones.

    completo.

    Tipo: Función (jqXHR jqXHR, Cuerda estado del texto).
    Una función que se llama cuando finaliza la solicitud (la función se ejecuta después de los eventos AJAX "éxito" o "error"). Se pasan dos parámetros a la función: jqXHR(en objeto jQuery 1.4.x Solicitud XMLHTTP) y una cadena correspondiente al estado de la solicitud ( "éxito", "no modificado", "sin contenido", "error", "se acabó el tiempo", "aborto", o "parsererror"). A partir de jQuery 1.5, el parámetro completo puede tomar una serie de funciones que se llamarán a su vez.

    contenido.

    Tipo: PlainObject.
    Un objeto que consta de pares de cadenas/regex que definen cómo jQuery procesará (analizará) la respuesta según el tipo de contenido. Agregado en jQuery 1.5.

    tipo de contenido(defecto: "aplicación/x-www-formulario-urlencodificado; juego de caracteres=UTF-8").

    Tipo: booleano o cadena.
    Especifica el tipo de contenido que se especifica en la solicitud al pasar datos al servidor. A partir de jQuery 1.6, se permite especificar el valor false , en cuyo caso jQuery no pasa el campo del encabezado. tipo de contenido en absoluto.

    contexto.

    Tipo: PlainObject.
    Al ejecutar funciones de devolución de llamada AJAX, su contexto de ejecución es el objeto de ventana. Parámetro contexto le permite configurar el contexto de ejecución de una función para que $(this ) se refiera a un elemento u objeto DOM específico. Por ejemplo: $.ajax(( URL: "prueba.html" contexto: $(".miClase"), // nuevo contexto de ejecución de la función éxito:función()( // si la solicitud es exitosa, llama a la función$(esto).html("Todo está bien"); // agregar contenido de texto al elemento con class.myClass } } );

    convertidores

    Valores predeterminados:
    ( "* texto ": ventana.Cadena, // cualquier tipo de texto"texto html": true, // texto a html "texto json": jQuery.parseJSON, // texto a JSON "texto xml": jQuery.parseXML // texto a XML) Tipo: PlainObject.
    Un objeto que contiene el tipo de datos para convertir y cómo convertirlo. El valor de cada convertidor es una función que devuelve el valor de respuesta convertido. Agregado en jQuery 1.5.

    dominio cruzado(predeterminado: falso para solicitudes dentro del mismo dominio, verdadero para solicitudes entre dominios).

    Tipo: Booleano.
    Si desea realizar una solicitud entre dominios mientras se encuentra en el mismo dominio (por ejemplo, una solicitud jsonp), establezca este parámetro en true . Esto permitirá, por ejemplo, redirigir una solicitud a otro dominio desde su servidor. Agregado en jQuery 1.5.

    Escriba: PlainObject, String o Array.
    Los datos que se enviarán al servidor. Si no son una cadena, se convierten en una cadena de consulta. Para OBTENER La cadena de solicitudes se agregará a la URL. Para evitar el procesamiento automático, puede utilizar el parámetro procesar datos con el valor falso. Si los datos se pasan como parte de un objeto, entonces deben constar de pares clave/valor. Si el valor es una matriz, entonces jQuery serializará múltiples valores con la misma clave (dependiendo del valor del parámetro tradicional, que le permite utilizar el tipo de serialización tradicional basado en el método $.param).

    filtro de datos.

    Tipo: función (cadena datos, Cuerda escribe) => Cualquier cosa .
    La función se llama después de una solicitud AJAX exitosa y le permite procesar los datos "sin procesar" recibidos de la respuesta del servidor. Los datos deben ser devueltos inmediatamente después de haber sido procesados. La función toma dos argumentos: datos- datos recibidos del servidor como una cadena y escribe- tipo de estos datos (valor del parámetro tipo de datos).

    tipo de datos(defecto: xml, json, guion, o html).

    Tipo: Cadena.
    Especifica el tipo de datos que espera recibir del servidor. Si no se especifica el tipo de datos, jQuery intentará determinarlo en función del tipo MIME de la respuesta ( XML tipo de MÍMICA dará como resultado XML, a partir de jQuery 1.4 json dará un objeto JavaScript, guion ejecutará el script y todo lo demás se devolverá como una cadena).

    Tipos básicos (el resultado se pasa como primer argumento a la función de devolución de llamada éxito):

    • "xml"- devoluciones XML un documento que se puede procesar con jQuery.
    • "html"- devoluciones HTML como texto sin formato, etiquetas

      La forma más fácil de trabajar con AJAX es conectar el marco jQuery que es exactamente lo que hice. jQuery nos proporciona una sintaxis fácil de entender y de trabajo para enviar AJAX solicitudes, ¿por qué no aprovechar esto?

      Crear un script js

      sintaxis del archivo validar.js

      $(documento).ready(función()( var email = ""; $("#email").keyup(function()( var valor = $(this).val(); $.ajax(( tipo: "POST", url:"email.php", datos:"email="+valor, éxito:función(mensaje)( if(mensaje == "válido"))( $("#mensaje").html(" Este correo electrónico se puede utilizar.Este correo electrónico ya está en uso."); ) ) )); )); $("#submit").click(function()( if(email == ""))( alert("Por favor, ponga datos en todos los correos electrónicos"); )else( $.ajax(( tipo: "POST", url:"email.php", data:"add_email="+email, success:function(msg)( $("#message").html(msg); ) ) ); ) )); ));

      controlador php

      Este script recibirá CORREO solicitud del cliente, procesarla y devolver el resultado. AJAX lee el resultado y toma una decisión basada en él.
      Sintaxis del archivo email.php

      $conexión = mysqli_connect("localhost","email","email","email"); if(isset($_POST["correo electrónico"]) && $_POST["correo electrónico"] != ""))( $correo electrónico = $_POST["correo electrónico"]; $correo electrónico = mysqli_real_escape_string($conexión,$correo electrónico); si (!filter_var($email, FILTER_VALIDATE_EMAIL))( echo "invalid"; )else( $sql = "SELECCIONE id DESDE el correo electrónico DONDE email="$email""; $resultado = mysqli_query($conexión,$sql); if( mysqli_num_rows($resultado) == 1)( echo "no válido"; )else( echo "válido"; ) ) ) if(isset($_POST["add_email"]) && $_POST["add_email"] != "" )( $email = mysqli_real_escape_string($conexión,$_POST["add_email"]); $sql = "INSERT INTO email(email) VALUES("$email")"; if(mysqli_query($connection,$sql))( eco éxito"; )otro( echo " error"; } }

      En nuestro script php, el código más común que procesa la solicitud de publicación e imprime cierto texto en la página. Como resultado AJAX envía una solicitud secuencia de comandos php, el script lo procesa y da el resultado, AJAX lee el resultado y cambia la página en tiempo real.

      AJAX pasa la solicitud POST al script con este código:

      $.ajax(( tipo:"POST", url:"email.php", datos:"email="+valor, éxito:función(mensaje)( if(mensaje == "válido"))( $("#mensaje ").html(" Este correo electrónico se puede utilizar."); correo electrónico = valor; )else ($("#mensaje").html(" Este correo electrónico ya está en uso."); } } });

      tipo: tipo de solicitud, POST o GET. En nuestro caso POST;
      url: dirección del script al que se envía la solicitud;
      datos - datos que se transmiten en la solicitud;
      éxito: qué hacer como resultado de una solicitud exitosa. En nuestro caso, se llama a una función;

      En el propio script, se comprueba la presencia de correo electrónico en la base de datos cada vez que se introduce un carácter en el campo de correo electrónico. En el script, la sección $("#email").keyup(function()()); es responsable de procesar la entrada. , que busca una pulsación de tecla en el campo con id = "email" .
      Como puede ver, el código es bastante simple y no requiere habilidades particularmente grandes para comprenderlo, todo está relacionado con el manejo de eventos keyup () - presionar una tecla, hacer clic () - hacer clic con el mouse en un elemento. Seguido por AJAX solicitud y respuesta del script. Por lo tanto, usando php y ajax, puede obtener posibilidades casi ilimitadas para crear páginas interactivas.
      Este código no pretende ser de alta calidad, pero si lo desarrollas, agregas las validaciones correctas a nivel de cliente y servidor, ingresas css, entonces podrás usarlo en tus proyectos.
      Si tiene alguna pregunta, no dude en escribir comentarios.
      Te deseo un buen día y hasta pronto 🙂

      Visa: Blaue Karte UE

      En el momento de la presentación de documentos 29 años

      Desde Astracán

      Ciudad de la embajada: Moscú

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

      Idiomas: Inglés medio

      Cómo todo empezó:

      El deseo de mudarse a algún lugar ha estado allí durante mucho tiempo. Es cierto que, en general, se consideraron países cálidos con mar. Dos veces sondearon seriamente el suelo para mudarse a Montenegro o Bulgaria. Como resultado, en el último momento, por una u otra razón, cambiaron de opinión. La última vez en septiembre de 2014 tras serios preparativos para la venta del coche.

      En octubre, vi accidentalmente un anuncio sobre la búsqueda de programadores con traslado a Alemania. En ese momento, no tenía idea de la existencia de la Tarjeta Azul y consideraba que Alemania era un país con una política de inmigración increíblemente estricta hacia los ciudadanos de fuera de la UE.

      Con cierto grado de escepticismo y desconfianza, escribí por Skype. Al otro lado de la pantalla contestó una chica reclutadora (Alina), que se dedica a la selección de personal TI con posterior reubicación en Alemania. En el momento de nuestra primera comunicación, había una contratación de programadores para una gran tienda en línea 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 la adecuación. La entrevista, más bien una conversación con dos acertijos lógicos, duró 30 minutos por Skype. Entonces me dijeron que esperara. Aproximadamente una semana después, se programó la primera entrevista técnica. La entrevista técnica también fue por Skype con uno de los desarrolladores de la empresa. En mi opinión me fue bastante bien, pero a la semana me dijeron que no encajaba. Por cierto, ni un solo candidato de Alina pasó por ciertas razones.

      Cómo resultó todo:

      Un poco molesto, pero la vida sigue. Y unos días más tarde, Alina dijo que tenían un nuevo cliente de Stuttgart que estaba buscando desarrolladores, y me programaron 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, yo y la empresa, muchas risas y bromas de ambos lados. Aparentemente, mi humor era de mi agrado, así que unos días después me asignaron una entrevista técnica con un posible supervisor directo. Esta parte de la entrevista me sorprendió un poco, porque como dijo más tarde uno de los candidatos, parecía “una conversación entre dos programadores con un vaso de cerveza”. En la tarde del mismo día, recibí una invitación para una entrevista personal en la oficina.

      En ese momento no tenía una visa Schengen abierta. Los documentos necesarios se recogieron con urgencia. En el centro de visas alemán en Moscú, solicité visa urgente y al día siguiente saqué mi pasaporte con visa. Yo solicité visa de negocios por invitación, que me enviaron desde Esslingen, es solo una carta en la que me invitan a comunicarme y donde se indica claramente que la empresa se encarga de todos los asuntos financieros con vuelos, traslados, comidas y alojamiento.

      La comunicación personal en la oficina tuvo lugar con los tres principales responsables de TI de la empresa. La primera parte es nuevamente solo una conversación sobre experiencia, habilidades y una comprensión general de algunos problemas. El segundo es en la computadora. Para ser honesto, tareas muy, muy fáciles del nivel "junior con experiencia en pruebas" :). Las tareas estaban hechas. Después de eso, almuerzo e inmediatamente una oferta en forma de dos copias de contratos de trabajo (cargo Desarrollador PHP sénior) firmado por la empresa. Me tomé un tiempo para pensar y dije que respondería dentro de una semana.

      La decisión fue tomada 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 2,5 años), nos alquiló un departamento los primeros tres meses (en mi caso, un lugar ideal con vista a plaza central Marktplatz) y asignó una persona para ayudar por primera vez. Este no es un agente de reubicación en forma pura, pero resolvimos todas las preguntas que surgieron a través de él. Fui el primer empleado de fuera de la UE en la empresa, por lo que me formularon muchas preguntas. Ahora, además de mí, otro chico de Kyiv trabaja en la empresa (llegó un mes después que yo) y un desarrollador de Odessa se está preparando para mudarse. Todos ellos también fueron empleados, no sin la ayuda de Alina.

      Aquí me gustaría decir que estoy muy agradecido con Alina, quien resolvió todas mis preguntas en el proceso de contratación. Tuve mucha suerte de que en todas las etapas del empleo y la posterior adaptación hubo una persona que estaba lista para ayudar y resolver el problema necesario.

      Al principio volaba solo, a las dos semanas llegó mi familia. Al llegar, nadie se encuentra, durante los primeros días viví en un hotel, esperando que mi apartamento quedara libre. Me recogieron del hotel y me llevaron al lugar 🙂

      De las cosas tomó el mínimo necesario.

      Con ABH todo fue muy rápido. Todas estas cuestiones se resolvieron conjuntamente con un empleado de la empresa. ABH fijó un plazo bastante pronto después de la llegada, solicitamos y en tres semanas recibimos nuestras tarjetas eAT.

      Cómo configuramos:

      Sobre el este momento vivimos en Esslingen, una ciudad increíblemente hermosa y limpia, ubicada a solo 15 minutos de Stuttgart. Todavía no experimentamos ninguna molestia por desconocimiento del idioma, en la mayoría de los casos es posible explicarnos en inglés o, en casos extremos, con gestos. El único problema que existe en este momento es alquilar 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:

      A mitad de octubre 2014- Vi un anuncio sobre la búsqueda de 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 personal

      20 de enero - 1 de febrero 2015- solicitar una visa nacional, obtener pasaportes con visas

      ). La nube está diseñada para ejecutar varias secuencias de comandos PHP en un horario o mediante una API. Por lo general, estos scripts procesan las colas y la carga se "distribuye" entre unos 100 servidores. Anteriormente, nos enfocamos en cómo se implementa la lógica de control, que es responsable de distribuir uniformemente la carga entre tal cantidad de servidores y generar tareas según un cronograma. Pero además de eso, necesitábamos escribir un demonio que pudiera ejecutar nuestros scripts PHP en la CLI y monitorear el estado de su ejecución.

      Originalmente fue escrito en C, como todos los demás demonios de nuestra empresa. Sin embargo, nos enfrentamos al hecho de que una parte significativa del tiempo del procesador (alrededor del 10%) se gastó, de hecho, en vano: este es el lanzamiento del intérprete y la carga del "núcleo" de nuestro marco. Por lo tanto, para poder inicializar el intérprete y nuestro marco solo una vez, se decidió reescribir el daemon en PHP. Lo llamamos PHP roca syd (similar a Phproxyd - PHP Proxy Daemon, el demonio C que teníamos antes). Acepta solicitudes para ejecutar clases individuales y realiza un fork() en cada solicitud, y también sabe cómo informar el estado de ejecución de cada uno de los lanzamientos. 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 "hijos" ya están procesando la solicitud. Como beneficio adicional, tenemos la capacidad de habilitar el caché de código de operación en la CLI, que funcionará correctamente ya que todos los niños heredan la misma área de memoria compartida que el proceso maestro. Para reducir los retrasos al procesar una solicitud de lanzamiento, puede fork() por adelantado (modelo prefork), pero en nuestro caso, los retrasos de fork() son de aproximadamente 1 ms, lo que nos conviene perfectamente.

      Sin embargo, dado que actualizamos el código con bastante frecuencia, este daemon 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 restablecimiento de conexión por par, incluida la denegación de servicio para los usuarios finales (el daemon es útil no solo para la nube, sino también para parte de nuestro sitio), decidimos buscar formas de reiniciar el daemon sin perder conexiones establecidas. Hay una técnica popular que se usa para hacer recarga elegante para demonios: se realiza fork-exec y el descriptor del conector de escucha se pasa al hijo. Así que ya se aceptan nuevas conexiones. nueva versión daemon, y los antiguos están "terminados" usando la versión anterior.

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

      Teoría

      Primero pensemos: ¿es posible lo que queremos conseguir? Y si es así, ¿cómo se puede lograr esto?

      Dado que el demonio se ejecuta en Linux, que es compatible con POSIX, las siguientes opciones están disponibles para nosotros:

      1. Todos los archivos y sockets abiertos son números que corresponden al número de descriptor abierto. El flujo estándar de entrada, salida y error tiene los descriptores 0, 1 y 2, respectivamente.
      2. Sin diferencias significativas entre abrir documento, un socket y una tubería (tubería) no están presentes (por ejemplo, puede trabajar con sockets utilizando las llamadas al sistema de lectura/escritura y envío/recepción).
      3. Cuando se ejecuta la llamada al sistema fork(), todos los descriptores abiertos se heredan, preservando sus números y posiciones de lectura/escritura (en archivos).
      4. Cuando se ejecuta la llamada al sistema execve(), todos los identificadores abiertos también se heredan y, además, el PID de proceso y por lo tanto vincularse con sus hijos.
      5. La lista de identificadores 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 es factible y sin mucho esfuerzo. Entonces empecemos.

      parches PHP

      Desafortunadamente, hay un pequeño detalle que complica nuestro trabajo: en PHP no hay forma de obtener el número de descriptor de archivo para flujos (streams) y abrir el descriptor de archivo por número (en cambio, se abre una copia del descriptor de archivo, que es no es adecuado para nuestro demonio, ya que somos muy monitoreados cuidadosamente los descriptores abiertos para no crear fugas al reiniciar y al iniciar procesos secundarios).

      Primero, haremos un par de pequeños parches en el código PHP para agregar la capacidad de obtener el fd de la secuencia (stream) y hacer que fopen(php://fd/ ) no abrió una copia del identificador (el segundo cambio es incompatible con el comportamiento actual de PHP, por lo que se podría agregar una nueva "dirección", 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 @@ #si TIENE_UNISTD_H #incluir #endif +#incluir #include "php.h" #include "php_globals.h" @@ -296.11 +297.11 @@ php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, char *ruta, ch "Los descriptores de archivo deben ser números no negativos menores que %d" , dtablesize); devuelve NULL; ) - - fd = dup(fildes_ori); - if (fd == -1) ( + + fd = fildes_ori; + if (fcntl(fildes_ori, F_GETFD) == -1) ( php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, - "Error al duplicar el descriptor de archivo %ld; posiblemente no 't "t exist: " + "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/estándar/streamsfuncs.c índice 0610ecf..14fd3b0 100644 --- a/ext/estándar/streamsfuncs.c +++ b/ext/estándar/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 (ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FALLA) (return; @@ -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 (ÉXITO == 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 ", corriente->modo, 1);


      Hemos agregado 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 hemos reemplazado la llamada dup() en el descriptor de archivo pasado con una simple verificación. Desafortunadamente, este código no funcionará sin modificaciones en Windows, porque la llamada fcntl() es específica de POSIX, por lo que un parche completo debe contener ramas de código adicionales para otros sistemas operativos.

      Daemon no reiniciable

      Para empezar, escribiremos un pequeño servidor que pueda aceptar solicitudes en formato JSON y dar algún tipo de respuesta. Por ejemplo, dará la cantidad de elementos en la matriz que vino en la solicitud.

      El daemon está escuchando en el puerto 31337. La salida debería verse así:

      $ telnet localhost 31337 Probando 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é descriptores están listos para ser leídos/escritos.

      Código de implementación simple (Simple.php)

      stream) */ private $streams = ; /** @var string (client_id => búfer de lectura) */ private $read_buf = ; /** @var string (client_id => buffer de escritura) */ private $write_buf = ; /** @var resource (client_id => secuencia desde la que leer) */ private $read = ; /** @var resource (client_id => secuencia donde escribir) */ private $write = ; /** @var int Recuento total de conexiones */ private $conn_count = 0; public function run() ( $this->listen(); echo "Entrando en el bucle principal\n"; $this->mainLoop(); ) protected function listen() ( $port = self::PORT; $ip_port = " 0.0.0.0:$puerto"; $dirección = "tcp://$ip_port"; $servidor = stream_socket_server($dirección, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); if (!$servidor) ( fwrite(STDERR, "stream_socket_server falló: $errno $errstr\n"); exit(1); ) $this->read = $server; echo "Escuchando en $dirección\n"; ) public function response($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->streams [$stream_id]; ) ) public function accept($servidor) ( echo "Aceptando nueva conexión\n"; $client = stream_socket_accept($server, 1, $peername); $stream_id = ($this->conn_count+ +); if (!$cliente) ( fwrite(STDERR, "Error de aceptación\n"); return; ) stream_set_read_buffer($cliente, 0); stream_set_write_buffer($cliente, 0); stream_set_blocking($cliente, 0); stream_set_timeout($cliente, 1); $this->read_buf[$stream_id] = ""; $this->write_buf[$stream_id] = ""; $this->read[$stream_id] = $this->streams[$stream_id] = $cliente; echo "Stream conectado$stream_id: $peername\n"; ) función privada desconectar ($stream_id) ( echo "Desconectar stream$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]); ) función privada handleRead($stream_id) ( $buf = fread($this->streams[$stream_id] ], 8192); if ($buf === false || $buf === "") ( echo "Obtuve EOF de stream$stream_id\n"; if (empty($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); ) función privada processJSONRequests($stream_id) ( if (!strpos($this->read_buf[$stream_id], "\n")) return; $requests = explotar("\n", $this ->read_buf[$stream_id]); $this->read_buf[$stream_id] = array_pop($solicitudes); foreach ($solicitudes como $req) ( $res = json_decode(rtrim($req), true); if ( $res! == false) ( $this->response($stream_id, "Request had" . count($res) . "keys"); ) else ($this->response($stream_id, "Invalid JSON"); ) ) ) función privada handleWrite($stream_id) ( if (!isset($this->write_buf[$stream_id])) ( return; ) $wrote = fwrite($this->streams[$stream_id], substr($this-> write_buf[$stream_id], 0, 65536)); if ($escribió === false) ( fwrite(STDERR, "escribió falló en el flujo #$stream_id\n"); $this->disconnect($stream_id); return ; ) if ($escrito === strlen($esto->write_buf[$stream_id])) ( $this->write_buf[$stream_id] = ""; unset($this->write[$stream_id]); if (empty($this->read[$stream_id])) ( $this->disconnect($stream_id); ) ) else ( $this->write_buf[$stream_id] = substr($this->write_buf[$stream_id] , $escrito); ) ) public function mainLoop() ( while (true) ( ​​$leer = $esto->leer; $escribir = $esto->escribir; $excepto = nulo; echo "Seleccionando para " . count ($leer) . " lee, " . contar($escribir) . "escribir\n"; $n = stream_select($leer, $escribir, $excepto, NULO); if (!$n) ( fwrite(STDERR, "No se pudo transmitir_select()\n"); ) if (count($read)) ( echo "Puede leer desde " . count($read) . " streams\n" ; ) if (count($write)) ( echo "Puede escribir en " . count($write) . " streams\n"; ) if (isset($read)) ($this->accept($read); unset($leer); ) foreach ($leer como $stream_id => $_) ( $this->handleRead($stream_id); ) foreach ($escribir como $stream_id => $_) ( $this->handleWrite( $stream_id); ) ) ) ) $instancia = new Simple(); $instancia->ejecutar();


      El código de este demonio es más que estándar, pero me gustaría señalar un detalle de implementación: almacenamos todos los búferes de lectura y escritura con referencia a conexiones específicas y realizamos el procesamiento de solicitudes en el mismo lugar donde leemos la solicitud. Esto es importante, porque una de estas solicitudes puede reiniciarse, en cuyo caso las siguientes solicitudes no se procesarán. Sin embargo, dado que aún no hemos leído las solicitudes, la próxima vez que stream_select() de los mismos descriptores arrojará el mismo resultado. Por lo tanto, no perderemos una sola solicitud si reiniciamos directamente desde el controlador de comandos (excepto en el caso de que se nos envíen varios comandos a la vez en la misma conexión, y uno de estos comandos será reiniciar).

      Entonces, ¿cómo hace posible reiniciar el daemon?

      Daemon con reiniciar y guardar conexiones establecidas

      Nuestro ejemplo más simple no pudo hacer nada útil, así que escribamos el daemon del que hablamos al principio. Queremos obtener algo como lo siguiente (los comandos se envían al daemon en la forma "command_name [datos JSON]", la respuesta está en forma de JSON):
      $ telnet localhost 31337 Probando 127.0.0.1... Conectado a localhost. El carácter de escape es "^]". # pide inmediatamente al daemon que reinicie restart # la respuesta es enviada por el daemon ya reiniciado "Reiniciado con éxito" # ejecuta la clase de prueba run ("hash":1,"params":,"class":"TestClass1") # ejecuta exitosamente ("error_text": "OK") # reiniciar el daemon nuevamente (su hijo TestClass1 aún se está ejecutando) reiniciar "Reiniciado con éxito" # verificar el estado de la tarea: aún en ejecución verificar ("hash": 1) ("error_text":" Sigue ejecutándose") # espere 5 segundos y verifique nuevamente: la clase TestClass1 completó con éxito la verificación ("hash": 1) ("retcode": 0) # el daemon recuerda todas las ejecuciones, por lo que necesita hacer una verificación gratuita ("hash" :1) ("retcode": 0) gratis ("hash":1) ("error_text":"OK") reiniciar "Reiniciado con éxito" # Actualicé el código, así que la segunda vez vemos una respuesta diferente para reiniciar reiniciar ("error_text": "Reiniciado con éxito") adiós Conexión cerrada por host externo.

      La idea para un reinicio es simple: crearemos un archivo con toda la información necesaria y, al iniciar, intentaremos leerlo y restaurar los descriptores de archivos abiertos.

      Primero, escribamos el código para escribir en el archivo de reinicio:

      echo "Creando archivo de reinicio...\n"; if (!$res = $this->getFdRestartData()) ( fwrite(STDERR, "No se pudieron reiniciar los datos de FD, saliendo, el reinicio correcto no es compatible\n"); exit(0); ) /* Cerrar todos los datos adicionales descriptores de archivo que no conocemos, incluido el descriptor opendir():) */ $dh = opendir("/proc/self/fd"); $fds = ; while (false !== ($archivo = readdir($dh))) ( if ($archivo === ".") continue; $fds = $archivo; ) foreach ($fds as $fd) ( if (! isset($this->known_fds[$fd])) ( fclose(fopen("php://fd/" . $fd, "r+")); ) ) $contenido = serialize($res); if (file_put_contents(self::RESTART_DIR . self::RESTART_FILENAME, $contenidos) !== strlen($contenidos)) ( fwrite(STDERR, "No se pudo escribir completamente el archivo de reinicio\n"); unlink(self::RESTART_DIR . self::RESTART_FILENAME); )

      El código para obtener la matriz de datos (la función getFdRestartData()) se proporciona a continuación:

      $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, "No hay fd en metadatos de transmisión para el recurso $v (clave $k en $prop), obtuvo " . 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;
      El código tiene en cuenta que tenemos 2 tipos de propiedades:

      1. Propiedades que contienen recursos con conexiones: $restart_fd_resources = ["read", "write", "streams"].
      2. Propiedades que contienen búferes y otra información de conexión que se puede "serializar" sin formato: $restart_fd_props = ["read_buf", "write_buf", "conn_count"].
      También recordamos todos los fds almacenados en el archivo de reinicio y cerramos todos los demás (si los hay), porque de lo contrario podemos filtrar descriptores de archivos.

      A continuación, tenemos que cargar este archivo al principio y continuar usando descriptores abiertos como si nada hubiera pasado :). El código para dos funciones (cargar un archivo de reinicio y cargar información sobre los descriptores de archivo) se muestra a continuación:

      If (!file_exists(self::RESTART_DIR . self::RESTART_FILENAME)) ( return; ) echo "Reiniciar archivo encontrado, tratando de adoptarlo\n"; $contenido = file_get_contents(self::RESTART_DIR . self::RESTART_FILENAME); unlink(self::RESTART_DIR . self::RESTART_FILENAME); if ($contenido === falso) ( fwrite(STDERR, "No se pudo leer el archivo de reinicio\n"); return; ) $res = unserialize($contenido); if (!$res) ( fwrite(STDERR, "No se pudo deserializar el contenido del archivo de reinicio"); return; ) foreach (self::$restart_props as $prop) ( if (!array_key_exists($prop, $res)) ( fwrite (STDERR, "Sin propiedad $prop en el archivo de reinicio\n"); continuar; ) $this->$prop = $res[$prop]; ) $this->loadFdRestartData($res);

      La función loadFdRestartData() para volver a expandir la matriz de descriptores de archivos:

      $fd_recursos = ; foreach (self::$restart_fd_resources as $prop) ( if (!isset($res[$prop])) ( fwrite(STDERR, "La propiedad "$prop" no está presente en restart fd resources\n"); continuar; ) $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, "Error al abrir fd = $v, saliendo\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, "La propiedad "$prop" no está presente en las propiedades de reinicio fd\n"); continuar; ) $this->$prop = $res[$prop]; )
      Restablecemos los valores read_buffer y write_buffer para los descriptores de archivos abiertos y ajustamos los tiempos de espera. Por extraño que parezca, después de estas manipulaciones, PHP acepta tranquilamente () en estos descriptores de archivo y continúa leyéndolos/escribiéndolos normalmente, aunque no sabe que se trata de sockets.

      Al final, debemos escribir la lógica para lanzar y monitorear el estado de ejecución de los trabajadores. Dado que esto no está relacionado con el tema del artículo, la implementación completa del demonio se coloca en el repositorio de github, cuyo enlace se proporciona a continuación.

      Conclusión

      Entonces, este artículo describió la implementación de un demonio que se comunica a través del protocolo JSON y puede ejecutar clases arbitrarias en procesos separados con el monitoreo del proceso de su ejecución. El modelo se utiliza para ejecutar clases individuales. bifurcación () por solicitud, por lo que la solicitud no requiere reiniciar el intérprete y cargar el marco, y es posible usar el caché de código de operación en la CLI. Dado que el daemon debe reiniciarse cada vez que se actualiza el código, es necesario proporcionar un mecanismo para reiniciar sin problemas este daemon (en nuestra empresa, las actualizaciones de código a veces ocurren cada pocos minutos, en forma de "revisiones").

      El reinicio se produce mediante la ejecución de la llamada al sistema execve(), lo que hace que todos los hijos permanezcan conectados al padre (porque el PID del proceso no cambia durante execve()). Todos los descriptores de archivos abiertos también se guardan, lo que le permite continuar procesando solicitudes de usuarios en conexiones ya abiertas. Todos los búferes de red, la información sobre los niños en ejecución y los descriptores abiertos se almacenan en un archivo de reinicio separado, que lee una nueva instancia del daemon, después de lo cual el trabajo continúa en el bucle de eventos estándar.

      El código de implementación completo se puede ver en GitHub en la siguiente dirección.