Saltar al contenido

Etiqueta: desarrollo

Tool: MySQL Profiler con Python

Hace ya algún tiempo que no hacia un post, tengo varios en el tintero y busco hacerme un tiempo en adelante para seguir actualizando el blog.

Dicho esto, en este post voy a presentar una herramienta sencilla que he realizado para suplir la necesidad de un «SQL Profiler» para MySQL. Como saben, en la suite de SQL Server, tenemos el IDE (management studio) y también un Profiler, es último nos sirve para poder monitorear que está ocurriendo en nuestra BD. Una herramienta extremadamente util, para distintos perfiles técnicos (DBAs, desarrolladores, etc).

SQL Profiler
SQL Profiler

Me encontraba revisando una aplicación que llego como un proyecto de fin de semana y me vi con la necesidad de usar una herramienta similar, pero que funcione en MySQL. Revisando un poco las herramientas opensource actuales, vi que ninguna me brindaba la información que necesitaba, por ahí vi que hay algunas herramientas que si cubrían esta necesidad, pero son de pago.

Teniendo esto en mente, la lógica detrás de una herramienta así, no se me hacía tan complicada, y ya antes había visto que uno puede habilitar que se genere un log de los queries que se ejecutan en el servidor.

Opción MySQL Logging en Windows
Cambiar configuración del Logging

Así que de pasada para poner practicar un poco de python, realice un script que nos permite monitorear los comando que ejecutamos.

Primero, debemos asegurarnos de tener habilitada la opción en nuestro MySQL, para que se registren los logs de los queries que se ejecutan (importante considerar que esta opción no es recomendado para servidores productivos, por lo que esta tool esta orientada a desarrolladores).

Ahora, un paso importante en esta configuración, es asegurarnos que esta información no la envie a una archivo de text (opción por default), sino que la mande a una tabla en base de datos. Esto lo conseguimos cambiando un atributo en nuestro archivo de configuración.

Configuración necesaria

Una vez que hemos hecho esto, corramos un script en nuestra base de datos para asegurarnos que se correctamente configurada nuestra tabla en la que se van a grabar todos los queries.

SET @old_log_state = @@global.general_log; 
SET GLOBAL general_log = 'OFF'; 
ALTER TABLE mysql.general_log ENGINE = MyISAM; 
ALTER TABLE mysql.general_log ADD INDEX (event_time); 
SET GLOBAL general_log = @old_log_state;

Con estos cambios, reiniciemos el servidor («services.msc» en windows y «systemctl restart mysql» en linux) y demosle una revisada a la tabla con un simple select, para asegurarnos que ya se están registrando valores.

Es recomendable que también creemos un usuario especifico para nuestra aplicación, esto nos va a servir mucho para poder filtrar solo los queries que pertenecen a nuestra aplicación.

Ahora si vamos a la parte de python. El código es sencillo, veamos un poco cuales han sido las funciones que hemos definido para conseguir nuestro cometido:

  • connect_to_database: Nos permite inicializar una conexión a la base de datos (cambién por la información que requieran para sus ambientes de desarrollo).
connect_to_database
  • fetch_general_log:
    • Realizamos la consulta a la base de datos, acá tenemos un query que permite obtener todas las consultas de un usuario en específico y sanitizando un poco la información que vamos a obtener, obviando consultas automáticas que no realiza nuestra aplicación. No hay mucha ciencia en el query, prefiltramos la data, para no traernos toda la información, dado que la idea de esta herramienta es ver las últimas consultas que se van realizando.
    • Hay una lógica para obtener la fecha del último query valido, esto nos sirve, sólo para mostrar los últimos registros.
    • Si los criterios se cumplen, pintamos en la consola el query realizado. Con la libreria antes instalada, podemos darle color a esto, para identificar cual es la marca de tiempo y cual es el contenido del query.
    • Finalmente, actualizamos cerramos el cursos y nuestra conexión a la base de datos.
fetch_general_log
  • main: Nuestra función inicial, acá inicializamos la fecha actual de nuestro sistema (esta la usaremos para asegurarnos de obtener sólo resultados, luego de esta fecha), indicamos el nombre del usuario por el que luego vamos a filtrar los registros en esta tabla y llamamos a nuestra función «fetch_general_log». Es importante tener en cuenta, que todo esto corre dentro de un bucle «while ‘true'», y se va a realizar cada 1 segundo. Tenemos un try/catch, para capturar algún error en la conexión a la BD y cuando cancelemos con un comando
main

Como ven, es un código sencillo, pero que nos brinda una utilidad importante al momento de desarrollar. Adiconalmente a esto, cree un script en bash, para poder llamarlo como un comando más del SO.

Les dejo una captura de como funciona la herramienta:

Profiler corriendo

Espero que les sirva, al menos para mi es una herramienta más cuando tengo que trabajar en poryecto que usa MySQL como base de datos. Finalmente, les comparto el repositorio donde he subido la herramienta.

REPO GITHUB

Saludos.

Deja un comentario

Integrando AWS Lambda a un Pipeline de Azure Devops

Hola, espero que estén bien. Este post es una continuación al post anterior, en el cual revisamos como podemos realizar el deploy de un api en un AWS Lambda. Ahora seguiremos con lo que es más lógico que suceda luego de hacer esto, automatizar este proceso. En post anteriores, he mostrado el uso de Azure Devops para los procesos de devops que uso, veamos como realizamos en este caso.

Primero, debemos tener creado un rol en IAM para que nuestro pipeline de devops se pueda authenticar. Con esto realizado, ingresar a azure devops y nos dirigimos a la siguientes opción.

Configuramos una nueva credencial
Elegimos AWS

Con esto hecho, vamos a AWS para crear un bucket, que es donde configuraremos luego para que nuestra pipeline deje los archivos.

Creamos un nuevo bucket

En nuestro pipeline, vamos a seleccionar la siguiente tarea.

Tarea a seleccionar
Información a completar

Para que funcione nuestro proceso de devops, es necesario crear una serie de archivos en nuestro proyecto, son dos los archivos, el primero es «serverless.template» y «aws-lambda-tools-defaults.json». Les dejo el detalle de la información que va en cada archivo y les comento cual es la información que debe variar.

serverless.template

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
  "Parameters": {},
  "Conditions": {},
  "Resources": {
    "AspNetCoreApi": {
      "Type": "AWS::Serverless::Api",
      "Properties": {
        "StageName": "Prod",
        "BinaryMediaTypes": [
          "*~1*"
        ]
      }
    },
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "Exphadis.Services.WebApi::Exphadis.Services.WebApi.LambdaEntryPoint::FunctionHandlerAsync",
        "Runtime": "dotnetcore3.1",
        "CodeUri": "",
        "MemorySize": 512,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambda_FullAccess"
        ],
        "Environment" : {
          "Variables" : {
            "ASPNETCORE_ENVIRONMENT" : "",
            "SENTRY_CONNECTION" : "",
            "DATABASE_CONNECTION" : "",
            "JWT_KEY" : "",
            "JWT_EXPIRATION" : ""
          }
        },
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          },
          "RootResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "Site for Exphadis Support",
      "Value": {
        "Fn::Sub": "https://${AspNetCoreApi}.execute-api.${AWS::Region}.amazonaws.com/site/"
      }
    }
  }
}

Consideren, que deben modificar de esta plantilla:

  • Handler
  • Environment – Variables

aws-lambda-tools-defaults.json

{
    "Information" : [
        "All the command line options for the Lambda command can be specified in this file."
    ],
    "profile"     : "default",
    "region"      : "us-east-1",
    "configuration" : "Release",
    "framework"     : "netcoreapp3.1",
    "s3-prefix"     : "exphadis-service/",
    "template"      : "serverless.template",
    "template-parameters" : "",
    "s3-bucket"           : "exphadis-service",
    "stack-name"          : "Exphadis-Service-Serverless"
}

Consideren modificar la información que visualizan, de-acuerdo al escenario que tengan.

Veamos como tendría que quedar nuestro archivo «pipeline.yml».

trigger:
  - feature/deploy-lambda

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
  - task: LambdaNETCoreDeploy@1
    name: 'Deploy_Lambda'
    displayName: 'Deploy to lambda'
    inputs:
      awsCredentials: 'AWS Admin'
      regionName: 'us-east-1'
      command: 'deployServerless'
      packageOnly: false
      lambdaProjectPath: '$(System.DefaultWorkingDirectory)\\Exphadis.Services.WebApi'
      stackName: 'Exphadis-Service-Serverless'
      s3Bucket: 'exphadis-service'
      s3Prefix: 'Exphadis.Services.WebApi/'


Notemos especificamente estas partes.

Información importante

Fuera de elegir las credenciales que ya hemos configurado previamente, la región, es necesario indicar:

  • lambdaProjectPath: La ruta en nuestro proyecto donde está nuestro entrypoint para lambda.
  • stackName: El nombre de nuestro lambda
  • s3Bucket: El nombre del bucket en S3 que hemos creado
  • s3Prefix: El prefijo de la carpeta en la que se va a desplegar nuestra proyecto compilado.

Probemos ahora toda nuestra configuración.

Despliegue en lambda desde azure devops

Finalmente, validamos en AWS este cambio.

Publicación visualizada desde AWS
Consumo del api

La demo de todo lo anteriormente explicado, pueden revisarla en el siguiente video.

Espero que les sea util, si tienen alguna duda, los espero en la sección de comentarios.

Saludos.

Deja un comentario

Dockerizando una aplicación Net 5.0 – Part. II

Este post es una continuación de su versión anterior, en este nos enfocaremos en como configuramos nuestro ambiente de desarrollo con docker.

Retomemos en el código del proyecto.

Código del proyecto

Vamos a modificar nuestro «launchSetting.json», eliminemos los profiles que tenemos actualmente, creemos uno nuevo para manejar nuestra instancia en docker que tendrá la siguiente estructura.

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:61567/",
      "sslPort": 44304
    }
  },
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
      "publishAllPorts": true,
      "useSSL": true,
      "httpPort": 7550,
      "sslPort": 7551,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "SENTRY_CONNECTION": "",
        "DATABASE_CONNECTION": "",
        "JWT_KEY": "",
        "JWT_EXPIRATION": "",
        "URL_BASE_PATH": "",
        "AWS_ACCESS_KEY": "",
        "AWS_SECRET_KEY": "",
        "EMAIL_NO_REPLY": "",
        "ASPNETCORE_URLS": "http://+:8080;https://+:443"
      }
    }
  }
}
Nueva configuración en launchSettings.json

Lo que hemos hecho es crear un nuevo perfil, le indicamos el comando que va a ejecutar «docker», que debe abrir un navegador, la url que se va a abrir (esta como vemos se obtiene de variables, no valores en duro), un poco de dolor de cabeza me llevo descubrir el uso de «httpPort» y «sslPort», dado que estos dos nos permiten indicarle que puertos queremos que use. Esto ultimo parece algo menor, pero nos sirve cuando mapeamos en nuestro postman o en el front el puerto del backend, y que este sea consistente en distintos ambientes, de otro modo, nos asignarán un puerto aleatorio y esto nos generar tener que estar cambiando nuestra configuración en el front. Finalmente, acá también podemos indicarle las variables de entorno que vamos a usar dentro de nuestro dockerfile.

En nuestro ide, selecionamos «Docker» y ejecutamos el proyecto. Si es la primera ves que se ejecuta, se va a crear la imagen en base a nuestro dockerfile, luego de esto, el ide automaticamente va a crear una instancia en base a esta imagen. Veamos que comando es el que usa para ejecutar este comando.

Primero se hace el build:

docker build -f "C:\Users\walbe\Development\Simplicity\simplicity-api\dockerfile-dev" --force-rm -t simplicityserviceapi:dev --target base  --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=Simplicity.Service.Api" "C:\Users\walbe\Development\Simplicity"
Docker build

Luego realizamos se realiza el run de nuestra imagen recién creada:

docker run -dt -v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Users\walbe\Development\Simplicity\simplicity-api\Simplicity.Service.Api:/app" -v "C:\Users\walbe\Development\Simplicity:/src/" -v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" -e "ASPNETCORE_ENVIRONMENT=Development" -e "ASPNETCORE_HTTPS_PORT=7551" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 7550:8080 -p 7551:443 -P --name Simplicity.Service.Api_1 --entrypoint tail simplicityserviceapi:dev -f /dev/null
Docker run
docker run -dt 
-v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" 
-v "C:\Users\walbe\Development\WebApplication1:/app" 
-v "C:\Users\walbe\Development\WebApplication1:/src/" 
-v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" 
-v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" 
-v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" 
-v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" 
-e "DOTNET_USE_POLLING_FILE_WATCHER=1" 
-e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" 
-e "ASPNETCORE_ENVIRONMENT=Development" 
-e "ASPNETCORE_URLS=https://+:443;http://+:80" 
-e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" 
-e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" 

Con esto, ya tendriamos todo funcionando correctamente, les dejo un video de todo este proceso, donde podemos ver mejor como nuestra aplicación corre desde nuestra imagen docker y los cambios que realizamos se actualizan directamente.

Espero que les sirva, en el siguiente post explicare como realizar el deploy de una aplicación en NetCore o Net 5.0 en AWS Lambda.

Deja un comentario

SQS en AWS con Spring Boot – Parte II

En el post pasado, revisamos que es una cola cuando hablamos de desarrollo, en que casos nos puede servir y que alternativas encontramos en el mercado para poder usarlas. Puntualmente, hicimos una demo de el uso de esta herramientas con SQS que es un servicio ofrecido por AWS. Sin embargo, buscando no extender el post y la demo demasiado conforme iba avanzando el post anterior, decidí que era mejor dividirlo en dos partes. En la primera, revisamos como crear desde AWS un usuario con los permisos necesarios para conectarse a una cola, en código con un proyecto hecho con spring boot conectarnos a esta y poder enviar mensajes.

En esta segunda parte, revisaremos como podemos suscribirnos a una cola, y estar a la escucha de nuevos mensajes.

Para poder conseguir esto, y no estar llenandonos de proyectos voy a usar el mismo proyecto en spring boot de la ves pasada, con la diferencia que creare algunos nuevos componentes, la idea basicamente es consumir una api desde un controlador, esta encola el mensaje y en un listener recibirlo e imprimirlo en consola.

Veamos que hemos añadido en nuestro proyecto inicial.

POM

Nuevas dependencias en el POM
Nuevas dependencias en el POM

JmsConfiguration

Clase de configuración del JMS
Clase de configuración del JMS

SqsListener

Clases del SqsListener
Clases del SqsListener

Con lo añadido en nuestro POM, tenemos acceso a clases de configuración que nos permitirán configurar un listener para las colas que ya tenemos definida en SQS. Si vemos a detalle la clase de configuración que hemos creado, tenemos lo siguiente:

Configuración en el JmsListener
Configuración en el JmsListener

En esta clase, como podemos ver, estamos creando un factory para las conexiones a AWS esto en el método «sqsConnectionFactory» y adicionalmente, configuramos nuestro listener factory para estar a la escucha de nuestra cola definida en AWS.

Como ven, tenemos que indicar que esta clase es de configuración con @Configuration, además de indicarse que habilite el JMS con @EnableJms. Sólo para aclarar por si hubiera alguna duda, JMS son las siglas de Java Message Service que es una clase definida en la librería JMS que nos permite realizar la creación y suscripción de mensajes a través de una cola.

Y finalmente, tenemos que crear nuestro listener, esto lo hacemos del siguiente modo:

SqsListener
SqsListener

Creamos una clase y dentro creamos un método void con las siguientes anotaciones «@JmsListeners» que tiene dentro un «@JmsListener». Es necesario dentro de esta última anotación indicarle cual es el nombre de la cola que vamos a estar a la escucha en nuestro listener.

Hagamos una prueba de todo junto.

Primero realizamos el consumo de nuestro controlador con una mensaje cualquiera.

Consumiendo el API
Consumiendo el API

Recibimos el mensaje en nuestro controlador.

Recibiendo el mensaje en el controlador
Recibiendo el mensaje en el controlador

El mensaje es enviado a nuestra cola previamente configurada en SQS.

Se genera el mensaje correctamente
Se genera el mensaje correctamente

Mensaje encolado en SQS.

Mensaje en SQS
Mensaje en SQS

Ahora, segundos despues de que se encoló el mensaje, debería llegar a nuestro listener que esta escuchando la misma cola «SqsTest».

Recibimos el mensaje
Recibimos el mensaje
Log del mensaje
Log del mensaje

Con esta prueba concluye este post. El código lo subire como una actualización al proyecto anterior. Espero que les sirva, como ven es sencillos tener un manejo de colas funcional con sólo un poco de código. Dejo el link del repo y el video.

REPO GITHUB

Saludos.

2 comentarios