Terza parte della esercitazione sul flusso di lavoro Implementazione delle attività - Amazon Simple Workflow Service

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Terza parte della esercitazione sul flusso di lavoro Implementazione delle attività

In questa parte del tutorial, implementeremo ogni attività nel nostro flusso di lavoro, cominciando con una classe di base che fornisce alcune funzionalità comuni per il codice delle attività.

Definizione di un tipo di attività di base

Quando abbiamo progettato il flusso di lavoro, abbiamo identificato le seguenti attività:

  • get_contact_activity

  • subscribe_topic_activity

  • wait_for_confirmation_activity

  • send_result_activity

Ora implementeremo ognuna di queste attività. Poiché queste condividono alcune funzionalità, creeremo un codice comune utilizzabile da tutte che chiameremo BasicActivity e che definiremo in un nuovo file denominato basic_activity.rb.

Come con gli altri file di origine, includeremo utils.rb per accedere alla funzione init_domain con cui configureremo l'esempio di dominio.

require_relative 'utils.rb'

Successivamente, dichiareremo la classe di attività di base e alcuni dati comuni che ci interessano per ogni attività. Registreremo l'istanza AWS::SimpleWorkflow::ActivityType, il nome e i risultati dell'attività negli attributi della classe.

class BasicActivity attr_accessor :activity_type attr_accessor :name attr_accessor :results

Questi attributi accedono ai dati dell'istanza definiti nella classe»initializemetodo, che richiede un'attivitànomee facoltativoversionee map ofopzionida utilizzare quando si registra l'attività con Amazon SWF.

def initialize(name, version = 'v1', options = nil) @activity_type = nil @name = name @results = nil # get the domain to use for activity tasks. @domain = init_domain # Check to see if this activity type already exists. @domain.activity_types.each do | a | if (a.name == @name) && (a.version == version) @activity_type = a end end if @activity_type.nil? # If no options were specified, use some reasonable defaults. if options.nil? options = { # All timeouts are in seconds. :default_task_heartbeat_timeout => 900, :default_task_schedule_to_start_timeout => 120, :default_task_schedule_to_close_timeout => 3800, :default_task_start_to_close_timeout => 3600 } end @activity_type = @domain.activity_types.register(@name, version, options) end end

Come con la registrazione del tipo di flusso di lavoro, se un tipo di attività è già registrato, possiamo recuperarlo esaminando la raccolta workflow_types del dominio. Se il tipo di attività è introvabile, verrà registrato.

Inoltre, come con i tipi di flusso di lavoro, puoi impostare delle opzioni di default che sono archiviate insieme al tipo di attività alla registrazione dello stesso.

L'ultima cosa di cui necessita la nostra attività di base è un modo di esecuzione coerente. A questo proposito, definiremo un metodo do_activity che accetta un task di attività Come mostrato di seguito, possiamo utilizzare il task di attività passato per ricevere dati via il relativo attributo di istanza input.

def do_activity(task) @results = task.input # may be nil return true end end

La creazione della classe BasicActivity è completata. Ora la utilizzeremo per definire le attività in modo semplice e coerente.

Definizione di GetContactActivity

La prima attività che viene eseguita durante un'esecuzione di flusso di lavoro èget_contact_activityche recupera le informazioni sulla sottoscrizione dell'utente all'argomento Amazon SNS.

Creazione di un nuovo file denominatoget_contact_activity.rbe richiedono entrambiyaml, che useremo per preparare una stringa per il passaggio ad Amazon SWF ebasic_activity.rb, che useremo come base per questoOttieni attività di contattoclasse.

require 'yaml' require_relative 'basic_activity.rb' # **GetContactActivity** provides a prompt for the user to enter contact # information. When the user successfully enters contact information, the # activity is complete. class GetContactActivity < BasicActivity

Poiché abbiamo incluso il codice di registrazione dell'attività in BasicActivity, il metodo initialize per GetContactActivity è alquanto semplice. È sufficiente chiamare il costruttore della classe di base con il nome dell'attività, ovvero get_contact_activity. Questo è tutto quello che dobbiamo fare per registrare la nostra attività.

# initialize the activity def initialize super('get_contact_activity') end

Ora definiremo il metodo do_activity, che richiede l'indirizzo e-mail e/o il numero di telefono dell'utente.

def do_activity(task) puts "" puts "Please enter either an email address or SMS message (mobile phone) number to" puts "receive SNS notifications. You can also enter both to use both address types." puts "" puts "If you enter a phone number, it must be able to receive SMS messages, and must" puts "be 11 digits (such as 12065550101 to represent the number 1-206-555-0101)." input_confirmed = false while !input_confirmed puts "" print "Email: " email = $stdin.gets.strip print "Phone: " phone = $stdin.gets.strip puts "" if (email == '') && (phone == '') print "You provided no subscription information. Quit? (y/n)" confirmation = $stdin.gets.strip.downcase if confirmation == 'y' return false end else puts "You entered:" puts " email: #{email}" puts " phone: #{phone}" print "\nIs this correct? (y/n): " confirmation = $stdin.gets.strip.downcase if confirmation == 'y' input_confirmed = true end end end # make sure that @results is a single string. YAML makes this easy. @results = { :email => email, :sms => phone }.to_yaml return true end end

Alla fine di do_activity, inseriamo l'indirizzo e-mail e il numero di telefono dell'utente in una mappa e utilizziamo to_yaml per convertire l'intera mappa in una stringa YAML. Questo parametro è necessario che tutti i risultati che passi ad Amazon SWF quando completi un'attività devono essereSolo dati stringa. La capacità di Ruby di convertire facilmente oggetti in stringhe YAML e quindi di riconvertirle in oggetti è particolarmente utile in questo caso.

L'implementazione di get_contact_activity è terminata. Utilizzeremo questi dati in seguito nell'implementazione di subscribe_topic_activity.

Definizione di SubscribeTopicActivity

Ora approfondiremo Amazon SNS e creeremo un'attività che utilizza le informazioni generate daget_contact_activityper sottoscrivere l'utente a un argomento Amazon SNS.

Crea un nuovo file denominato subscribe_topic_activity.rb, aggiungi gli stessi requisiti utilizzati per get_contact_activity, dichiara la classe e fornisci il relativo metodo initialize.

require 'yaml' require_relative 'basic_activity.rb' # **SubscribeTopicActivity** sends an SMS / email message to the user, asking for # confirmation. When this action has been taken, the activity is complete. class SubscribeTopicActivity < BasicActivity def initialize super('subscribe_topic_activity') end

Ora che disponiamo del codice per configurare e registrare l'attività, aggiungeremo del codice per creare un argomento Amazon SNS. Per farlo, useremo ilAWS:: SNS:: Clientoggettocreate_topicmetodo.

Aggiungi ilcreate_topicnella classe, che accetta un oggetto client Amazon SNS passato.

def create_topic(sns_client) topic_arn = sns_client.create_topic(:name => 'SWF_Sample_Topic')[:topic_arn] if topic_arn != nil # For an SMS notification, setting `DisplayName` is *required*. Note that # only the *first 10 characters* of the DisplayName will be shown on the # SMS message sent to the user, so choose your DisplayName wisely! sns_client.set_topic_attributes( { :topic_arn => topic_arn, :attribute_name => 'DisplayName', :attribute_value => 'SWFSample' } ) else @results = { :reason => "Couldn't create SNS topic", :detail => "" }.to_yaml return nil end return topic_arn end

Una volta ottenuto l'ARN dell'argomento, possiamo utilizzarlo con il client Amazon SNSset_topic_attributesmetodo per impostare l'argomentoDisplayNamenecessaria per inviare messaggi SMS con Amazon SNS.

Infine, definiremo il metodo do_activity. Inizieremo con il raccogliere tutti i dati passati via l'opzione input durante la pianificazione dell'attività. Come menzionato in precedenza, questi dati devono essere passati come stringa creata utilizzando to_yaml. Dopo il recupero dei dati, utilizzeremo YAML.load per convertirli in oggetti Ruby.

Di seguito è illustrato l'inizio di do_activity, che ci consente di recuperare i dati di input.

def do_activity(task) activity_data = { :topic_arn => nil, :email => { :endpoint => nil, :subscription_arn => nil }, :sms => { :endpoint => nil, :subscription_arn => nil }, } if task.input != nil input = YAML.load(task.input) activity_data[:email][:endpoint] = input[:email] activity_data[:sms][:endpoint] = input[:sms] else @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml puts(" #{@results.inspect}") return false end # Create an SNS client. This is used to interact with the service. Set the # region to $SMS_REGION, which is a region that supports SMS notifications # (defined in the file `utils.rb`). sns_client = AWS::SNS::Client.new( :config => AWS.config.with(:region => $SMS_REGION))

Nel caso non ricevessimo alcun input, non ci sarebbe molto da fare. Dovremmo semplicemente generare un errore nell'attività.

Tuttavia, supponendo che tutto è ok, continueremo a definire la nostrado_activitymetodo, ottieni un client Amazon SNS conAWS SDK for Ruby, e passalo al nostrocreate_topicmetodo per creare l'argomento Amazon SNS.

# Create the topic and get the ARN activity_data[:topic_arn] = create_topic(sns_client) if activity_data[:topic_arn].nil? return false end

A questo punto, è importante considerare quanto segue:

  • UsiamoAWS.config.withper impostare la regione per il client Amazon SNS. Poiché intendiamo inviare messaggi SMS, utilizziamo la regione che supporta il servizio SMS dichiarata in utils.rb.

  • Salviamo l'ARN dell'argomento nella mappa activity_data. Questi sono alcuni dei dati che saranno passati all'attività successiva nel flusso di lavoro.

Infine, questa attività sottoscrive l'utente all'argomento Amazon SNS, utilizzando gli endpoint passati (e-mail e SMS). L'utente non deve immettere entrambi gli endpoint, ma almeno uno.

# Subscribe the user to the topic, using either or both endpoints. [:email, :sms].each do | x | ep = activity_data[x][:endpoint] # don't try to subscribe an empty endpoint if (ep != nil && ep != "") response = sns_client.subscribe( { :topic_arn => activity_data[:topic_arn], :protocol => x.to_s, :endpoint => ep } ) activity_data[x][:subscription_arn] = response[:subscription_arn] end end

AWS::SNS::Client.subscribe accetta l'ARN dell'argomento e il protocollo (trasformato nella chiave mappa activity_data per l'endpoint corrispondente).

Comprimiamo infine le informazioni per la prossima attività in formato YAML, in modo da poterle restituire ad Amazon SWF.

# if at least one subscription arn is set, consider this a success. if (activity_data[:email][:subscription_arn] != nil) or (activity_data[:sms][:subscription_arn] != nil) @results = activity_data.to_yaml else @results = { :reason => "Couldn't subscribe to SNS topic", :detail => "" }.to_yaml puts(" #{@results.inspect}") return false end return true end end

L'implementazione di subscribe_topic_activity è terminata. Nella sezione successiva, definiremo wait_for_confirmation_activity.

Definizione di WaitForConfirmationActivity

Una volta eseguita la sottoscrizione di un utente a un argomento Amazon SNS, l'utente dovrà confermare la richiesta di sottoscrizione. In questo caso, attendiamo la conferma dell'utente via e-mail o SMS.

L'attività che attende la conferma della sottoscrizione da parte dell'utente è denominata wait_for_confirmation_activity e la definiremo in questa sezione. Per prima cosa, crea un nuovo file denominato wait_for_confirmation_activity.rb e configuralo come nelle attività precedenti.

require 'yaml' require_relative 'basic_activity.rb' # **WaitForConfirmationActivity** waits for the user to confirm the SNS # subscription. When this action has been taken, the activity is complete. It # might also time out... class WaitForConfirmationActivity < BasicActivity # Initialize the class def initialize super('wait_for_confirmation_activity') end

Successivamente, inizieremo a definire il metodo do_activity e a recuperare i dati di input in una variabile locale denominata subscription_data.

def do_activity(task) if task.input.nil? @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml return false end subscription_data = YAML.load(task.input)

Ora che disponiamo dell'ARN dell'argomento, possiamo creare una nuova istanza di AWS::SNS::Topic e passargli l'ARN per recuperare l'argomento.

topic = AWS::SNS::Topic.new(subscription_data[:topic_arn]) if topic.nil? @results = { :reason => "Couldn't get SWF topic ARN", :detail => "Topic ARN: #{topic.arn}" }.to_yaml return false end

A questo punto, verificheremo l'argomento per determinare se l'utente ha confermato la sottoscrizione utilizzando uno degli endpoint. Per considerare l'attività come riuscita, è sufficiente che venga confermato un solo endpoint.

Un argomento Amazon SNS gestisce un elenco dellesottoscrizioniPer questo argomento, possiamo quindi determinare se l'utente ha confermato o meno una particolare sottoscrizione verificando se l'ARN della sottoscrizione è impostato su qualcosa di diverso daPendingConfirmation.

# loop until we get some indication that a subscription was confirmed. subscription_confirmed = false while(!subscription_confirmed) topic.subscriptions.each do | sub | if subscription_data[sub.protocol.to_sym][:endpoint] == sub.endpoint # this is one of the endpoints we're interested in. Is it subscribed? if sub.arn != 'PendingConfirmation' subscription_data[sub.protocol.to_sym][:subscription_arn] = sub.arn puts "Topic subscription confirmed for (#{sub.protocol}: #{sub.endpoint})" @results = subscription_data.to_yaml return true else puts "Topic subscription still pending for (#{sub.protocol}: #{sub.endpoint})" end end end

Se otteniamo un ARN per la sottoscrizione, lo registreremo nei dati di risultato dell'attività, lo convertiremo in YAML e restituiremo true da do_activity, a indicare il completamento senza errori dell'attività.

Poiché è possibile che la conferma di una sottoscrizione richieda parecchio tempo, di tanto in tanto chiameremo record_heartbeat sul task di attività. Questo parametro indica ad Amazon SWF che l'attività è ancora in fase di elaborazione. Fornisce inoltre informazioni sull'avanzamento dell'attività (se state eseguendo un'operazione, come l'elaborazione di file, per la quale è possibile determinare l'avanzamento).

task.record_heartbeat!( { :details => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" }) # sleep a bit. sleep(4.0) end

Qui si conclude il ciclo while. Se per un qualsiasi motivo, il ciclo while non riesce, segnaleremo un errore e termineremo il metodo do_activity.

if (subscription_confirmed == false) @results = { :reason => "No subscriptions could be confirmed", :detail => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" }.to_yaml return false end end end

L'implementazione di wait_for_confirmation_activity risulta completata. Abbiamo quindi una sola attività da definire, ovvero send_result_activity.

Definizione di SendResultActivity

Se siamo arrivati fino a questo punto nella creazione del flusso di lavoro, la sottoscrizione dell'utente a un argomento Amazon SNS è riuscita e che l'utente l'ha confermata.

La nostra ultima attività, send_result_activity, invia all'utente la conferma dell'avvenuta sottoscrizione all'argomento, utilizzando l'argomento in questione e l'endpoint con il quale l'utente ha confermato la sottoscrizione.

Crea un nuovo file denominato send_result_activity.rb e configuralo come per le altre attività.

require 'yaml' require_relative 'basic_activity.rb' # **SendResultActivity** sends the result of the activity to the screen, and, if # the user successfully registered using SNS, to the user using the SNS contact # information collected. class SendResultActivity < BasicActivity def initialize super('send_result_activity') end

Anche il metodo do_activity inizia in modo simile: ottiene i dati di input dal flusso di lavoro, li converte in formato YAML e quindi utilizza l'ARN dell'argomento per creare un'istanza AWS::SNS::Topic.

def do_activity(task) if task.input.nil? @results = { :reason => "Didn't receive any input!", :detail => "" } return false end input = YAML.load(task.input) # get the topic, so we publish a message to it. topic = AWS::SNS::Topic.new(input[:topic_arn]) if topic.nil? @results = { :reason => "Couldn't get SWF topic", :detail => "Topic ARN: #{topic.arn}" } return false end

Una volta che abbiamo l'argomento, vi pubblicheremo un messaggio (che riprodurremo anche sullo schermo).

@results = "Thanks, you've successfully confirmed registration, and your workflow is complete!" # send the message via SNS, and also print it on the screen. topic.publish(@results) puts(@results) return true end end

La pubblicazione su un argomento Amazon SNS invia il messaggio a cui forniscituttidegli endpoint sottoscritti e confermati che esistono per quell'argomento. Di conseguenza, se l'utente ha confermato la sottoscrizione con un'e-mail e un numero SMS, riceverà due messaggi di conferma, uno a ogni endpoint.

Fasi successive

L'implementazione di send_result_activity risulta completata. Nella fase successiva, combinerai tutte queste attività in un'applicazione di attività che gestisce i task di attività e che può avviare attività in risposta, come descritto nella sezione Quarta parte del tutorial sul flusso di lavoro Quinta parte del poller dei task di attività.