Obsah
1. Mikroslužby založené na REST API
2. Jakou technologii vybrat pro implementaci mikroslužby s REST API?
3. Jednoduchá aplikace s HTTP serverem naprogramovaná v Go
4. Použití protokolu HTTPS namísto HTTP
5. Druhá varianta serveru používajícího protokol HTTPS
6. Použití frameworku Flask a Connexion při tvorbě služeb s REST API v Pythonu
7. Nejjednodušší aplikace založená pouze na frameworku Flask
8. Specifikace API s využitím Swaggeru
10. První služba naprogramovaná v Pythonu
11. Přidání dvou koncových bodů do specifikace služby
12. Implementace handlerů nových koncových bodů
13. Otestování druhé varianty služby pomocí Swagger UI i nástroje curl
14. Vylepšení služby – koncový bod pro poslání zprávy
15. Specifikace nového koncového bodu s určením parametrů
16. Demonstrační implementace nového koncového bodu a otestování služby
17. Poslání zprávy přímo přes Swagger UI
18. Čtvrtá varianta služby s koncovým bodem vracejícím data ve formátu JSON
19. Repositář s demonstračními příklady
1. Mikroslužby založené na REST API
V prvních pěti částech seriálu o mikroslužbách jsme se zabývali především teoretickým popisem vlastní architektury mikroslužeb a taktéž některých základních technologií, které jsou ve světě mikroslužeb často používány. Druhá část tohoto seriálu bude ovšem zaměřena více prakticky, protože si postupně ukážeme způsoby použití jednotlivých technologií. Demonstrační příklady budou většinou naprogramovány v Pythonu a taktéž v jazyku Go. Volba těchto dvou programovacích jazyků samozřejmě není náhodná, protože se s oběma zmíněnými jazyky ve světě mikroslužeb poměrně často setkáme, i když je na tomto místě nutné říci, že se Python od Go v mnoha oblastech odlišuje (nebo jinými slovy – tyto jazyky se vzájemně doplňují). Dále se pochopitelně v této oblasti setkáme s Javou a dalšími programovacími jazyky postavenými nad JVM (především se Scalou), popř. s C#, s JavaScriptem a TypeScriptem (Node.js) a někdy též s jazykem Ruby.
![](https://i.iinfo.cz/images/372/microservices1-4-prev.png)
Obrázek 1: Jednotlivé mikroslužby mezi sebou mohou komunikovat například s využitím protokolu HTTP (REST API), STOMP atd. Dnes nás bude zajímat právě HTTP.
Nejdříve si ukážeme, jakým způsobem se v Pythonu a Go vytváří služby s rozhraním REST API. Ve skutečnosti se nemusí jednat o nic složitého, protože REST API je postaveno na protokolu HTTP (popř. dnes spíše HTTPS) a při použití vhodných knihoven a frameworků je implementace jednotlivých koncových bodů (endpointů) REST API stejně přímočará jako vytvoření běžného API.
2. Jakou technologii vybrat pro implementaci mikroslužby s REST API?
Samotné mikroslužby mohou být realizovány relativně snadno (alespoň z hlediska použitých technologií, protože business logika může být někdy hodně složitá). Primárním způsobem komunikace mikroslužeb s okolím je v současnosti protokol HTTP(S) a REST; ovšem nesmíme zapomenout ani na protokoly používané při komunikaci s message brokerem (AMQP, MQTT, STOMP, XMPP). Pro samotnou realizaci je možné použít různé programovací jazyky, například (z těch používanějších jazyků) Javu, Python, Ruby, JavaScript (TypeScript) a v neposlední řadě i programovací jazyk Go. Pro každý z těchto jazyků popř. ekosystémů existují knihovny a frameworky určené (mimo dalších použití) právě pro tvorbu mikroslužeb. V následující tabulce jsou některé z těchto knihoven/frameworků vypsány, ovšem pochopitelně se nejedná o úplný přehled, protože mikroslužbu můžeme v případě potřeby naprogramovat například i v čistém céčku s využitím socketů, knihoven 0MQ, nanomsg atd.:
# | Jazyk/ekosystém | Knihovna či framework |
---|---|---|
1 | Java | Vert.x, Spring Boot, Dropwizard, Ninja |
2 | Python | Django REST framework, FlaskRESTful, restless, Falcon |
3 | Ruby | Rails, Grape, Sinatra, Hanami |
4 | JavaScript/TypeScript | Seneca, Koa, LoopBack |
5 | Go | Goa, standardní balíček net/http |
6 | Clojure | Ring, Liberator, Compojure |
Pro ukázku, že webovou službu (či její kostru) lze vytvořit v prakticky v jakémkoli programovacím jazyce si ukažme primitivní službu naprogramovanou v Clojure, v níž se využívá knihovna Clojure Ring:
(ns webapp1.core (:gen-class)) (require '[ring.adapter.jetty :as jetty]) (defn app "Funkce predstavujici kostru webove aplikace." [request] {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}) (defn -main "Spusteni webove aplikace na portu 8080." [& args] (jetty/run-jetty app {:port 8080}))
Navíc je možné s využitím knihovny Hiccup vygenerovat i HTML stránku z dat reprezentovaných rozšířenými S-výrazy:
(ns htmltest1.core (:gen-class)) (require '[hiccup.page :as page]) (defn html-page [] (page/xhtml [:head [:title "Hiccup test #1"] [:meta {:name "Generator" :content "Clojure"}] [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}] ] [:body [:h1 "Hiccup test #1"] [:div "Hello world!"] ]))
3. Jednoduchá aplikace s HTTP serverem naprogramovaná v Go
V programovacím jazyku Go můžeme pro implementaci HTTP serverů použít balíček pojmenovaný výstižně net/http, který je součástí standardní knihovny nainstalované současně s překladačem a dalšími standardními nástroji ekosystému Go (jinými slovy – kromě samotného Go již není vyžadována žádná další komponenta). Jak již název tohoto balíčku napovídá, obsahuje funkce a nové datové typy určené pro práci s protokolem HTTP, a to jak pro klienty, kteří posílají dotazy (request) na servery, tak i pro implementaci vlastních serverů zpracovávajících dotazy a vytvářejících odpovědi (response). Ostatně právě existence tohoto balíčku měla poměrně velký vliv na oblíbenost programovacího jazyka Go pro tvorbu síťově orientovaných aplikací a tím pádem i pro implementaci služeb a mikroslužeb v tomto programovacím jazyku.
Nejužitečnější vlastností balíčku net/http je jeho podpora pro vytvoření skutečného a plnohodnotného HTTP serveru, a to doslova na několika řádcích programového kódu. Základem pro vytvoření HTTP serveru je funkce nazvaná HandleFunc, která nám umožňuje zaregistrovat obslužnou funkci (takzvaný handler) v případě, že je server volán s určitým URL (endpointem). Můžeme si například zaregistrovat handler pro endpoint /:
http.HandleFunc("/", mainEndpoint)
Hlavička funkce HandleFunc z balíčku net/http vypadá takto:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Povšimněte si, že druhým parametrem této funkce je jiná funkce (onen handler) s hlavičkou:
func MujHandler(ResponseWriter, *Request)
Tomuto handleru se předávají dva objekty – objekt sloužící pro zápis hlavičky i těla odpovědi a objekt s informacemi o požadavku došlého od klienta.
Konkrétně může implementace našeho handleru poslat na výstup (typu ResponseWriter) jednoduchý text, který bude zaslán klientovi v celé HTTP odpovědi (s hlavičkami, stavovým kódem, případnou délkou zprávy, cookies, atd. atd.):
func mainEndpoint(writer http.ResponseWriter, request *http.Request) { io.WriteString(writer, "Hello world!\n") }
Následně již stačí server spustit na určeném portu. My jsme si pro testovací účely zvolili port 8000:
http.ListenAndServe(":8000", nil)
Úplná implementace takto jednoduchého HTTP serveru může vypadat takto:
package main import ( "io" "net/http" ) func mainEndpoint(writer http.ResponseWriter, request *http.Request) { io.WriteString(writer, "Hello world!\n") } func main() { http.HandleFunc("/", mainEndpoint) http.ListenAndServe(":8000", nil) }
K otestování tohoto příkladu využijeme například známou utilitku curl:
$ curl localhost:8000 Hello world!
Popř. pro podrobnější výstup můžeme utilitě curl předat přepínač -v:
$ curl -v localhost:8000 * Rebuilt URL to: localhost:8000/ * Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8000 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.35.0 > Host: localhost:8000 > Accept: */* > < HTTP/1.1 200 OK < Date: Wed, 13 Feb 2019 19:31:17 GMT < Content-Length: 13 < Content-Type: text/plain; charset=utf-8 < Hello world! * Connection #0 to host localhost left intact
Nic nám samozřejmě nebrání otestovat naši službu přímo ve webovém prohlížeči:
![](https://i.iinfo.cz/images/598/microservices6-1.png)
Obrázek 2: Výsledek zobrazený ve Firefoxu.
![](https://i.iinfo.cz/images/598/microservices6-2-prev.png)
Obrázek 3: Výsledek zobrazený v prohlížeči Lynx.
Partner seriálu o mikroslužbách
V IGNUM mají rádi technologie a staví na nich vlastní mikroslužby, díky kterým je registrace a správa domén, hostingů a e-mailů pro zákazníky hračka. Vymýšlet jednoduchá řešení pro obsluhu složitých systémů a uvádět je v život je výzva. Společnost IGNUM miluje mikroslužby a je proto hrdým partnerem tohoto seriálu.
4. Použití protokolu HTTPS namísto HTTP
V této kapitole si ukážeme, jakým způsobem je možné s využitím programovacího jazyka Go a jeho základních knihoven implementovat HTTPS server. Uvidíme, že samotná implementace bude velmi podobná implementaci HTTP serveru, ovšem pro správnou funkčnost protokolu HTTPS budeme muset použít externího nástroje pro vytvoření privátního klíče serveru a jeho certifikátu. Bližší informace o samotném konceptu, na němž je HTTPS postaveno, naleznete například na stránce https://en.wikipedia.org/wiki/HTTPS.
Samotná implementace jednoduchého HTTPS serveru se ve skutečnosti podobá implementaci běžného HTTP serveru. Jediným rozdílem je, že se server bude spouštět odlišným způsobem. Namísto:
http.ListenAndServe(":8000", nil)
použijeme:
http.ListenAndServeTLS(":4443", "server.crt", "server.key", nil)
kde „server.crt“ a „server.key“ jsou soubory, které si vygenerujeme podle návodu uvedeného v navazující kapitole.
Korektnější bude provést kontrolu, zda funkce http.ListenAndServeTLS neskončila s chybou:
err := http.ListenAndServeTLS(":4443", "server.crt", "server.key", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }
5. Druhá varianta serveru používajícího protokol HTTPS
Implementace HTTPS serveru bude vypadat takto:
package main import ( "io" "log" "net/http" ) func mainEndpoint(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "text/plain") io.WriteString(writer, "Hello world!\n") } func main() { http.HandleFunc("/", mainEndpoint) err := http.ListenAndServeTLS(":4443", "server.crt", "server.key", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }
Prozatím ovšem ještě nemáme připraveny všechny potřebné soubory, a to ani na straně serveru, ani na straně klienta. Proto se pokus o zavolání serveru nezdaří:
$ curl -v https://localhost:4443 * Rebuilt URL to: https://localhost:4443/ * Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 4443 (#0) * successfully set certificate verify locations: * CAfile: none CApath: /etc/ssl/certs * SSLv3, TLS handshake, Client hello (1): * SSLv3, TLS handshake, Server hello (2): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS alert, Server hello (2): * SSL certificate problem: self signed certificate * Closing connection 0 curl: (60) SSL certificate problem: self signed certificate More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
Nyní musíme vygenerovat soubory server.key a server.crt, které bude naše implementace HTTPS serveru používat.
Privátní klíč používaný serverem vygenerujeme s využitím nástroje openssl, který již pravděpodobně máte v systému nainstalovaný:
$ openssl genrsa -out server.key 2048
Výsledkem by měl být soubor server.key obsahující klíč pro 2048bitové RSA:
-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAyhgV0Gmo0dCkdcEO5X0J//xKD73E+n0pyw7htM/1gPnU9h2X JYNqFnq0xz9QsxTAPHYLkueW1SNhWT9gq3Sad/M3Cxb6uomB+i0qSk71Q6PkaqHQ KveSsNNa4lw5DBFVjTD/JPnWhVvKS7v0A266snwmTi18+fRpWZ/TaQN5uQRy0bik ... ... ... RbBs8QKBgB3dl+NGC+iTPVviPixjFkP5KAcf3Is57Pi0RUgTj4Fmq2q90Scoi6Vv OzZoo2XHmqAnqxV75OWqA7NiKdBHwWg2O9BupFa+G3uRXgoP7cpCeT9ZoUbbMKww j49BC9GHmOhlcz3fBT4YE3OgoeM5Fga8sVtWew9YkKe/gBAkR0+Y -----END RSA PRIVATE KEY-----
Dále, opět nástrojem openssl, vytvoříme soubor s certifikátem s uvedenou platností:
$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
V tomto okamžiku se bude openssl interaktivně ptát na několik údajů, které jsou v přepisu konverzace vypsány tučně:
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:CZ State or Province Name (full name) [Some-State]: Locality Name (eg, city) []:Kocourkov Organization Name (eg, company) [Internet Widgits Pty Ltd]:Mestska garda Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:localhost Email Address []:nikdo@nikde.cz
Výsledný soubor nazvaný „server.crt“ může vypadat takto:
-----BEGIN CERTIFICATE----- MIIDlzCCAn+gAwIBAgIJALw/AUKjIONeMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV BAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlLb2NvdXJrb3Yx FjAUBgNVBAoMDU1lc3Rza2EgZ2FyZGExEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x ... ... ... rnPxzautFYD++NjhJ/j537I0Lcj9t/DkjvBiECZYkJF8p9dL4+lWZXc27n3RYS6L 7Dj+85WUXwxkfPqhkggGi8jSrZesUDtWw4XFw7bLGOaKTo2JMGxOfxL3RhrFtiMO 4j9Rvz9cr2R6a0Y= -----END CERTIFICATE-----
HTTPS server již máme připravený (a pravděpodobně i úspěšně spuštěný), takže ještě musíme provést konfiguraci na straně klienta. Nejprve získáme certifikát z běžícího serveru, opět s využitím nástroje openssl, který zkontaktuje HTTPS server a získá od něj všechny potřebné údaje:
$ openssl s_client -showcerts -connect localhost:4443
Výsledek může vypadat následovně – nejprve je zobrazen vlastní certifikát a posléze další metadata:
CONNECTED(00000003) --- Certificate chain 0 s:/C=CZ/ST=Some-State/L=Kocourkov/O=Mestska garda/CN=localhost i:/C=CZ/ST=Some-State/L=Kocourkov/O=Mestska garda/CN=localhost -----BEGIN CERTIFICATE----- MIIDlzCCAn+gAwIBAgIJALw/AUKjIONeMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV BAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlLb2NvdXJrb3Yx FjAUBgNVBAoMDU1lc3Rza2EgZ2FyZGExEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x OTAyMTYyMDE0MTJaFw0yOTAyMTMyMDE0MTJaMGIxCzAJBgNVBAYTAkNaMRMwEQYD VQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlLb2NvdXJrb3YxFjAUBgNVBAoMDU1l c3Rza2EgZ2FyZGExEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMoYFdBpqNHQpHXBDuV9Cf/8Sg+9xPp9KcsO4bTP9YD5 1PYdlyWDahZ6tMc/ULMUwDx2C5LnltUjYVk/YKt0mnfzNwsW+rqJgfotKkpO9UOj 5Gqh0Cr3krDTWuJcOQwRVY0w/yT51oVbyku79ANuurJ8Jk4tfPn0aVmf02kDebkE ctG4pIsfu6HPfBPyMFgEBXYDiObKfGCEgpnGIeX8Li4n9r8Law45+KEFz4n2Yj3c Jq77ZLopjV4w4n+JZYNXkK9JeV9twM5PrsYLqrLEvstXqyo/2ccYFtMvTsXx57SY BEKABLYuPsEzYVzNo2lgtXJxxgcXfS+PrCnH6KhS4c0CAwEAAaNQME4wHQYDVR0O BBYEFBdmG7K8HXslTnR5OkOLZCGVPD1hMB8GA1UdIwQYMBaAFBdmG7K8HXslTnR5 OkOLZCGVPD1hMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKYKeOz4 u0er6BQmy72Wc4H9ZjWnXjphfVAC0UK2gz7UHXDnyzfrBKR6FkVbeiIUjBzbrWG5 xUoHcZsfayefOEEpqcAyKpa8CRkbissHF6qtFZArt+cOWwTmmPfYQxfa9KqVP13L FcZqcchyvTLdNTGD5ZBtLI9B5Pcm4a7vgEdMqdJb++FpSNhW9H2P0wvfhTK7Mh6/ rnPxzautFYD++NjhJ/j537I0Lcj9t/DkjvBiECZYkJF8p9dL4+lWZXc27n3RYS6L 7Dj+85WUXwxkfPqhkggGi8jSrZesUDtWw4XFw7bLGOaKTo2JMGxOfxL3RhrFtiMO 4j9Rvz9cr2R6a0Y= -----END CERTIFICATE----- --- Server certificate subject=/C=CZ/ST=Some-State/L=Kocourkov/O=Mestska garda/CN=localhost issuer=/C=CZ/ST=Some-State/L=Kocourkov/O=Mestska garda/CN=localhost --- No client certificate CA names sent --- SSL handshake has read 1529 bytes and written 421 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES128-GCM-SHA256 Session-ID: 1F74CA2806A25B6AE774B7A0D4E470A477D16B73130EAB527C03FE861086B076 Session-ID-ctx: Master-Key: 555000EA285A2EEFE95D5268756ED98CC71711075F8036251EC6B34494C7ED8F6861EDDDA842BC847922F536AC9CF0EA Key-Arg : None PSK identity: None PSK identity hint: None SRP username: None TLS session ticket: 0000 - 71 d2 38 c8 ec 5d e7 5e-c7 fe a7 f5 d4 33 03 e8 q.8..].^.....3.. 0010 - e9 ba 8d ff f2 1e e7 9f-8e 66 9b 0e 2b 34 eb db .........f..+4.. 0020 - 83 c1 4b 96 f7 a4 67 71-26 3a a5 2d 65 2e 08 ae ..K...gq&:.-e... 0030 - 84 38 3f bf 90 2e 04 0a-62 25 aa 0e 86 ca 31 4a .8?.....b%....1J 0040 - b7 2a 1b 1a b7 b0 b2 d9-d5 3c f4 9e 39 37 a1 69 .*.......<..97.i 0050 - 6c ac 2c 8b 83 d0 25 53-da 7c 43 17 4d 55 d6 fc l.,...%S.|C.MU.. 0060 - 7a 55 2f 74 bd 6a e2 6f-59 b0 cc 16 d7 e0 a9 14 zU/t.j.oY....... 0070 - 71 35 d4 62 27 85 93 f7- q5.b'... Start Time: 1550348405 Timeout : 300 (sec) Verify return code: 18 (self signed certificate) ---
Nástroj budeme muset ukončit klávesovou zkratkou Ctrl+C.
Pro klienta je nejjednodušší přesměrovat výstup z předchozího volání nástroje openssl do souboru, který bývá nazván „certs.pem“:
$ openssl s_client -showcerts -connect localhost:4443 > certs.pem
$ openssl s_client -showcerts -connect localhost:4443 > certs.pem
To je ze strany klienta vše – klient pouze potřebuje pro každé volání použít soubor „certs.pem“ s certifikátem serveru, aby ho mohl ověřit.
Nyní již máme vše připravené pro to, aby se klient mohl připojit k serveru s využitím certifikátu uloženého v lokálním souboru certs.pem. Příkaz volající utilitu curl bude vypadat následovně:
$ curl -v --cacert certs.pem https://localhost:4443
Po spuštění nástroje curl by se měl klient připojit k serveru s využitím protokolu HTTPS, ověřit certifikát a následně přečíst odpověď serveru („Hello world!“):
* Rebuilt URL to: https://localhost:4443/ * Hostname was NOT found in DNS cache % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 4443 (#0) * successfully set certificate verify locations: * CAfile: certs.pem CApath: /etc/ssl/certs * SSLv3, TLS handshake, Client hello (1): } [data not shown] * SSLv3, TLS handshake, Server hello (2): { [data not shown] * SSLv3, TLS handshake, CERT (11): { [data not shown] * SSLv3, TLS handshake, Server key exchange (12): { [data not shown] * SSLv3, TLS handshake, Server finished (14): { [data not shown] * SSLv3, TLS handshake, Client key exchange (16): } [data not shown] * SSLv3, TLS change cipher, Client hello (1): } [data not shown] * SSLv3, TLS handshake, Finished (20): } [data not shown] * SSLv3, TLS change cipher, Client hello (1): { [data not shown] * SSLv3, TLS handshake, Finished (20): { [data not shown] * SSL connection using ECDHE-RSA-AES128-GCM-SHA256 * Server certificate: * subject: C=CZ; ST=Some-State; L=Kocourkov; O=Mestska garda; CN=localhost * start date: 2019-02-16 20:14:12 GMT * expire date: 2029-02-13 20:14:12 GMT * common name: localhost (matched) * issuer: C=CZ; ST=Some-State; L=Kocourkov; O=Mestska garda; CN=localhost * SSL certificate verify ok. > GET / HTTP/1.1 > User-Agent: curl/7.35.0 > Host: localhost:4443 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/plain < Date: Sat, 16 Feb 2019 20:20:29 GMT < Content-Length: 13 < { [data not shown] 100 13 100 13 0 0 1106 0 --:--:-- --:--:-- --:--:-- 1181 * Connection #0 to host localhost left intact Hello world!
Vidíme, že certifikát byl skutečně použit.
Pokud se používá webový prohlížeč s GUI, bývá práce s certifikáty snazší:
![](https://i.iinfo.cz/images/552/golang-12-1-prev.png)
Obrázek 4: Informace o certifikátu při prvním přístupu k našemu serveru. Certifikát jsme si podepsali sami a nebyl potvrzen žádnou certifikační autoritou.
![](https://i.iinfo.cz/images/552/golang-12-2-prev.png)
Obrázek 5: Zobrazení dalších informací o certifikátu – tyto informace jsme zadali při jeho vytváření.
Po potvrzení, že certifikátu důvěřujeme, se již zobrazí kýžená webová stránka.
6. Použití frameworku Flask a Connexion při tvorbě služeb s REST API v Pythonu
Ve druhé části dnešního článku o mikroslužbách si ukážeme, jakým způsobem je možné vyvinout jednoduchou (mikro)službu s rozhraním REST API, tentokrát však nikoli v jazyku Go, ale v Pythonu. Zatímco v případě programovacího jazyka Go byl ukázán spíše nízkoúrovňový přístup, v němž jsme jednotlivé koncové body registrovali ručně (a vlastně jsme vůbec neřešili případ, kdy je část cesty v URL proměnná, což je v REST API časté), v Pythonu použijeme frameworky pojmenované Flask a Connexion, které umožňují, aby celé REST API bylo deklarativně popsáno v samostatném souboru swagger.yaml. V tomto souboru mohou být uvedeny všechny důležité metainformace o REST API, včetně jmen funkcí, které implementují jednotlivé handlery požadavků.
To však není zdaleka vše, protože framework Connexion umožňuje, aby služba obsahovala i automaticky generované webové uživatelské rozhraní s popisem všech koncových bodů, které je možné z tohoto rozhraní přímo volat. Nemusíme se tedy spoléhat na externí utilitu curl, protože samotná služba obsahuje vše potřebné pro své vlastní otestování.
![](https://i.iinfo.cz/images/598/microservices6-3-prev.png)
Obrázek 6: Editor souborů s popisem REST API.
7. Nejjednodušší aplikace založená pouze na frameworku Flask
S využitím frameworku Flask je vytvoření jednoduché webové aplikace (služby) s malým množstvím koncových bodů velmi snadné. Ostatně se stačí podívat na následující příklad, který po svém spuštění inicializuje HTTP server, který po poslání požadavku na koncový bod / vrátí odpověď s textem „Hello, world!“. Povšimněte si, jakým způsobem je s využitím dekorátoru určena vazba mezi koncovým bodem a handlerem (funkcí zavolanou při příchodu požadavku). Samotný handler v tom nejjednodušším případě pouze vrátí řetězec, který je považován za tělo odpovědi (HTTP kód bude 200 OK):
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!\n'
export FLASK_APP=01_basic_app.py PYTHONDONTWRITEBYTECODE=1 python3 -m flask run
8. Specifikace API s využitím Swaggeru
V předchozím textu jsme se zmínili o tom, že popis celého rozhraní REST API je uložen v souborech se jménem swagger.yaml. Koncovka .yaml je pochopitelná, protože se používá formát YAML (YAML Ain't Markup Language). Ovšem proč se soubor jmenuje swagger? Původně se totiž popis REST API v tomto formátu objevil ve frameworku Swagger, takže se pro něj vžilo označení „Swagger Specification“. Ovšem samotný formát metadat s popisem REST API se později přejmenoval ze „Swagger Specification“ na „OpenAPI Specification“; současně se o vývoj tohoto formátu a nástrojů okolo něho postavených začala starat OpenAPI Initiative. Bližší informace o OpenAPI je možné nalézt na stránce https://swagger.io/specification/, popř. pro verzi 2.0 přímo na GitHubu na adrese https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md. Základní informace a seznam dalších informačních zdrojů je uveden i na Wikipedii, konkrétně na stránce https://en.wikipedia.org/wiki/OpenAPI_Specification. Dnes existuje velké množství nástrojů pro generování a editaci popisu REST API, pro vytvoření serveru či klienta na základě tohoto popisu apod. Seznam těchto nástrojů je dostupný na stránkách https://github.com/OAI/OpenAPI-Specification/blob/master/IMPLEMENTATIONS.md#implementations a https://github.com/APIs-guru/awesome-openapi3.
9. Kostra služby
Nyní si ukážeme kostru webové služby vytvořenou s využitím frameworků Flask a taktéž Connexion. Druhý z těchto frameworků mj. poslouží pro zobrazení webové stránky, přes kterou bude možné webovou službu ovládat. Kostra celé služby obsahuje jen minimum souborů a má jednoduchou strukturu:
├── requirements.in ├── requirements.txt ├── run.sh ├── service1.py ├── setup.cfg └── swagger └── swagger.yaml
Význam jednotlivých souborů:
Soubor | Stručný popis |
---|---|
requirements.in | seznam balíčků, na kterých služba závisí |
requirements.txt | balíčky i s uvedením verze (automaticky generováno z předchozího souboru) |
run.sh | skript pro spuštění služby |
service1.py | vlastní implementace webové služby |
setup.cfg | pomocný soubor s konfigurací používaný různými nástroji pro Python |
swagger/swagger.yaml | metainformace o REST API |
Obsah souboru requirements.in se seznamem balíčků, na kterých služba závisí:
connexion jsonschema
Obsah souboru requirements.txt obsahujícího balíčky i s uvedením verze (automaticky generováno z předchozího souboru):
connexion==1.1.15 jsonschema==2.5.1
Skript pro inicializaci virtuálního prostředí Pythonu a spuštění webové služby:
#!/bin/bash -ex export NOVENV=0 function prepare_venv() { virtualenv -p python3 venv && source venv/bin/activate && python3 "$(which pip3)" install -r requirements.txt } [ "$NOVENV" == "1" ] || prepare_venv || exit 1 PYTHONDONTWRITEBYTECODE=1 python3 service1.py
10. První služba naprogramovaná v Pythonu
Implementace webové služby (prozatím bez koncových bodů) vypadá následovně. Můžeme v něm vidět pouze inicializaci serveru, předání adresáře obsahujícího soubor swagger.yaml a spuštění serveru na portu 8080:
#!/usr/bin/env python3 import connexion def main(): """Start the Flask app.""" app = connexion.App(__name__, specification_dir='./swagger/') app.add_api('swagger.yaml', arguments={'title': 'Service 1'}) app.run(port=8080) if __name__ == '__main__': main()
Mnohem zajímavější jsou metainformace o REST API. Ty jsou zapsány v souboru s formátem YAML, v němž se jednotlivé poduzly specifikují odsazením a dvojtečkou, podobně jako v Pythonu. Povšimněte si, že používáme specifikaci OpenAPI verze 2.0:
--- swagger: "2.0" info: description: "Simple REST API service." version: "1.0.0" title: "Service 1" contact: email: "tisnik@somewhere.else" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" basePath: "/api/v1" tags: - name: "Service settings" description: "The service settings" schemes: - "https" - "http" paths: {} definitions: {} externalDocs: description: "Find out more about Swagger" url: "http://swagger.io"
První demonstrační příklad založený na frameworku Flask a Connexion spustíme přes skript run.sh. Tento skript nejdříve připraví virtuální prostředí Pythonu a následně inicializuje server. Na posledním řádku je informace o portu, na kterém server naslouchá požadavkům od klientů:
$ ./run.sh + python3 service1.py * Serving Flask app "service1" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
Po spuštění prvního demonstračního příkladu si můžeme vyzkoušet, zda služba skutečně přijímá požadavky. Prozatím sice nemáme žádné koncové body REST API, ovšem minimálně by mělo být možné získat popis REST API, tentokrát pro změnu ve formátu JSON:
$ curl http://localhost:8080/api/v1/swagger.json
Výsledkem předchozího volání by měla být odpověď s nenaformátovaným JSONem:
{"basePath":"/api/v1","definitions":{},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"},"info":{"contact":{"email":"tisnik@somewhere.else"},"description":"Simple REST API service.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"title":"Service 1","version":"1.0.0"},"paths":{},"schemes":["https","http"],"swagger":"2.0","tags":[{"description":"The service settings","name":"Service settings"}]}
Po naformátování:
{ "basePath": "/api/v1", "definitions": { }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" }, "info": { "contact": { "email": "tisnik@somewhere.else" }, "description": "Simple REST API service.", "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, "title": "Service 1", "version": "1.0.0" }, "paths": { }, "schemes": [ "https", "http" ], "swagger": "2.0", "tags": [ { "description": "The service settings", "name": "Service settings" } ] }
Samozřejmě se můžeme podívat i na všechny hlavičky posílané klientem na server i serverem zpět na klienta:
$ curl -v http://localhost:8080/api/v1/swagger.json
Směr komunikace se pozná podle prefixů jednotlivých řádků. Požadavek klienta začíná znakem >, odpověď serveru naopak znakem <:
* Trying ::1... * TCP_NODELAY set * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /api/v1/swagger.json HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.53.1 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Content-Type: application/json < Content-Length: 475 < Server: Werkzeug/0.15.4 Python/3.6.3 < Date: Wed, 10 Jul 2019 12:03:07 GMT < {"basePath":"/api/v1","definitions":{},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"},"info":{"contact":{"email":"tisnik@somewhere.else"},"description":"Simple REST API service.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"title":"Service 1","version":"1.0.0"},"paths":{},"schemes":["https","http"],"swagger":"2.0","tags":[{"description":"The service settings","name":"Service settings"}]} * Closing connection 0
![](https://i.iinfo.cz/images/598/microservices6-4.png)
Obrázek 7: Zobrazení popisu služby přímo v prohlížeči.
11. Přidání dvou koncových bodů do specifikace služby
Nyní do webové služby přidáme dva koncové body /liveness a /readiness. Povšimněte si, že vazba na handlery (konkrétní funkce zapsané plným jménem) je provedena přímo v souboru swagger.yaml a nikoli ve zdrojovém kódu aplikace:
/liveness: get: tags: [Service settings] operationId: "service2.get_liveness" summary: "Get service liveness" responses: 200: description: Service is alive /readiness: get: tags: [Service settings] operationId: "service2.get_readiness" summary: "Get service readiness" responses: 200: description: Service is ready
Úplný obsah souboru swagger.yaml vypadá následovně:
--- swagger: "2.0" info: description: "Simple REST API service." version: "1.0.0" title: "Service 2" contact: email: "tisnik@somewhere.else" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" basePath: "/api/v1" tags: - name: "Service settings" description: "The service settings" schemes: - "https" - "http" paths: /liveness: get: tags: [Service settings] operationId: "service2.get_liveness" summary: "Get service liveness" responses: 200: description: Service is alive /readiness: get: tags: [Service settings] operationId: "service2.get_readiness" summary: "Get service readiness" responses: 200: description: Service is ready definitions: {} externalDocs: description: "Find out more about Swagger" url: "http://swagger.io"
12. Implementace handlerů nových koncových bodů
Samotná implementace obou handlerů (umístěná ve zdrojovém souboru service2.py) je přímočará, protože ve skutečnosti vrátíme prázdnou strukturu (objekt) a stavový kód HTTP bude za všech okolností 200 OK:
def health_check(): """Check the health status of the service.""" return {}, 200 def get_readiness(): """Get service readiness status.""" return health_check() def get_liveness(): """Get service liveness status.""" return health_check()
Úplný zdrojový kód druhého příkladu vytvořeného v Pythonu vypadá následovně:
#!/usr/bin/env python3 import connexion def health_check(): """Check the health status of the service.""" return {}, 200 def get_readiness(): """Get service readiness status.""" return health_check() def get_liveness(): """Get service liveness status.""" return health_check() def main(): """Start the Flask app.""" app = connexion.App(__name__, specification_dir='./swagger/') app.add_api('swagger.yaml', arguments={'title': 'Service 2'}) app.run(port=8080) if __name__ == '__main__': main()
13. Otestování druhé varianty služby pomocí Swagger UI i nástroje curl
Druhý demonstrační příklad již svým uživatelům (tedy klientům) nabízí dva koncové body /api/v1/liveness a /api/v1/readiness. Tyto koncové body by měly vracet prázdnou strukturu ve formátu JSON a stavový kód HTTP odpovědi by měl být 200 OK, tj. informace o tom, že požadavek byl korektní a server ho správně zpracoval:
$ curl http://localhost:8080/api/v1/liveness {}
I druhý koncový bod by se měl chovat naprosto stejným způsobem:
$ curl http://localhost:8080/api/v1/readiness {}
Můžeme se pochopitelně přesvědčit i o HTTP stavovém kódu HTTP odpovědi (viz též https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). Pro zobrazení celé odpovědi, samozřejmě včetně HTTP kódu, použijeme přepínač -v:
$ curl -v http://localhost:8080/api/v1/liveness * Trying ::1... * TCP_NODELAY set * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /api/v1/liveness HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.53.1 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Content-Type: application/json < Content-Length: 3 < Server: Werkzeug/0.15.4 Python/3.6.3 < Date: Wed, 10 Jul 2019 12:56:16 GMT < {} * Closing connection 0
Zajímavější ovšem bude přesměrovat webový prohlížeč na adresu /api/v1/ui. Na této adrese se nachází dynamicky generovaná HTML stránka obsahující jak popis REST API (všech koncových bodů), tak i nástroje, které nám umožní tyto body zavolat:
![](https://i.iinfo.cz/images/598/microservices6-5-prev.png)
Obrázek 8: Popis služby se zobrazením koncových bodů.
![](https://i.iinfo.cz/images/598/microservices6-6-prev.png)
Obrázek 9: Zobrazení podrobnějších informací o vybraném koncovém bodu.
![](https://i.iinfo.cz/images/598/microservices6-7-prev.png)
Obrázek 10: Otestování koncového bodu přímo z prohlížeče.
14. Vylepšení služby – koncový bod pro poslání zprávy
Webovou službu ještě vylepšíme, a to přidáním koncového bodu sloužícího pro poslání zprávy vybranému uživateli (ve skutečnosti se zpráva nikam nepošle, ovšem koncový bod bude existovat). Přitom bude služba kontrolovat, zda zadaný uživatel existuje a zda mu lze poslat zprávu. Jedná se o komplikaci služby, protože budeme muset:
- Přečíst parametr (jméno uživatele) předaného přes URL
- Přečíst tělo požadavku, protože bude obsahovat text zprávy
- Zkontrolovat oba parametry a v případě chyby vrátit vhodný stavový kód HTTP
- Zkontrolovat existenci příjemce, opět s navrácením vhodného stavového kódu
- Zkontrolovat, jestli příjemce může zprávy přijímat
15. Specifikace nového koncového bodu s určením parametrů
Nový koncový bod je nakonfigurován následujícím způsobem:
/message/{recipient}: post: tags: [Basic operations] operationId: "service3.send_message" summary: "Post a message" consumes: - "text/plain" produces: - "application/json" parameters: - name: "recipient" in: "path" description: "The recipient for the message" required: true type: "string" - name: "message" in: body description: "Message body" required: true schema: type: string responses: 200: description: "Successful operation" 400: description: "Invalid recipient supplied" 404: description: "The specified recipient was not found" 405: description: "Not allowed - it is not allowed to send the message to selected recipient" 500: description: "Any other failure"
Povšimněte si, jak vypadá specifikace parametru předaného přes URL (ve skutečnosti se nejedná o skutečný parametr, ale o součást URL). Dále je specifikováno, že se v těle požadavku očekává běžný text (plain text), zatímco v těle odpovědi bude JSON. Oba parametry (příjemce, text zprávy) jsou dále podrobněji popsány a dokonce je pro uživatele REST API specifikováno, které HTTP kódy může odpověď obsahovat a co přesně v kontextu tohoto koncového bodu znamenají.
16. Demonstrační implementace nového koncového bodu a otestování služby
Do zdrojového kódu příkladu přidáme novou funkci, které se při jejím zavolání automaticky předají oba parametry popsané v souboru swagger.yaml. Kromě toho vytvoříme dvojici n-tic se seznamem všech příjemců i se seznamem příjemců, kterým je dovoleno zprávy posílat:
KNOWN_RECIPIENTS = ("Puchmajer", "Meyer", "Pihrt", "Jason", "Drson", "Trachta", "Fristensky") ALLOWED_RECIPIENTS = ("Jason", "Drson", "Trachta", "Fristensky") def send_message(recipient, message): """Send a message to selected recipient.""" if not recipient: return {"Status": "error", "Reason": "No recipient supplied"}, 400 if not message: return {"Status": "error", "Reason": "Message is empty"}, 400 if recipient not in KNOWN_RECIPIENTS: return {"Status": "not found", "Reason": "The specified recipient was not found"}, 404 if recipient not in ALLOWED_RECIPIENTS: return {"Status": "forbidden", "Reason": "Not allowed - it is not allowed to send the message to selected recipient"}, 405 try: # kod pro skutecne poslani zpravy return {"Status": "ok"}, 200 except Exception as e: return {"Status": "error", "Reason": str(e)}, 500
Přístup ke koncovým bodům, ke kterým se přistupuje metodou POST, PUT či DELETE, je nepatrně složitější, než je tomu u metody GET. Použitou metodu totiž musíme specifikovat, a to přepínačem -X metoda. První pokus by tedy mohl vypadat následovně:
$ curl -X POST http://localhost:8080/api/v1/message/Drson
V tomto případě ovšem server (zcela podle očekávání) odpoví, že nebylo specifikováno tělo požadavku, v němž měla být uložena zpráva:
{ "Reason": "Message is empty", "Status": "error" }
Celý požadavek je tedy nutné strukturovat odlišně. Nejdříve nastavíme typ dat v požadavku:
--header 'Content-Type: text/plain'
Následně (což ovšem není v tomto případě povinné) určíme, v jakém formátu očekáváme odpověď:
--header 'Accept: application/json'
Samotné tělo požadavku s textem zprávy se specifikuje přepínačem -d (ovšem musíme si dát pozor na to, aby nezačínalo zavináčem):
-d 'Transfer Of Usd $21,500.000{Twenty - One Million, Five Hundred Thousand Us Dollars Only. I am a member of the Federal Government Of Nigerian National Petroleum Corporation (N.N.P.C). Sometime ago, a contract was awarded to a foreign firm in the Petroleum Trust Fund (P.T.F.) BY MY COMMITTEE'
Celé volání nástroje curl tedy může vypadat následovně:
$ curl -X POST --header 'Content-Type: text/plain' --header 'Accept: application/json' -d 'Transfer Of Usd $21,500.000{Twenty - One Million, Five Hundred Thousand Us Dollars Only. I am a member of the Federal Government Of Nigerian National Petroleum Corporation (N.N.P.C). Sometime ago, a contract was awarded to a foreign firm in the Petroleum Trust Fund (P.T.F.) BY MY COMMITTEE' 'http://localhost:8080/api/v1/message/Drson'
S výsledkem:
{ "Status": "ok" }
17. Poslání zprávy přímo přes Swagger UI
Zprávu samozřejmě můžeme poslat i přes UI. Postup je následující:
![](https://i.iinfo.cz/images/598/microservices6-8-prev.png)
Obrázek 11: Popis třetí varianty webové služby.
![](https://i.iinfo.cz/images/598/microservices6-9-prev.png)
Obrázek 12: Popis koncového bodu /message.
![](https://i.iinfo.cz/images/598/microservices6-10-prev.png)
Obrázek 13: Oba parametry jsou povinné a je je nutné vyplnit.
![](https://i.iinfo.cz/images/598/microservices6-11-prev.png)
Obrázek 14: Pokus o poslání zprávy neznámému příjemci.
![](https://i.iinfo.cz/images/598/microservices6-12-prev.png)
Obrázek 15: Úspěšné doručení zprávy.
18. Čtvrtá varianta služby s koncovým bodem vracejícím data ve formátu JSON
Jen pro úplnost si demonstrační příklad rozšíříme o další koncový bod, který bude sloužit pro získání statistiky o tom, kolik zpráv bylo jednotlivým příjemcům prozatím posláno. Při poslání zprávy se informace o příjemci zaznamená:
counter = Counter() def send_message(recipient, message): """Send a message to selected recipient.""" if not recipient: return {"Status": "error", "Reason": "No recipient supplied"}, 400 if not message: return {"Status": "error", "Reason": "Message is empty"}, 400 if recipient not in KNOWN_RECIPIENTS: return {"Status": "not found", "Reason": "The specified recipient was not found"}, 404 if recipient not in ALLOWED_RECIPIENTS: return {"Status": "forbidden", "Reason": "Not allowed - it is not allowed to send the message to selected recipient"}, 405 try: # kod pro skutecne poslani zpravy counter[recipient] += 1 return {"Status": "ok"}, 200 except Exception as e: return {"Status": "error", "Reason": str(e)}, 500
Implementace logiky nového koncového bodu je v tomto případě opět přímočará:
def message_statistic(): """Returns message statistic.""" return counter
Nový koncový bod samozřejmě budeme muset zaregistrovat a popsat:
--- swagger: "2.0" info: description: "Simple REST API service." version: "1.0.0" title: "Service 4" contact: email: "tisnik@somewhere.else" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" basePath: "/api/v1" tags: - name: "Basic operations" description: "Basic operations" - name: "Service settings" description: "The service settings" schemes: - "https" - "http" paths: /liveness: get: tags: [Service settings] operationId: "service4.get_liveness" summary: "Get service liveness" responses: 200: description: Service is alive /readiness: get: tags: [Service settings] operationId: "service4.get_readiness" summary: "Get service readiness" responses: 200: description: Service is ready /message_statistic: get: tags: [Basic operations] operationId: "service4.message_statistic" summary: "Returns basic statistic about sent messages" responses: 200: description: Query was successful produces: - "application/json" /message/{recipient}: post: tags: [Basic operations] operationId: "service4.send_message" summary: "Post a message" consumes: - "text/plain" produces: - "application/json" parameters: - name: "recipient" in: "path" description: "The recipient for the message" required: true type: "string" - name: "message" in: body description: "Message body" required: true schema: type: string responses: 200: description: "Successful operation" 400: description: "Invalid recipient supplied" 404: description: "The specified recipient was not found" 405: description: "Not allowed - it is not allowed to send the message to selected recipient" 500: description: "Any other failure" definitions: {} externalDocs: description: "Find out more about Swagger" url: "http://swagger.io"
Nyní si můžeme službu spustit, poslat několik zpráv a následně si zobrazit statistiku:
$ curl http://localhost:8080/api/v1/message_statistic
S výsledkem:
{ "Drson": 1, "Jason": 3 }
Totéž lze provést přes Swagger UI:
![](https://i.iinfo.cz/images/598/microservices6-13-prev.png)
Obrázek 16: Použití nového koncového bodu ze Swagger UI.
![](https://i.iinfo.cz/images/598/microservices6-14-prev.png)
Obrázek 17: Swagger editor dokonce dokáže vygenerovat celou kostru aplikace.
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/microservices (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- OpenAPI Initiative (OAI)
https://www.openapis.org/ - OpenAPI Implementations
https://github.com/OAI/OpenAPI-Specification/blob/master/IMPLEMENTATIONS.md#implementations - awesome-openapi3
https://github.com/APIs-guru/awesome-openapi3 - Swagger (software)
https://en.wikipedia.org/wiki/Swagger_(software) - Editor pro swagger soubory s popisem REST API
http://editor.swagger.io/# - YAML
https://yaml.org/ - YAML na Wikipedii
https://en.wikipedia.org/wiki/YAML - Flask na PyPi
https://pypi.org/project/Flask/ - Connexion na PyPi
https://pypi.org/project/connexion/ - Werkzeug na PyPi
https://pypi.org/project/Werkzeug/ - gdbgui 0.7.8.3: browser-based gdb frontend using Flask and JavaScript to visually debug C, C++, Go, or Rust
https://pypi.python.org/pypi/gdbgui - Alertmanager
https://prometheus.io/docs/alerting/alertmanager/ - Grafana support for Prometheus
https://prometheus.io/docs/visualization/grafana/ - goa
https://stackshare.io/goa - goa (GitHub)
https://github.com/goadesign/goa - Grafana support for Prometheus
https://prometheus.io/docs/visualization/grafana/ - Useful Tools for Managing Complexity of Microservice Architecture
https://blog.byndyusoft.com/useful-tools-for-managing-complexity-of-microservice-architecture-109a2289acc - Three pillars of microservice culture
https://www.oreilly.com/ideas/three-pillars-of-microservice-culture - Prometheus: from metrics to insight
https://prometheus.io/ - Docker Swarm
https://docs.docker.com/swarm/ - Kubernetes: production-Grade Container Orchestration
https://kubernetes.io/ - 29 Top Tools for Building Microservices on All Levels
https://dzone.com/articles/30top-tools-for-building-microservices-on-all-leve - The 8 best open-source tools for building microservice apps
https://techbeacon.com/enterprise-it/8-best-open-source-tools-building-microservice-apps - Consul
https://www.consul.io/ - Apache ZooKeeper
https://zookeeper.apache.org/ - ZooKeeper: Because Coordinating Distributed Systems is a Zoo
http://zookeeper.apache.org/doc/current/index.html - ZooKeeper: A Distributed Coordination Service for Distributed Applications
http://zookeeper.apache.org/doc/current/zookeeperOver.html - Understanding Kafka with Legos (video)
https://www.youtube.com/watch?v=Q5wOegcVa8E - Apache Kafka Tutorial For Beginners (video)
https://www.youtube.com/watch?v=U4y2R3v9tlY - Franz Kafka (Wikipedia)
https://en.wikipedia.org/wiki/Franz_Kafka - NATS
https://nats.io/about/ - NATS Streaming Concepts
https://nats.io/documentation/streaming/nats-streaming-intro/ - NATS Streaming Server
https://nats.io/download/nats-io/nats-streaming-server/ - NATS Introduction
https://nats.io/documentation/ - NATS Client Protocol
https://nats.io/documentation/internals/nats-protocol/ - NATS Messaging (Wikipedia)
https://en.wikipedia.org/wiki/NATS_Messaging - Stránka Apache Software Foundation
http://www.apache.org/ - Logstash
https://www.elastic.co/products/logstash - Elasticsearch
https://www.elastic.co/products/elasticsearch - Understanding When to use RabbitMQ or Apache Kafka
https://content.pivotal.io/blog/understanding-when-to-use-rabbitmq-or-apache-kafka - Part 1: Apache Kafka for beginners – What is Apache Kafka?
https://www.cloudkarafka.com/blog/2016–11–30-part1-kafka-for-beginners-what-is-apache-kafka.html - What are some alternatives to Apache Kafka?
https://www.quora.com/What-are-some-alternatives-to-Apache-Kafka - What is the best alternative to Kafka?
https://www.slant.co/options/961/alternatives/~kafka-alternatives - Apache Flume
https://flume.apache.org/index.html - Snare
https://www.snaresolutions.com/ - The Log: What every software engineer should know about real-time data's unifying abstraction
https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying - A super quick comparison between Kafka and Message Queues
https://hackernoon.com/a-super-quick-comparison-between-kafka-and-message-queues-e69742d855a8?gi=e965191e72d0 - Kafka Queuing: Kafka as a Messaging System
https://dzone.com/articles/kafka-queuing-kafka-as-a-messaging-system - Microservices – Not a free lunch!
http://highscalability.com/blog/2014/4/8/microservices-not-a-free-lunch.html - Microservices, Monoliths, and NoOps
http://blog.arungupta.me/microservices-monoliths-noops/ - Microservice Design Patterns
http://blog.arungupta.me/microservice-design-patterns/ - Vision of a microservice revolution
https://www.jolie-lang.org/vision.html - Microservices: a definition of this new architectural term
https://martinfowler.com/articles/microservices.html - Mikroslužby
http://voho.eu/wiki/mikrosluzba/ - Microservice Prerequisites
https://martinfowler.com/bliki/MicroservicePrerequisites.html - Microservices in Practice, Part 1: Reality Check and Service Design (vyžaduje registraci)
https://ieeexplore.ieee.org/document/7819415 - Microservice Trade-Offs
https://www.martinfowler.com/articles/microservice-trade-offs.html - What is a microservice? (from a linguistic point of view)
http://claudioguidi.blogspot.com/2017/03/what-microservice-from-linguisitc.html - Microservices (Wikipedia)
https://en.wikipedia.org/wiki/Microservices - Fallacies of distributed computing (Wikipedia)
https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing - Service (systems architecture)
https://en.wikipedia.org/wiki/Service_(systems_architecture) - Microservices in a Nutshell
https://www.thoughtworks.com/insights/blog/microservices-nutshell - What is Microservices?
https://smartbear.com/solutions/microservices/ - Mastering Chaos – A Netflix Guide to Microservices
https://www.youtube.com/watch?v=CZ3wIuvmHeM&t=17s - Messaging in Microservice Architecture
https://www.youtube.com/watch?v=MkQWQ5f-SEY - Pattern: Messaging
https://microservices.io/patterns/communication-style/messaging.html - Microservices Messaging: Why REST Isn’t Always the Best Choice
https://blog.codeship.com/microservices-messaging-rest-isnt-always-best-choice/ - Protocol buffers
https://developers.google.com/protocol-buffers/ - BSON
http://bsonspec.org/ - Apache Avro!
https://avro.apache.org/ - REST vs Messaging for Microservices – Which One is Best?
https://solace.com/blog/experience-awesomeness-event-driven-microservices/ - How did we end up here?
https://gotocon.com/dl/goto-chicago-2015/slides/MartinThompson_and_ToddMontgomery_HowDidWeEndUpHere.pdf - Scaling microservices with message queues to handle data bursts
https://read.acloud.guru/scaling-microservices-with-message-queue-2d389be5b139 - Microservices: What are smart endpoints and dumb pipes?
https://stackoverflow.com/questions/26616962/microservices-what-are-smart-endpoints-and-dumb-pipes - Common Object Request Broker Architecture
https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture - Enterprise service bus
https://en.wikipedia.org/wiki/Enterprise_service_bus - Microservices vs SOA : What’s the Difference
https://www.edureka.co/blog/microservices-vs-soa/ - Pravda o SOA
https://businessworld.cz/reseni-a-realizace/pravda-o-soa-2980 - Is it a good idea for Microservices to share a common database?
https://www.quora.com/Is-it-a-good-idea-for-Microservices-to-share-a-common-database - Pattern: Shared database
https://microservices.io/patterns/data/shared-database.html - Is a Shared Database in Microservices Actually an Anti-pattern?
https://hackernoon.com/is-shared-database-in-microservices-actually-anti-pattern-8cc2536adfe4 - Shared database in microservices is a problem, yep
https://ayende.com/blog/186914-A/shared-database-in-microservices-is-a-problem-yep - Microservices with shared database? using multiple ORM's?
https://stackoverflow.com/questions/43612866/microservices-with-shared-database-using-multiple-orms - Examples of microservice architecture
https://www.coursera.org/lecture/intro-ibm-microservices/examples-of-microservice-architecture-JXOFj - Microservices: The Rise Of Kafka
https://movio.co/blog/microservices-rise-kafka/ - Building a Microservices Ecosystem with Kafka Streams and KSQL
https://www.confluent.io/blog/building-a-microservices-ecosystem-with-kafka-streams-and-ksql/ - An introduction to Apache Kafka and microservices communication
https://medium.com/@ulymarins/an-introduction-to-apache-kafka-and-microservices-communication-bf0a0966d63 - ACID (computer science)
https://en.wikipedia.org/wiki/ACID_(computer_science) - Distributed transaction
https://en.wikipedia.org/wiki/Distributed_transaction - Two-phase commit protocol
https://en.wikipedia.org/wiki/Two-phase_commit_protocol - Why is 2-phase commit not suitable for a microservices architecture?
https://stackoverflow.com/questions/55249656/why-is-2-phase-commit-not-suitable-for-a-microservices-architecture - 4 reasons why microservices resonate
https://www.oreilly.com/ideas/4-reasons-why-microservices-resonate - Pattern: Microservice Architecture
https://microservices.io/patterns/microservices.html - Pattern: Monolithic Architecture
https://microservices.io/patterns/monolithic.html - Pattern: Saga
https://microservices.io/patterns/data/saga.html - Pattern: Database per service
https://microservices.io/patterns/data/database-per-service.html - Pattern: Access token
https://microservices.io/patterns/security/access-token.html - Databázová integrita
https://cs.wikipedia.org/wiki/Datab%C3%A1zov%C3%A1_integrita - Referenční integrita
https://cs.wikipedia.org/wiki/Referen%C4%8Dn%C3%AD_integrita - Introduction into Microservices
https://specify.io/concepts/microservices - Are Microservices ‘SOA Done Right’?
https://intellyx.com/2015/07/20/are-microservices-soa-done-right/ - The Hardest Part About Microservices: Your Data
https://blog.christianposta.com/microservices/the-hardest-part-about-microservices-data/ - From a monolith to microservices + REST
https://www.slideshare.net/InfoQ/from-a-monolith-to-microservices-rest-the-evolution-of-linkedins-service-architecture - DevOps and the Myth of Efficiency, Part I
https://blog.christianposta.com/devops/devops-and-the-myth-of-efficiency-part-i/ - DevOps and the Myth of Efficiency, Part II
https://blog.christianposta.com/devops/devops-and-the-myth-of-efficiency-part-ii/ - Standing on Distributed Shoulders of Giants: Farsighted Physicists of Yore Were Danged Smart!
https://queue.acm.org/detail.cfm?id=2953944 - Building DistributedLog: High-performance replicated log service
https://blog.twitter.com/engineering/en_us/topics/infrastructure/2015/building-distributedlog-twitter-s-high-performance-replicated-log-servic.html - Turning the database inside-out with Apache Samza
https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/ - Debezium: Stream changes from your databases.
https://debezium.io/ - Change data capture
https://en.wikipedia.org/wiki/Change_data_capture - Apache Samza (Wikipedia)
https://en.wikipedia.org/wiki/Apache_Samza - Storm (event processor)
https://en.wikipedia.org/wiki/Storm_(event_processor) - kappa-architecture.com
http://milinda.pathirage.org/kappa-architecture.com/ - Questioning the Lambda Architecture
https://www.oreilly.com/ideas/questioning-the-lambda-architecture - Lambda architecture
https://en.wikipedia.org/wiki/Lambda_architecture - Event stream processing
https://en.wikipedia.org/wiki/Event_stream_processing - How to beat the CAP theorem
http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html - Kappa Architecture Our Experience
https://events.static.linuxfound.org/sites/events/files/slides/ASPgems%20-%20Kappa%20Architecture.pdf - Messaging Patterns in Event Driven Microservice Architectures
https://www.youtube.com/watch?v=3×Dc4MEYuHI - Why monolithic apps are often better than microservices
https://gigaom.com/2015/11/06/why-monolithic-apps-are-often-better-than-microservices/ - How Enterprise PaaS Can Add Critical Value to Microservices
https://apprenda.com/blog/enterprise-paas-microservices/ - Common React Mistakes: Monolithic Components and a Lack of Abstraction
https://www.pmg.com/blog/common-react-mistakes-monolithic-components-lack-abstraction/ - From monolith to microservices – to migrate or not to migrate?
https://altkomsoftware.pl/en/blog/monolith-microservices/ - Command–query separation
https://en.wikipedia.org/wiki/Command%E2%80%93query_separation - GOTO 2016: Messaging and Microservices (Clemens Vasters)
https://www.youtube.com/watch?v=rXi5CLjIQ9kx - GOTO Amsterdam 2019
https://gotoams.nl/ - Lesson 2 – Kafka vs. Standard Messaging
https://www.youtube.com/watch?v=lwMjjTT1Q-Q - CommandQuerySeparation (Martin Fowler)
https://martinfowler.com/bliki/CommandQuerySeparation.html - Command–query separation
https://en.wikipedia.org/wiki/Command%E2%80%93query_separation - CQRS – Martin Fowler
https://martinfowler.com/bliki/CQRS.html - Lesson 12 – CQRS and Microservices
https://www.youtube.com/watch?v=pUGvXUBfvEE - Message queues - the right way to process and work with realtime data on your servers
https://www.ably.io/blog/message-queues-the-right-way - Function as a service
https://en.wikipedia.org/wiki/Function_as_a_service - AWS Lambda
https://en.wikipedia.org/wiki/AWS_Lambda