Heroku: dodatki, logi i monitorowanie

We wcześniejszym artykule wprowadziłem Was w podstawy Heroku oraz wdrożyłem aplikację opartą na spring boot. W drugiej części zademonstruję, jak dodać obsługiwanie baz danych. Poruszymy również temat logów i metryk.

Heroku dostarcza szereg dodatków uzupełniających naszą aplikację, na przykład bazy danych, funkcję kolejkowania wiadomości, buforowania, przechowywania danych, pocztę elektroniczną i inne. Dostarczają je (zazwyczaj) strony trzecie; istnieje nawet osobna strona do pobierania ich (https://elements.heroku.com/addons).

Dodanie obsługi baz danych dla aplikacji opartej na Heroku

Jeśli chcemy dodać obsługę bazy danych (PostgreSQL jest dostępny za darmo), wystarczy wpisać następuje polecenie w Heroku CLI:

heroku addons:create heroku-PostgreSQL

Po wykonaniu polecenia, nasza baza danych zostaje utworzona i jest gotowa do użytku. Polecenie

heroku config
Pokaże nam adres URL bazy danych, który nie będzie ładnie wyglądać.

postgres://qamtrwcghvuzfm:e5cc937842c248126e9dba981e55159d4233f0a690c8abaa5f76ad7984ba6

Ale bez obaw, nie trzeba go nawet kopiować i wklejać. Na ten moment zostawmy go i przejdźmy do naszej bazy kodu, aby skorzystać z trwałości danych.

W pierwszej kolejności dodajmy trzy zależności do pliku pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>1.5.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.1.4</version>
</dependency>

Spring-boot-starter-data-jpa pozwoli nam wykorzystać z repozytoriów spring data, aby odciążyć nas z konieczności pisania szablonowego kodu CRUD. Spring-boot-starter-data-rest automagicznie wyeksponuje nasze repozytoria jako punkty końcowe REST. Ostatni element to sterownik JDBC dla PostgreSQL.

Teraz stwórzmy standardową encję Person

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    private Person(){}

    public Person(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

Nie zapomnij o pustym konstruktorze. Mając już naszą encję możemy utworzyć repozytorium spring-data.

public interface PeopleRepository extends CrudRepository<Person, Long> {

}

Teraz rozciągnijmy nieco nasz kontroler oraz szablon thymeleaf, aby wykorzystać trwałość danych:

@Controller
public class HelloController {

    PeopleRepository peopleRepository;

    @Autowired
    public HelloController(PeopleRepository peopleRepository) {
        this.peopleRepository = peopleRepository;
    }

    @RequestMapping("hi")
    public String hello(Model model) {
        model.addAttribute("people", peopleRepository.findAll());
        return "hello";
    }
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Clockwork Java Hello</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
        <p th:each="person : ${people}" th:text="${person.name}"/>
    </body>
</html>

Teraz przejdźmy do pliku konfiguracyjnego application.properties

spring.datasource.url: ${JDBC_DATABASE_URL}
spring.datasource.username: ${JDBC_DATABASE_USERNAME}
spring.datasource.password: ${JDBC_DATABASE_PASSWORD}
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
Być może zauważysz odsyłacze do zmiennych środowiskowych – JDBC_DATABASE_URL i tak dalej. O co z nimi chodzi?

A no o to, że w Heroku dyno (kontener unixowy, w którym działa nasza aplikacja) dostarcza zmienne środowiskowe z danymi uwierzytelniającymi, adresami, portami itp. do naszych usług dodatkowych. Zatem w tym wypadku nie musimy na sztywno kodować brzydkiego URL JDBC otrzymanego z „heroku config”. Zamiast tego użyjemy zmiennych systemowych.

Teraz należy wdrożyć zmodyfikowaną aplikację (git commit;heroku login; git push heroku master) i przejść do https://radiant-island-50498.herokuapp.com/, gdzie powinniśmy zobaczyć więcej informacji o punktach końcowych REST dostarczonych przez spring data rest (nie przejmuj się obiektami „person”, w końcu to jednak demo).

{
  "_links" : {
    "persons" : {
      "href" : "https://radiant-island-50498.herokuapp.com/persons"
    },
    "profile" : {
      "href" : "https://radiant-island-50498.herokuapp.com/profile"
    }
  }
}

Teraz wykorzystaj curl, postman lub dowolne preferowane narzędzie, by przesłać żądania POST do punktu końcowego https://radiant-island-50498.herokuapp.com/persons i utrwalić dane. Główna treść to

{ “name”: “John” }

Nie zapomnij o ustawieniu odpowiedniego nagłówka content-type.

Teraz wchodząc na stronę https://radiant-island-50498.herokuapp.com/hi, zobaczymy listę wszystkich dodanych osób.

I to wszystko. W kilku prostych krokach dodaliśmy obsługę baz danych do naszej aplikacji chmurowej.

Logi

Heroku udostępnia logi oznaczone czasowo za pośrednictwem polecenia heroku logs.

2017-12-20T12:14:13.241355+00:00 app[web.1]: 2017-12-24 12:14:13.241 INFO 4 — [io-57474-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet’
2017-12-24T12:14:13.241454+00:00 app[web.1]: 2017-12-24 12:14:13.241 INFO 4 — [io-57474-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet’: initialization started
2017-12-20T12:14:13.364319+00:00 app[web.1]: 2017-12-24 12:14:13.363 INFO 4 — [io-57474-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet’: initialization completed in 122 ms
2017-12-20T12:14:14.266844+00:00 heroku[router]: at=info method=GET path=”/” host=radiant-island-50498.herokuapp.com request_id=55f80a5c-fe8d-4d6a-bed0-3b19e0f94a06 fwd=”185.125.

Format każdego wpisu to timestamp source[dyno]: message

Timestamp i message są łatwe do zrozumienia, za to source i dyno to elementy związane z heroku. Source określa, czy zapis z logów pochodzi z naszego dyno – jeśli tak, wówczas nazywa się „app”, zaś jeśli pochodzi z komponentu systemowego heroku (np. routera HTTP lub managera dyno), jego źródło to „heroku”.

Dyno to nazwa komponentu dyno, który napisał zapis z logów (web.1) lub, w przypadku komponentów systemowych heroku, będzie to „router” lub „manager”.

Oczywiście w logach możemy filtrować po konkretnym źródle, dyno lub obu na raz. Możemy użyć argumentów –source (lub -s) oraz –dyno (lub -d).

heroku logs –dyno web.1
heroku logs –source heroku

Oprócz tego możemy też łączyć przełączniki filtrowania używając –tail, aby otrzymać strumień filtrowanych danych wyjściowych w czasie rzeczywistym.

heroku logs –source app –tail

Polecenie logs domyślnie pobiera 100 wierszy z logów. Można również podać konkretną liczbę wierszy do pobrania (maksymalnie 1500 wierszy) za pomocą opcji –num (lub -n).

heroku logs -n 200

Metryki

Niestety, darmowe konta Heroku nie mają dostępu do funkcji gromadzenia metryk. Konieczny jest zakup co najmniej najtańszego planu. Po ulepszeniu konta uzyskamy dostęp do podstawowych metryk na pulpicie klienta dostępnym na stronie głównej heroku, tj. do zużycia pamięci, czasu reakcji i żądań na minutę. Dostępne są metryki z ostatnich 24 godzin. Oprócz tego można również skonfigurować podstawowe powiadomienia.

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami