Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O padrão de monitoramento refere-se a um processo recorrente flexível em um fluxo de trabalho - por exemplo, sondagem até que determinadas condições sejam atendidas. Este artigo explica um exemplo que usa as Funções Duráveis para implementar o monitoramento.
Pré-requisitos
Visão geral do cenário
Este exemplo monitora a contagem de problemas em um repositório GitHub e alerta o usuário quando há mais de 3 problemas abertos. É possível usar uma função disparada por temporizador regular para solicitar as contagens de problemas abertas em intervalos regulares. No entanto, um problema com essa abordagem é o gerenciamento do tempo de vida. Quando apenas um alerta é enviado, o monitor precisa se desabilitar depois que 3 ou mais problemas são detectados. O padrão de monitoramento pode encerrar sua própria execução, entre outros benefícios:
- Os monitores são executados em intervalos, não em agendamentos: um disparador com timer executa a cada hora; um monitor aguarda uma hora entre as ações. As ações de um monitor não se sobrepõem, exceto se especificado, o que pode ser importante para tarefas de execução longa.
- Os monitores podem ter intervalos dinâmicos: o tempo de espera pode mudar com base em alguma condição.
- Os monitores poderão terminar quando alguma condição for atendida ou terminada por outro processo.
- Monitores podem receber parâmetros. O exemplo mostra como o mesmo processo de monitoramento de repositório pode ser aplicado a números de telefone e repositórios GitHub públicos solicitados.
- Monitores são escalonáveis. Como cada monitor é uma instância de orquestração, vários monitores podem ser criados sem a necessidade de criar novas funções ou definir mais código.
- Os monitores integram-se facilmente em fluxos de trabalho maiores. Um monitor pode ser uma seção de uma função de orquestração mais complexa ou uma sub-orquestração.
Configuração
Configurando a integração com o Twilio
Este exemplo envolve o uso do serviço Twilio para enviar mensagens SMS a um telefone celular. O Azure Functions já tem suporte para Twilio por meio da Associação com o Twilio e o exemplo usa esse recurso.
A primeira coisa de que você precisa é uma conta do Twilio. É possível criar uma gratuitamente em https://www.twilio.com/try-twilio. Quando tiver uma conta, adicione as três seguintes configurações de aplicativo ao seu aplicativo de função.
Nome da configuração do aplicativo | Descrição do valor |
---|---|
TwilioAccountSid | A SID de sua conta do Twilio |
TwilioAuthToken | O token de autenticação de sua conta do Twilio |
TwilioPhoneNumber | O número de telefone associado à sua conta do Twilio. Ele é usado para enviar mensagens SMS. |
As funções
Este artigo explica as seguintes funções no aplicativo de exemplo:
E3_Monitor
: uma função do orquestrador que chamaE3_TooManyOpenIssues
periodicamente. Ele vai chamarE3_SendAlert
se o valor retornado deE3_TooManyOpenIssues
forTrue
.E3_TooManyOpenIssues
: uma função de atividade que verifica se um repositório tem muitos problemas em aberto. Para essa demonstração, consideramos que mais de 3 problemas abertos é muito.E3_SendAlert
: uma função de atividade que envia uma mensagem SMS via Twilio.
Função de orquestrador E3_Monitor
A função E3_Monitor usa function.json padrão para funções de orquestrador.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
Este é o código que implementa a função:
import azure.durable_functions as df
from datetime import timedelta
from typing import Dict
def orchestrator_function(context: df.DurableOrchestrationContext):
monitoring_request: Dict[str, str] = context.get_input()
repo_url: str = monitoring_request["repo"]
phone: str = monitoring_request["phone"]
# Expiration of the repo monitoring
expiry_time = context.current_utc_datetime + timedelta(minutes=5)
while context.current_utc_datetime < expiry_time:
# Count the number of issues in the repo (the GitHub API caps at 30 issues per page)
too_many_issues = yield context.call_activity("E3_TooManyOpenIssues", repo_url)
# If we detect too many issues, we text the provided phone number
if too_many_issues:
# Extract URLs of GitHub issues, and return them
yield context.call_activity("E3_SendAlert", phone)
break
else:
# Reporting the number of statuses found
status = f"The repository does not have too many issues, for now ..."
context.set_custom_status(status)
# Schedule a new "wake up" signal
next_check = context.current_utc_datetime + timedelta(minutes=1)
yield context.create_timer(next_check)
return "Monitor completed!"
main = df.Orchestrator.create(orchestrator_function)
Essa função de orquestrador executa as ações a seguir:
- Obtém o repositório para monitorar e o número de telefone para o qual ela vai enviar uma notificação por SMS.
- Determina o tempo de expiração do monitor. O exemplo usa um valor embutido em código para brevidade.
- Chama E3_TooManyOpenIssues para determinar se há muitos problemas em aberto no repositório solicitado.
- Se houver muitos problemas, as chamadas E3_SendAlert para enviar uma notificação por SMS para o número de telefone solicitado.
- Cria um temporizador durável para retomar a orquestração no próximo intervalo de sondagem. O exemplo usa um valor embutido em código para brevidade.
- Continua em execução até que o horário UTC atual passe o tempo de expiração do monitor ou um alerta por SMS seja enviado.
É possível executar várias instâncias de orquestrador ao mesmo tempo. Para isso, basta chamar a função de orquestrador várias vezes. O repositório a ser monitorado e o número de telefone para o qual um alerta por SMS será enviado pode ser especificado. Por fim, observe que a função de orquestrador não está em execução enquanto aguarda o temporizador; portanto, você não será cobrado por ela.
Função de atividade E3_TooManyOpenIssues
Como com outros exemplo, as funções de atividade auxiliares são funções regulares que usam a associação de gatilho activityTrigger
. A função E3_TooManyOpenIssues obtém uma lista de problemas abertos no momento no repositório e determina se há "muitos" deles: mais de 3, no nosso exemplo.
O function.json é definido da seguinte maneira:
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "repoID",
"type": "activityTrigger",
"direction": "in"
}
]
}
E aqui está a implementação.
import requests
import json
def main(repoID: str) -> str:
# We use the GitHub API to count the number of open issues in the repo provided
# Note that the GitHub API only displays at most 30 issues per response, so
# the maximum number this activity will return is 30. That's enough for demo'ing purposes.
[user, repo] = repoID.split("/")
url = f"https://api.github.com/repos/{user}/{repo}/issues?state=open"
res = requests.get(url)
if res.status_code != 200:
error_message = f"Could not find repo {user} under {repo}! API endpoint hit was: {url}"
raise Exception(error_message)
issues = json.loads(res.text)
too_many_issues: bool = len(issues) >= 3
return too_many_issues
Função de atividade E3_SendAlert
A função E3_SendAlert usa a associação Twilio para enviar um SMS notificando o usuário final de que há pelo menos 3 problemas em aberto aguardando uma resolução.
O function.json é simples:
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "repoID",
"type": "activityTrigger",
"direction": "in"
}
]
}
E aqui está o código que envia a mensagem SMS:
import json
import random
random.seed(10)
def main(phoneNumber: str, message):
payload = {
"body": f"Hey! You may want to check on your repo, there are too many open issues",
"to": phoneNumber
}
message.set(json.dumps(payload))
return "Message sent!"
Execute o exemplo
Você também vai precisar de uma conta do GitHub. Com ela, crie um repositório público temporário onde você pode abrir problemas.
Usando as funções disparadas por HTTP incluídas no exemplo, você pode iniciar a orquestração enviando a seguinte solicitação HTTP POST:
POST https://{host}/orchestrators/E3_Monitor
Content-Length: 77
Content-Type: application/json
{ "repo": "<your GitHub handle>/<a new GitHub repo under your user>", "phone": "+1425XXXXXXX" }
Por exemplo, se o nome de usuário do GitHub for foo
e o repositório for bar
, o valor de "repo"
deverá ser "foo/bar"
.
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
Location: https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635?taskHub=SampleHubVS&connection=Storage&code={SystemKey}
RetryAfter: 10
{"id": "f6893f25acf64df2ab53a35c09d52635", "statusQueryGetUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635?taskHub=SampleHubVS&connection=Storage&code={systemKey}", "sendEventPostUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/raiseEvent/{eventName}?taskHub=SampleHubVS&connection=Storage&code={systemKey}", "terminatePostUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason={text}&taskHub=SampleHubVS&connection=Storage&code={systemKey}"}
A instância E3_Monitor é iniciada e consulta o repositório de problemas abertos. Se pelo menos 3 problemas abertos forem encontrados, ele chamará uma função de atividade para enviar um alerta; caso contrário, ele definirá um temporizador. Quando o temporizador expirar, a orquestração será retomada.
É possível visualizar a atividade da orquestração, observando os logs de função no Portal do Azure Functions.
[2020-12-04T18:24:30.007Z] Executing 'Functions.HttpStart' (Reason='This function was programmatically
called via the host APIs.', Id=93772f6b-f4f0-405a-9d7b-be9eb7a38aa6)
[2020-12-04T18:24:30.769Z] Executing 'Functions.E3_Monitor' (Reason='(null)', Id=058e656e-bcb1-418c-95b3-49afcd07bd08)
[2020-12-04T18:24:30.847Z] Started orchestration with ID = '788420bb31754c50acbbc46e12ef4f9c'.
[2020-12-04T18:24:30.932Z] Executed 'Functions.E3_Monitor' (Succeeded, Id=058e656e-bcb1-418c-95b3-49afcd07bd08, Duration=174ms)
[2020-12-04T18:24:30.955Z] Executed 'Functions.HttpStart' (Succeeded, Id=93772f6b-f4f0-405a-9d7b-be9eb7a38aa6, Duration=1028ms)
[2020-12-04T18:24:31.229Z] Executing 'Functions.E3_TooManyOpenIssues' (Reason='(null)', Id=6fd5be5e-7f26-4b0b-98df-c3ac39125da3)
[2020-12-04T18:24:31.772Z] Executed 'Functions.E3_TooManyOpenIssues' (Succeeded, Id=6fd5be5e-7f26-4b0b-98df-c3ac39125da3, Duration=555ms)
[2020-12-04T18:24:40.754Z] Executing 'Functions.E3_Monitor' (Reason='(null)', Id=23915e4c-ddbf-46f9-b3f0-53289ed66082)
[2020-12-04T18:24:40.789Z] Executed 'Functions.E3_Monitor' (Succeeded, Id=23915e4c-ddbf-46f9-b3f0-53289ed66082, Duration=38ms)
(...trimmed...)
A orquestração será concluída quando seu tempo limite for atingido ou mais de 3 problemas abertos forem detectados. Também é possível usar a API terminate
dentro de outra função ou invocar o webhook HTTP POST terminatePostUri referenciado na resposta 202 acima. Para usar o webhook, substitua {text}
pelo motivo do encerramento antecipado. A URL HTTP POST terá uma aparência semelhante a esta:
POST https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason=Because&taskHub=SampleHubVS&connection=Storage&code={systemKey}
Próximas etapas
Este exemplo demonstrou como usar as Funções Duráveis para monitorar o status de uma fonte externa usando temporizadores duráveis e lógica condicional. O próximo exemplo mostra como usar eventos externos e temporizadores duráveis para lidar com interação humana.