Статьи

Микросервисы для разработчиков Java: ландшафт Java / JVM

1. Введение

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

Хотя существует достаточно много старых, чтобы помнить эру SOAP , многие из фреймворков, которые мы собираемся обсудить в ближайшее время, довольно молоды и часто довольно самоуверенны. Выбор того, какой из них вам подходит, вероятно, является самым важным решением, которое вы собираетесь принять на раннем этапе. Помимо спецификации JAX-RS (и более общей спецификации сервлета ), не существует общеотраслевых стандартов, гарантирующих взаимодействие между различными платформами и библиотеками на платформе JVM, поэтому выполняйте вызов с умом.

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

Тем не менее, пара ценных ресурсов может оказать большую помощь. Репозиторий Awesome Microservices — это потрясающий список принципов и технологий, связанных с архитектурой микросервисов . В том же духе тесты веб-платформы TechEmpower предоставляют ряд интересных и полезных сведений о производительности нескольких платформ и платформ веб-приложений, а не только JVM.

2. Пребывание ОТДЫХА

Наиболее загруженное пространство занимают фреймворки и библиотеки, которые продвигают архитектурный стиль REST по протоколу HTTP . Почти все номинанты в этой категории — это очень зрелые и хорошо зарекомендовавшие себя бренды, которые годами используются в производстве.

2.1. JAX-RS: RESTful Java на предприятии

Спецификация JAX-RS , также известная как JSR-370 (и ранее описанная в JSR-339 и JSR-311 ), определяет набор API-интерфейсов Java для разработки веб-служб, созданных в соответствии с архитектурным стилем REST . Это довольно успешное усилие с множеством реализаций, доступных для выбора, и, возможно, предпочтение номер один в корпоративном мире.

API -интерфейсы JAX-RS управляются аннотациями Java и, как правило, могут плавно переноситься из одной среды в другую. Кроме того, существует тесная интеграция с другими спецификациями платформы Java, такими как контексты и внедрение зависимостей для Java ( JSR-365 ), проверка бинов ( JSR-380 ), Java API для обработки JSON ( JSR-374 ) и многие другие.

Возвращаясь к мыслимым веб-API управления библиотеками, о которых мы говорили в предыдущей части руководства , типичная (но очень упрощенная) реализация веб-службы JAX-RS может выглядеть следующим образом:

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
@Path("/library")
public class LibraryRestService {
    @Inject private LibraryService libraryService;
 
    @GET
    @Path("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }
     
    @POST
    @Path("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Response addBook(@Context UriInfo uriInfo, Book payload) {
        final Book book = libraryService.addBook(payload);
         
        return Response
            .created(
                uriInfo
                    .getRequestUriBuilder()
                    .path(book.getIsbn())
                    .build())
            .entity(book)
            .build();    }
     
    @GET
    @Path("/books/{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    public Book findBook(@PathParam("isbn") String isbn) {
        return libraryService
            .findBook(isbn)
            .orElseThrow(() -> new NotFoundException("No book found for ISBN: " + isbn));
    }
}

Он должен работать на любой платформе, которая полностью соответствует последней спецификации JAX-RS 2.1 ( JSR-370 ), поэтому давайте возьмем ее оттуда.

2.2. Apache CXF

Apache CXF , платформа с открытым исходным кодом, отмечает в этом году свое 10 летие! Apache CXF помогает создавать и разрабатывать сервисы с использованием API-интерфейсов внешнего интерфейса, таких как JAX-WS и JAX-RS , которые могут работать с различными протоколами, такими как SOAP , REST , SSE (даже CORBA ), и работать с различными транспортными средствами, такими как HTTP. , JMS или JBI .

Что делает Apache CXF идеальным выбором для микросервисов, так это тот факт, что он имеет выдающуюся интеграцию со многими другими проектами, в частности: OpenAPI для разработки по контракту, Brave / OpenTracing / Apache HTrace для распределенной трассировки, JOSE / OAuth2 / OpenID Connect для безопасности.

2,3. Apache Meecrowave

Meecrowave — это очень легкая и простая в использовании платформа микросервисов , построенная исключительно на основе других великих проектов Apache : Apache OpenWebBeans ( CDI 2.0 ), Apache CXF ( JAX-RS 2.1 ) и Apache Johnzon ( JSON-P ). Это значительно сокращает время разработки, поскольку все необходимые компоненты уже соединены вместе.

2,4. Resteasy

RESTEasy от RedHat / JBoss — еще одна полностью сертифицированная и портативная реализация спецификации JAX-RS 2.1 . Одним из преимуществ инфраструктуры RESTEasy является более тесная интеграция с сервером приложений WildFly , если это может быть важно в вашем контексте.

2.5. Джерси

Jersey — это платформа с открытым исходным кодом, обеспечивающая качество продукции для разработки веб-сервисов RESTful на Java. Фактически, он служит эталонной реализацией JAX-RS ( JSR-370 , JSR-339 и JSR-311 ). Подобно другим средам, Jersey выходит далеко за рамки простой реализации JAX-RS и предоставляет дополнительные функции, расширения и утилиты для дальнейшего упрощения разработки веб-API и клиентов RESTful .

2.6. Dropwizard

Dropwizard — еще одна отличная среда Java для разработки веб-сервисов и API-интерфейсов RESTful с акцентом на удобство работы и высокую производительность. Он построен на основе структуры Джерси и объединяет лучшие в своем роде библиотеки из всей экосистемы Java. В этом смысле он довольно самоуверенный, но в то же время известен как простой, стабильный , зрелый и легкий .

Dropwizard имеет встроенную поддержку сложной конфигурации, метрик приложений, ведения журналов, рабочих инструментов и многого другого, что позволяет вам и вашей команде в кратчайшие сроки доставить качественный веб-сервис. https://www.dropwizard.io/1.3.5/docs

Стоит отметить, что Dropwizard действительно установил базовый уровень инструментария и мониторинга для современных Java-приложений (возможно, вы слышали о библиотеке Metrics, рожденной из него) и является действительно хорошим выбором для создания микросервисов .

2,7. Eclipse Microprofile: мышление в микросервисах с самого начала

Говоря о микросервисах во вселенной Java, нельзя не упомянуть недавнюю инициативу Eclipse Foundation , известную как MicroProfile .

MicroProfile — это базовое определение платформы, которое оптимизирует Enterprise Java для архитектуры микросервисов и обеспечивает переносимость приложений в нескольких средах выполнения MicroProfile. Первоначально запланированный базовый уровень — JAX-RS + CDI + JSON-P с намерением сообщества играть активную роль в определении и дорожной карте MicroProfile. https://microprofile.io/faq

Основная цель MicroProfile — ускорить темпы инноваций в корпоративной Java-среде, которая традиционно страдает от очень медленных процессов. Это, безусловно, стоит следить.

2,8. Spring WebMvc / WebFlux

Давным-давно Spring Framework , изначально реализовавший принцип проектирования с инверсией управления ( IoC ), потряс мир корпоративной Java, наполненный чудовищными инфраструктурами и спецификациями. Он предоставил простой и понятный способ использования возможностей внедрения зависимостей в приложениях Java.

IoC и внедрение зависимостей по-прежнему лежат в основе Spring Framework, но под его эгидой находится так много проектов, что в настоящее время он больше похож на платформу, чем на что-либо еще. Что касается этого раздела, то нас интересует Spring Web MVC , оригинальная веб-инфраструктура, основанная на Servlet API и широко используемая для традиционных веб-сервисов RESTful , таких как, например, наши API-интерфейсы управления библиотеками.

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
@RestController
@RequestMapping("/library")
public class LibraryController {
    @Autowired private LibraryService libraryService;
     
    @RequestMapping(path = "/books/{isbn}", method = GET, produces = APPLICATION_JSON_VALUE)
    public ResponseEntity<Book> findBook(@PathVariable String isbn) {
        return libraryService
            .findBook(isbn)
            .map(ResponseEntity::ok)
            .orElseGet(ResponseEntity.notFound()::build);
    }
     
    @RequestMapping(path = "/books", method = GET, produces = APPLICATION_JSON_VALUE)
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }
 
    @RequestMapping(path = "/books", method = POST, consumes = APPLICATION_JSON_VALUE)
    public ResponseEntity<Book> addBook(@RequestBody Book payload) {
        final Book book = libraryService.addBook(payload);
         
        return ResponseEntity
            .created(linkTo(methodOn(LibraryController.class).findBook(book.getIsbn())).toUri())
            .body(book);
    }
}

Было бы несправедливо не упомянуть относительно новый проект Spring WebFlux , аналог Spring Web MVC , построенный поверх реактивного стека и неблокирующего ввода-вывода .

Инновационная, продуктивная и чрезвычайно успешная среда Spring Framework является выбором номер один для разработчиков Java в наши дни, особенно в отношении микросервисной архитектуры .

2.9. Spark Java

Spark , также часто называемый Spark-Java для устранения путаницы с гиперпопулярной средой обработки данных с одноименным названием, представляет собой микроуровень для создания веб-приложений на Java с минимальными усилиями. Он в значительной степени полагается на свой выразительный и простой DSL , разработанный с использованием возможностей лямбда-выражений Java 8 . Это действительно приводит к довольно компактному и чистому коду. Например, вот краткий обзор нашего определения API управления библиотекой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
path("/library", () -> {
    get("/books",
        (req, res) -> libraryService.getBooks(),
        json()
    );
             
    get("/books/:isbn",
        (req, res) -> libraryService
            .findBook(req.params(":isbn"))
            .orElseGet(() -> {
                res.status(404);
                return null;
            }),
        json()
    );
             
    post("/books",
        (req, res) -> libraryService
            .addBook(JsonbBuilder.create().fromJson(req.body(), Book.class)),
        json()
    );
});

Помимо Java, Spark-Java имеет первоклассную поддержку Kotlin и, хотя он в основном используется для создания REST API , он интегрируется с множеством шаблонизаторов.

2.10. Restlet

Фреймворк Restlet призван помочь разработчикам Java создавать лучшие веб-API, соответствующие архитектурному стилю REST . Он предоставляет довольно мощные возможности маршрутизации и фильтрации, а также предлагает многочисленные расширения. У него совершенно уникальный способ структурировать и связывать вещи вместе.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BookResource extends ServerResource {
    @Inject private LibraryService libraryService;
     
    @Get
    public Book findBook() {
        final String isbn = (String) getRequest().getAttributes().get("isbn");
        return libraryService.findBook(isbn).orElse(null);
    }
}
 
public class BooksResource extends ServerResource  {
    @Inject private LibraryService libraryService;
     
    @Get
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }
     
    @Post("json")
    public Representation addBook(Book payload) throws IOException {
        return toRepresentation(libraryService.addBook(payload));
    }
}

И вот привязки между ресурсами и их URI , в основном типичным маршрутизатором.

1
2
3
final Router router = new Router(getContext());
router.attach("/library/books/{isbn}", BookResource.class);
router.attach("/library/books", BooksResource.class);

Интересно, что Restlet — одна из немногих платформ с открытым исходным кодом, которая выросла из простой библиотеки в полноценную платформу разработки RESTful API .

2,11. Vert.x

Проект Vert.x от Eclipse Foundation — это набор инструментов с открытым исходным кодом для создания реактивных приложений на платформе JVM. Он следует за реактивной парадигмой и разработан с нуля, чтобы быть управляемым событиями и неблокирующим.

У этого есть тонны различных компонентов. Одним из них является Vert.x-Web , предназначенный для написания сложных современных веб-приложений и микросервисов на основе HTTP . Фрагмент ниже демонстрирует наш веб-API управления библиотекой, реализованный поверх Vert.x-Web .

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
final LibraryService libraryService = ...;
 
final Vertx vertx = Vertx.vertx();
final HttpServer server = vertx.createHttpServer();
final Router router = Router.router(vertx);
         
router
    .get("/library/books")
    .produces("application/json")
    .handler(context ->
        context
            .response()
            .putHeader("Content-Type", "application/json")
            .end(Json.encodePrettily(libraryService.getBooks()))
    );
             
router
    .get("/library/books/:isbn")
    .produces("application/json")
    .handler(context ->
        libraryService
            .findBook(context.request().getParam("isbn"))
            .ifPresentOrElse(
                book -> context
                        .response()
                        .putHeader("Content-Type", "application/json")
                        .end(Json.encodePrettily(book)),
                () -> context
                        .response()
                        .setStatusCode(204)
                        .putHeader("Content-Type", "application/json")
                        .end()
            )
    );
         
router
    .post("/library/books")
    .consumes("application/json")
    .produces("application/json")
    .handler(BodyHandler.create())
    .handler(context -> {
        final Book book = libraryService
            .addBook(context.getBodyAsJson().mapTo(Book.class));
                 
        context
            .response()
            .putHeader("Content-Type", "application/json")
            .end(Json.encodePrettily(book));
    });
         
server.requestHandler(router::accept);
server.listen(4567);

Некоторые из примечательных характеристик Vert.x — это высокая производительность и модульная конструкция. Более того, он довольно легкий, очень хорошо масштабируется и изначально поддерживает Java, JavaScript , Groovy , Ruby , Ceylon , Scala и Kotlin .

2.12. Play Framework

Play — это высокоскоростной, высокопроизводительный веб-фреймворк для Java и Scala . Он основан на облегченной архитектуре без поддержки состояния и для веб-приложений и построен на основе Akka Toolkit . Хотя Play — это полноценный фреймворк с исключительно мощным движком шаблонов, он очень хорошо подходит и для разработки веб-сервисов RESTful . Наши веб-API для управления библиотекой могут быть легко спроектированы в Play .

1
2
3
GET    /library/books           controllers.LibraryController.list
GET    /library/books/:isbn     controllers.LibraryController.show(isbn: String)
POST   /library/books           controllers.LibraryController.add
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
public class LibraryController extends Controller {
    private LibraryService libraryService;
 
    @Inject
    public LibraryController(LibraryService libraryService) {
        this.libraryService = libraryService;
    }
     
     
    public Result list() {
        return ok(Json.toJson(libraryService.getBooks()));
    }
 
    public Result show(String isbn) {
        return  libraryService
            .findBook(isbn)
            .map(resource -> ok(Json.toJson(resource)))
            .orElseGet(() -> notFound());
    }
     
    public Result add() {
        JsonNode json = request().body().asJson();
        final Book book = Json.fromJson(json, Book.class);
        return created(Json.toJson(libraryService.addBook(book)));
    }
}

Возможность Play обслуживать как конечные точки бэкэнда ( RESTful ), так и внешнего интерфейса (используя, например, Angular , React , Vue.js ), другими словами, полный стек, может быть привлекательным предложением в отношении реализации микросервисов с использованием единой платформы ( хотя такие решения должны приниматься с большой осторожностью).

2.13. Акка HTTP

Akka HTTP , часть удивительного семейства Akka Toolkit , предоставляет полный HTTP- стек на стороне сервера и на стороне клиента, реализованный поверх актерской модели . Это не полноценный веб-фреймворк (например, Play ), а созданный специально для управления HTTP- сервисами. Как и в других средах, Akka HTTP имеет собственный DSL для элегантного определения конечных точек веб-API RESTful . Лучший способ попробовать это — создать наши определения API управления библиотекой.

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
public class LibraryRoutes extends AllDirectives {
    private final LibraryService libraryService;
 
    // ...   
     
    public Route routes() {
        return route(
            pathPrefix("library", () ->
                pathPrefix("books", () ->
                    route(
                        pathEndOrSingleSlash(() ->
                            route(
                                get(() ->
                                    complete(StatusCodes.OK, libraryService.getBooks(), Jackson.marshaller())
                                ),
                                post(() ->
                                    entity(
                                        Jackson.unmarshaller(Book.class),
                                        payload -> complete(StatusCodes.OK, libraryService.addBook(payload), Jackson.marshaller())
                                    )
                                )
                            )
                        ),
                        path(PathMatchers.segment(), isbn ->
                            route(
                                get(() ->
                                    libraryService
                                        .findBook(isbn)
                                        .map(book -> (Route)complete(StatusCodes.OK, book, Jackson.marshaller()))
                                        .orElseGet(() -> complete(StatusCodes.NOT_FOUND))
                                )
                            )
                        )
                    )
                )
            )
        );
    }
}

Akka HTTP имеет первоклассную поддержку Java и Scala и является отличным выбором для построения веб-сервисов RESTful и масштабируемой архитектуры микросервисов в целом.

2,14. Micronaut

Micronaut — это современная полнофункциональная инфраструктура на основе JVM для создания модульных, легко тестируемых приложений микросервиса. Это действительно полиглот (в смысле JVM) и предлагает поддержку Java, Groovy и Kotlin из коробки.

В центре внимания Micronaut — первоклассная поддержка парадигмы реактивного программирования и внедрения зависимостей во время компиляции. Вот скелет нашей декларации веб-API управления библиотекой в Micronaut .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller("/library")
public class LibraryController {
    @Inject private LibraryService libraryService;
     
    @Get("/books/{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    public Optional<Book> findBook(String isbn) {
        return libraryService.findBook(isbn);
    }
     
    @Get("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Observable<Book> getAll() {
        return Observable.fromIterable(libraryService.getBooks());
    }
     
    @Post("/books")
    @Consumes(MediaType.APPLICATION_JSON)
    public HttpResponse<Book> addBook(Book payload) {
        return HttpResponse.created(libraryService.addBook(payload));
    }
}

По сравнению с другими, Micronaut — очень молодая, но многообещающая структура, ориентированная на современное программирование. Это новый старт без багажа, накопленного за эти годы.

3. GraphQL, новая сила

Популярность GraphQL медленно, но неуклонно растет. Он оказался незаменимым инструментом для решения широкого спектра проблем, хотя для разработчиков Java не так много вариантов. В этом разделе мы будем использовать ту же схему GraphQL, которую мы обсуждали в предыдущей части руководства , и повторяем ниже для удобства.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
schema {
  query: Query
  mutation: Mutation
}
 
type Book {
  isbn: ID!
  title: String!
  year: Int
}
 
type Query {
  books: [Book]
  book(isbn: ID!): Book
}
 
type Mutation {
  addBook(isbn: ID!, title: String!, year: Int): Book
  updateBook(isbn: ID!, title: String, year: Int): Book
  removeBook(isbn: ID!): Boolean
}

3.1. Сангрия

Sangria — это реализация GraphQL в Scala . Это потрясающая платформа с активным сообществом и бесшовной интеграцией с Akka HTTP и / или Play Framework . Хотя в настоящее время он не предоставляет API-интерфейсы, дружественные к Java, стоит упомянуть об этом. Для этого требуется немного другой подход, например, путем определения схемы по определителям в коде.

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
object SchemaDefinition {
  val BookType = ObjectType(
    "Book", "A book.", fields[LibraryService, Book](
      Field("isbn", StringType, Some("The book's ISBN."), resolve = _.value.isbn),
      Field("title", StringType, Some("The book's title."), resolve = _.value.title),
      Field("year", IntType, Some("The book's year."), resolve = _.value.year)
    ))
     
  val ISBN = Argument("isbn", StringType, description = "ISBN")
  val Title = Argument("title", StringType, description = "Book's title")
  val Year = Argument("year", IntType, description = "Book's year")
 
  val Query = ObjectType(
    "Query", fields[LibraryService, Unit](
      Field("book", OptionType(BookType),
        arguments = ISBN :: Nil,
        resolve = ctx ⇒ ctx.ctx.findBook(ctx arg ISBN)),
      Field("books", ListType(BookType),
        resolve = ctx ⇒  ctx.ctx.getBooks())
    ))
 
  val Mutation = ObjectType(
    "Mutation", fields[LibraryService, Unit](
      Field("addBook", BookType,
        arguments = ISBN :: Title :: Year :: Nil,
        resolve = ctx ⇒ ctx.ctx.addBook(Book(ctx arg ISBN, ctx arg Title, ctx arg Year))),
      Field("updateBook", BookType,
        arguments = ISBN :: Title :: Year :: Nil,
        resolve = ctx ⇒ ctx.ctx.updateBook(ctx arg ISBN, Book(ctx arg ISBN, ctx arg Title, ctx arg Year))),
      Field("removeBook", BooleanType,
        arguments = ISBN :: Nil,
        resolve = ctx ⇒ ctx.ctx.removeBook(ctx arg ISBN))
    ))
     
   val LibrarySchema = Schema(Query, Some(Mutation))
}

Хотя есть некоторые плюсы и минусы в разработке схемы с первым кодом, это может быть хорошим решением в некоторых реализациях микросервисной архитектуры .

3.2. graphql-ява

Легко догадаться, graphql-java — это реализация GraphQL в Java. Он имеет довольно хорошую интеграцию со Spring Framework, а также с любым другим Servlet- совместимым каркасом или контейнером. В случае такой простой схемы GraphQL, как наша, реализация — это вопрос определения распознавателей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Query implements GraphQLQueryResolver {
    private final LibraryService libraryService;
     
    public Query(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
     
    public Optional<Book> book(String isbn) {
        return libraryService.findBook(isbn);
    }
     
    public List<Book> books() {
        return new ArrayList<>(libraryService.getBooks());
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class Mutation implements GraphQLMutationResolver {
    private final LibraryService libraryService;
     
    public Mutation(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
         
    public Book addBook(String isbn, String title, int year) {
        return libraryService.addBook(new Book(isbn, title, year));
    }
     
    public Book updateBook(String isbn, String title, int year) {
        return libraryService.updateBook(isbn, new Book(isbn, title, year));
    }
     
    public boolean removeBook(String isbn) {
        return libraryService.removeBook(isbn);
    }
}

И это буквально это. Если вы планируете использовать GraphQL в некоторых или во всех сервисах в вашей микросервисной архитектуре , то GraphQL-Java может стать надежной основой для построения.

4. Стиль RPC

По сравнению с RESTful или GraphQL , эффективность RPC- разговоров, особенно для обмена данными между сервисами, действительно трудно превзойти. Основную роль играет gRPC- инфраструктура от Google, но это не единственный игрок.

Как мы увидим, многие системы RPC построены на идее определения сервисного контракта: в Java это, как правило, аннотированное определение интерфейса. На стороне сервера реализует этот интерфейс и предоставляет его по проводам, тогда как на стороне клиента этот интерфейс используется для получения прокси или заглушки.

4.1. Java-КПГР

Мы кратко рассмотрели общие концепции gRPC в предыдущей части руководства , но в этом разделе мы поговорим о его реализации на Java — java-grpc . Поскольку в основном все генерируется для вас из определения сервиса Protocol Buffers , разработчикам остается только предоставить соответствующие реализации сервиса. Вот gRPC- версия нашей службы управления библиотекой.

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
static class LibraryImpl extends LibraryGrpc.LibraryImplBase {
    private final LibraryService libraryService;
         
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
         
    @Override
    public void addBook(AddBookRequest request, StreamObserver<Book> responseObserver) {
        final Book book = Book.newBuilder()
            .setIsbn(request.getIsbn())
            .setTitle(request.getTitle())
            .setYear(request.getYear())
            .build();
 
        responseObserver.onNext(libraryService.addBook(book));
        responseObserver.onCompleted();
    }
         
    @Override
    public void getBooks(Filter request, StreamObserver<BookList> responseObserver) {
        final BookList bookList = BookList
            .newBuilder()
            .addAllBooks(libraryService.getBooks())
            .build();
        responseObserver.onNext(bookList);
        responseObserver.onCompleted();
    }
         
    @Override
    public void updateBook(UpdateBookRequest request, StreamObserver<Book> responseObserver) {
        responseObserver.onNext(libraryService.updateBook(request.getIsbn(), request.getBook()));
        responseObserver.onCompleted();
    }
         
    @Override
    public void removeBook(RemoveBookRequest request, StreamObserver<Empty> responseObserver) {
        libraryService.removeBook(request.getIsbn());
        responseObserver.onCompleted();
    }
}

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

4.2. Реактивная gRPC

В последние годы реактивное программирование неуклонно выходит на мейнстрим. Reactive gRPC — это набор библиотек для расширения gRPC для работы с реализациями Reactive Streams . В двух словах, он просто генерирует альтернативные привязки gRPC , относительно библиотеки по вашему выбору ( RxJava 2 и Spring Reactor на данный момент), все остальное остается в значительной степени неизменным. Чтобы доказать это, давайте взглянем на реализацию LibraryImpl с использованием API Reactive Streams .

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
static class LibraryImpl extends RxLibraryGrpc.LibraryImplBase {
    private final LibraryService libraryService;
         
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
         
    @Override
    public Single<Book> addBook(Single<AddBookRequest> request) {
        return request
            .map(r ->
                Book
                    .newBuilder()
                    .setIsbn(r.getIsbn())
                    .setTitle(r.getTitle())
                    .setYear(r.getYear())
                    .build())
            .map(libraryService::addBook);
    }
         
    @Override
    public Single<BookList> getBooks(Single<Filter> request) {
        return request
            .map(r ->
                BookList
                    .newBuilder()
                    .addAllBooks(libraryService.getBooks())
                    .build());
    }
         
    @Override
    public Single<Book> updateBook(Single<UpdateBookRequest> request) {
        return request
            .map(r -> libraryService.updateBook(r.getIsbn(), r.getBook()));
    }
         
    @Override
    public Single<Empty> removeBook(Single<RemoveBookRequest> request) {
        return request
            .map(r -> {
                libraryService.removeBook(r.getIsbn());
                return Empty.newBuilder().build();
            });
    }
}

Справедливости ради, Reactive gRPC — это не полноценная реализация gRPC, а отличное дополнение к java-grpc .

4,3. Акка ГРПЦ

Еще один замечательный инструмент из пакета Akka Toolkit , Akka gRPC, обеспечивает поддержку для построения потоковых серверов и клиентов gRPC поверх Akka StreamsAkka Toolkit в целом). Реализация на основе Java опирается на CompletionStage из стандартной библиотеки и довольно проста в использовании.

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
public class LibraryImpl implements Library {
    private final LibraryService libraryService;
     
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
     
    @Override
    public CompletionStage<Book> addBook(AddBookRequest in) {
        final Book book = Book
            .newBuilder()
            .setIsbn(in.getIsbn())
            .setTitle(in.getTitle())
            .setYear(in.getYear())
            .build();
         
        return CompletableFuture.completedFuture(libraryService.addBook(book));
    }
 
    @Override
    public CompletionStage<BookList> getBooks(Filter in) {
        return CompletableFuture.completedFuture(
            BookList
                .newBuilder()
                .addAllBooks(libraryService.getBooks())
                .build());
    }
     
    @Override
    public CompletionStage<Book> updateBook(UpdateBookRequest in) {
        return CompletableFuture.completedFuture(libraryService.updateBook(in.getIsbn(), in.getBook()));
    }
     
    @Override
    public CompletionStage<Empty> removeBook(RemoveBookRequest in) {
        libraryService.removeBook(in.getIsbn());
        return CompletableFuture.completedFuture(Empty.newBuilder().build());
    }
}

Akka gRPC является новым участником Akka Toolkit и в настоящее время находится в режиме предварительного просмотра. Он может быть использован уже сегодня, но, безусловно, ожидать некоторые изменения в будущем.

4.4. Apache Dubbo

Apache Dubbo , в настоящее время находящийся в стадии разработки в рамках Apache Software Foundation, но первоначально разработанный в Alibaba , представляет собой высокопроизводительную среду RPC на основе Java с открытым исходным кодом. Наш библиотечный сервис может быть запущен всего за несколько строк кода.

1
2
3
4
5
6
final ServiceConfig serviceConfig = new ServiceConfig();
serviceConfig.setApplication(new ApplicationConfig("library-provider"));
serviceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
serviceConfig.setInterface(LibraryService.class);
serviceConfig.setRef(new LibraryServiceImpl());
serviceConfig.export();

Он ориентирован исключительно на Java, поэтому, если вы хотите создать микросервисную архитектуру полиглота, Apache Dubbo может помочь вам не изначально, а с помощью дополнительных интеграций, таких как, например, RPC через REST или RPC через HTTP .

4,5. Финатра и Финагл

Finagle , библиотека RPC , и Finatra , сервисная структура, построенная на его основе, родились в Твиттере и вскоре были открыты.

Finagle — это расширяемая RPC-система для JVM, используемая для создания серверов с высокой степенью параллелизма. Finagle реализует единые клиентские и серверные API для нескольких протоколов и рассчитан на высокую производительность и параллелизм. https://github.com/twitter/finagle

Finatra — это облегченный фреймворк для создания быстрых, тестируемых, scala-приложений поверх TwitterServer и Finagle . https://github.com/twitter/finatra

Они оба основаны на Scala и активно используются в производстве. Finagle была одной из первых библиотек, использовавших Apache Thrift для генерации сервисов и двоичной сериализации. Давайте возьмем IDL библиотечного сервиса из предыдущей части руководства и реализуем его как сервис Finatra (как обычно, большая часть кода скаффолдинга создается от нашего имени).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
@Singleton
class LibraryController @Inject()(libraryService: LibraryService) extends Controller with Library.BaseServiceIface {
  override val addBook = handle(AddBook) { args: AddBook.Args =>
    Future.value(libraryService.addBook(args.book))
  }
   
  override val getBooks = handle(GetBooks) { args: GetBooks.Args =>
    Future.value(libraryService.getBooks())
  }
   
  override val removeBook = handle(RemoveBook) { args: RemoveBook.Args =>
    Future.value(libraryService.removeBook(args.isbn))
  }
   
  override val updateBook = handle(UpdateBook) { args: UpdateBook.Args =>
    Future.value(libraryService.updateBook(args.isbn, args.book))
  }
}

Одной из отличительных особенностей Finagle является встроенная поддержка распределенной трассировки и статистики для мониторинга и диагностики, бесценная информация для работы микросервисов в производстве.

5. Сообщения и события

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

5.1. Axon Framework

Axon — это легкая Java-инфраструктура с открытым исходным кодом для создания масштабируемых, расширяемых событийно-управляемых приложений. Это один из пионеров практического применения принципов надежной архитектуры доменного проектирования ( DDD ) и разделения ответственности и запросов ( CQRS ).

5.2. ЛАГ

Lagom — это самоуверенная среда с открытым исходным кодом для создания реактивных микросервисных систем на Java или Scala . Лагом стоит на плечах гигантов, Akka Toolkit и Play! Framework , две проверенные технологии, которые испытаны в бою во многих самых требовательных приложениях. Он разработан в соответствии с принципами доменного управления ( DDD ), источников событий и разделения ответственности и запросов и запросов ( CQRS ) и настоятельно рекомендует использовать эти шаблоны.

5.3. Akka

Akka — это инструментарий для создания параллельных, распределенных и устойчивых приложений, управляемых сообщениями, для Java и Scala . Как мы уже видели, Akka служит основой для нескольких других высокоуровневых фреймворков (таких как, например, Akka HTTP и Play Framework ), однако сам по себе это отличный способ построить микросервисы, которые можно разложить на независимых участников . Актер управления библиотекой является примером такого решения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LibraryActor extends AbstractActor {
    private final LibraryService libraryService;
     
    public LibraryActor(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
     
    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .match(GetBooks.class, e ->
                getSender().tell(libraryService.getBooks(), self()))
            .match(AddBook.class, e -> {
                final Book book = new Book(e.getIsbn(), e.getTitle());
                getSender().tell(libraryService.addBook(book), self());
            })
            .match(FindBook.class, e ->
                getSender().tell(libraryService.findBook(e.getIsbn()), self()))
            .matchAny(o -> log.info("received unknown message"))
            .build();
    }
}

Связь между участниками Akka очень эффективна и не основана на протоколе HTTP (одним из предпочтительных транспортов является Aeron , о котором мы кратко говорили в предыдущей части руководства ). Модуль Akka Persistence позволяет субъектам с сохранением состояния сохранять свое внутреннее состояние и восстанавливать его позже. Есть некоторые сложности, с которыми вы можете столкнуться при реализации микросервисной архитектуры с использованием Akka, но в целом это надежный и надежный выбор.

5.4. ZeroMQ

ZeroMQ не является типичным промежуточным программным обеспечением для обмена сообщениями. Это абсолютно без брокера (изначально нулевой префикс в ZeroMQ подразумевался как «нулевой брокер»).

ZeroMQ (также известный как ØMQ, 0MQ или zmq) выглядит как встраиваемая сетевая библиотека, но действует как среда параллелизма. Это дает вам сокеты, которые переносят атомарные сообщения через различные транспорты, такие как внутрипроцессный, межпроцессный, TCP и многоадресная передача. Вы можете соединить сокеты N-to-N с такими шаблонами, как разветвление, pub-sub, распределение задач и запрос-ответ. Это достаточно быстро, чтобы быть тканью для кластерных продуктов. Его модель асинхронного ввода-вывода предоставляет масштабируемые многоядерные приложения, созданные как задачи асинхронной обработки сообщений. Он имеет множество языковых API и работает в большинстве операционных систем. http://zguide.zeromq.org/page:all#ZeroMQ-in-a-Hundred-Words

Он широко используется в приложениях и службах, где необходимо добиться низкой задержки. Это те места в микросервисной архитектуре, где ZeroMQ может оказать большую помощь.

5.5. Апач Кафка

Apache Kafka , основанная на идее построения распределенной системы журналов , расширилась далеко за ее пределы в распределенную потоковую платформу. Он горизонтально масштабируемый, отказоустойчивый, быстрый злой и способен переваривать безумно массивные объемы сообщений (или событий).

Гиперпопулярность Apache Kafka (и по уважительным причинам) привела к тому, что многие другие брокеры сообщений стали забытыми и незаслуженно исчезают. Крайне маловероятно, что Apache Kafka не сможет удовлетворить спрос вашей реализации микросервисной архитектуры , но чаще всего может быть более простая альтернатива.

5.6. RabbitMQ и Apache Qpid

RabbitMQ и Apache Qpid являются классическими примерами брокеров сообщений, использующих протокол AMQP . Нечего и говорить, кроме того, что RabbitMQ наиболее известен тем, что он написан на Erlang . Оба являются открытым исходным кодом и хорошим выбором, чтобы служить основой обмена сообщениями между вашими микросервисами .

5,7. Apache ActiveMQ

Apache ActiveMQ — одно из самых старых и мощных решений для обмена сообщениями с открытым исходным кодом. Он поддерживает широкий спектр протоколов (включая AMQP , STOMP , MQTT ) и в то же время полностью совместим со спецификацией Java Messaging Service ( JMS 1.1 ).

Интересно, что под зонтиком Apache ActiveMQ скрыто довольно много разных брокеров сообщений. Одной из них является ActiveMQ Artemis, целью которой является многопротокольная, встраиваемая, высокопроизводительная, кластерная, асинхронная система обмена сообщениями.

Другим примером является ActiveMQ Apollo , разработчик для разработки более быстрого, надежного и простого в обслуживании брокера обмена сообщениями. Он был построен на основе оригинального Apache ActiveMQ с радикально отличающейся архитектурой потоков и диспетчеризации сообщений. Хотя очень многообещающе, кажется, заброшено.

Весьма вероятно, что Apache ActiveMQ обладает всеми необходимыми функциями, чтобы ваши микросервисы могли надежно передавать сообщения. Кроме того, поддержка JMS может быть важным преимуществом в корпоративном мире.

5,8. Apache RocketMQ

Apache RocketMQ — это платформа с распределенным обменом сообщениями и потоковыми данными с открытым исходным кодом (еще один вклад Alibaba ). Он нацелен на чрезвычайно низкую задержку, высокую доступность и большую емкость сообщений.

5.9. NATS

NATS — это простая, высокопроизводительная система обмена сообщениями с открытым исходным кодом для облачных приложений, IoT-сообщений и микросервисных архитектур . Он реализует легко масштабируемую и элегантную модель распространения сообщений публикации-подписки.

5.10. NSQ

NSQ — это платформа распределенных сообщений в реальном времени с открытым исходным кодом, предназначенная для работы в масштабе и обработки миллиардов сообщений в день. Он также следует модели без посредников и поэтому не имеет единой точки отказа, поддерживает высокую доступность и горизонтальную масштабируемость.

6. Получи все

Существует определенный класс библиотек, которые имеют единственное намерение объединить множество различных каналов связи. В совокупности они известны как интеграционные структуры и в значительной степени вдохновлены потрясающей книгой Enterprise Integration Patters .

6.1. Apache Camel

Apache Camel — это мощная и зрелая интегрированная среда с открытым исходным кодом. Это удивительно небольшая библиотека с минимальным набором зависимостей и легко встраиваемая в любое приложение Java. Он абстрагирует виды транспорта, используемые за лаконичным API-уровнем, который позволяет взаимодействовать с более чем 300 компонентами, предоставляемыми из коробки.

6.2. Весенняя интеграция

Spring Integration , еще один замечательный участник портфеля проектов Spring , обеспечивает легкий обмен сообщениями и интеграцию с внешними системами. Он в основном используется в приложениях и сервисах на основе Spring , обеспечивая исключительную совместимость со всеми остальными проектами Spring .

Основная цель Spring Integration — предоставить простую модель для построения корпоративных решений по интеграции, сохраняя при этом разделение задач, что важно для создания поддерживаемого, тестируемого кода. https://spring.io/projects/spring-integration

Если ваши микросервисы построены на основе Spring Framework , Spring Integration является логичным выбором (если его необходимость оправдана и вписывается в общую архитектуру).

7. Как насчет облака?

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

8. Но есть намного больше…

Честно говоря, слишком много разных библиотек и фреймворков, чтобы о них говорить. Мы обсуждали наиболее широко используемые из них, но их гораздо больше. Давайте кратко упомянем некоторые из них.

OSGi и ее распределенный аналог DOSGi , как известно, были единственным верным способом построения модульной системы и платформ на Java. Несмотря на то, что с этим не просто работать, он может очень хорошо подходить для реализации микросервисов .

RxNetty — это довольно низкоуровневый адаптер реактивного расширения для Netty , весьма полезный, если вам нужен движок с очень низкими издержками.

Rest.li (от Linkedin ) — это платформа для построения надежных, масштабируемых архитектур веб-сервисов RESTful с использованием динамического обнаружения и простых асинхронных API.

Apache Pulsar — это мультитенантное, высокопроизводительное решение с очень малой задержкой для обмена сообщениями между серверами, которое изначально было разработано Yahoo .

9. Пейзаж Java / JVM — Выводы

В этом разделе руководства мы разбирали тонны различных библиотек и сред, которые используются сегодня для создания микросервисов в Java (и JVM в целом). У каждого из них есть своя ниша, но это не облегчает процесс принятия решения. Важно отметить, что сегодняшняя архитектура может не отражать реальность завтрашнего дня: вам придется выбирать стек, который может масштабироваться вместе с вашими микросервисами на долгие годы.

10. Что дальше

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

Полный набор примеров проектов доступен для скачивания .