Статьи

Heroku и Java — от новичка до новичка, часть 1

Недавно я слышал, что Heroku позволяет развертывать приложения Java в стеке Cedar. Не имея настоящей идеи программного обеспечения, я решил попробовать и просто настроить ЧТО-ТО на работу с Heroku.

Я влюблен в ReST (я все еще хочу изучить его и попрактиковаться в этом), поэтому я решил, что моим первым приложением будет простой привет мир с использованием Джерси (реализация JAX-RS). Поэтому я запустил проект на GitHub и начал настраивать Heroku CLI.

Настройка Heroku CLI

Heroku теперь легко настроить. Я помню, когда это требовало Ruby env и мой первый

встреча с Ruby была не такой уж замечательной (не было никакого инсталлятора, поэтому все было вручную — и я ленив), поэтому я отказался от Heroku тогда. Но теперь его установка очень проста — просто зайдите на Heroku Toolbelt и загрузите версию для вашей платформы. Теперь я установил его на Linux Mint и Windows 7, и он прекрасно работает.

Настройка проекта для Heroku

Мой проект называется recaps — это должна быть еще одна система управления билетами. Но это не имеет значения на данный момент. Самое главное, чтобы Heroku обнаружил, что наше приложение представляет собой приложение Java, должен присутствовать файл pom.xml. Это потому, что Heroku использует Maven 3 для создания приложений Java.

Таким образом, чтобы начать работу с Heroku, вам нужен простой файл pom.xml. В моем случае я добавил отдельный модуль для приложения, поэтому мой основной модуль выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.github.pbuda.recaps</groupId>
    <artifactId>recaps</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>
 
    <inceptionYear>2012</inceptionYear>
 
    <developers>
        <developer>
            <name>Piotr Buda</name>
            <email>[email protected]</email>
            <timezone>+1</timezone>
        </developer>
    </developers>
 
    <licenses>
        <license>
            <name>Apache License, version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
        </license>
    </licenses>
 
    <modules>
        <module>webmodule</module>
    </modules>
 
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
 
</project>

Тогда есть веб-модуль, просто для разделения проектов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="UTF-8"?>
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <modelVersion>4.0.0</modelVersion>
 
    <parent>
        <groupId>com.github.pbuda.recaps</groupId>
        <artifactId>recaps</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
 
    <artifactId>webmodule</artifactId>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-grizzly2</artifactId>
            <version>1.12</version>
        </dependency>
    </dependencies>
 
</project>

Моя первая попытка запустить мой пример проекта была сосредоточена на создании Джерси. После проверки документов я решил использовать HTTP-сервер Grizzly2 только потому, что его очень легко настроить. Я в основном вставил учебник по документации в основной класс. Были некоторые необходимые различия, потому что, например, порт сервера динамически назначается Heroku. Таким образом, после очень небольшого количества изменений полученный класс Main выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * Copyright 2012 Piotr Buda
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package com.github.pbuda.recaps;
 
import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import org.glassfish.grizzly.http.server.HttpServer;
 
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
 
/**
 * Created by IntelliJ IDEA.
 * User: pbu
 * Date: 28.02.12
 * Time: 21:01
 * To change this template use File | Settings | File Templates.
 */
public class Main {
    private static URI getBaseURI(String hostname, int port) {
        return UriBuilder.fromUri("http://0.0.0.0/").port(port).build();
    }
 
    protected static HttpServer startServer(URI uri) throws IOException {
        System.out.println("Starting grizzly...");
        ResourceConfig rc = new PackagesResourceConfig("com.github.pbuda.recaps");
        return GrizzlyServerFactory.createHttpServer(uri, rc);
    }
 
    public static void main(String[] args) throws IOException {
        URI uri = getBaseURI(System.getenv("HOSTNAME"), Integer.valueOf(System.getenv("PORT")));
        HttpServer httpServer = startServer(uri);
        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...",
                uri, uri));
        while(true) {
            System.in.read();
        }
    }
}

Это запускает сервер и регистрирует некоторые ресурсы вместе с ним.

Некоторые трюки гризли

Во-первых, метод GrizzlyServerFactory.createHttpServer принимает URI, который должен начинаться с имени схемы ведьмы — в данном случае http: //. Затем он должен указать имя хоста, которое я сначала настроил на имя приложения на herokuapp.com. Это не сработало, но Heroku хорошо сказал мне об этом: в журналах есть уведомление, что сервер должен привязаться к 0.0.0.0, поэтому я изменил URI на http://0.0.0.0.

Во-вторых, пример Джерси ожидал нажатия клавиши, чтобы завершить работу сервера. К сожалению, Heroku напечатал сообщение, которое затем каким-то образом было передано приложению, и сервер был остановлен. Чтобы решить эту проблему, я обернул System.in.read () в бесконечный цикл while.

Это, конечно, не лучшее решение, но оно сработало, или так казалось. Через несколько часов я проверил журналы приложения, и они сказали, что приложение пошло сверху вниз. Поэтому я решил переключиться с Grizzly на Jetty, но это не тема для этого поста.

Прежде чем отправить все это в Heroku, я также добавил Procfile:

1
web:    java -cp webmodule/target/classes:webmodule/target/dependency/* com.github.pbuda.recaps.Main

После нажатия на Heroku приложение было собрано и запущено, и запрос к http://growing-dawn-9158.herokuapp.com/helloworld дал некоторый вывод (в данном случае простое сообщение «Сообщение»). Работа хорошо сделана.

Ошибки, которые я сделал и чему научился

Во-первых, я забыл добавить плагин Maven Dependency, но решил его, прежде чем перейти к Heroku. Без этого я не смог бы добавить зависимости к classpath, что, в свою очередь, привело к исключениям ClassNotFound. Сначала мне это не приходило в голову, это требовалось, но потом я посмотрел на пример Heroku и легко его исправил.

Во-вторых, я не знал, что тайм-аут веб-динамов. После успешного развертывания я был уверен, что приложение работает, но из-за тайм-аута в журналах сообщалось, что приложение отключилось. Поскольку я не знал о том, что истекло время ожидания веб-динамов, я подозревал, что Grizzly просто каким-то образом прервали, поэтому я решил переехать в Jetty. Но это случилось и с реализацией Jetty, поэтому я начал копать и нашел нужную информацию.

Резюме

Я думаю, что Heroku великолепен. Это бесплатный хостинг на Java, что-то, что НЕ популярно, и все же они сделали это, и это работает довольно хорошо. Однажды я попробовал Google App Engine, но опыт был не очень хорошим (учтите, это было довольно давно), поэтому я решил дать Heroku шанс, и потому что на самом деле было довольно просто настроить приложение, я думаю, что « Я придерживаюсь его некоторое время и играю с платформой — посмотрите на все эти плагины