Привет, Хаброжители! Год от года обретая новых сторонников, облачно-ориентированные и микросервисные архитектуры стали основой современного IT. Такой переход значительно повлиял и на структуру коммуникаций. Теперь приложения часто подключаются друг к другу по сети, и это происходит с помощью технологий межпроцессной коммуникации. Одной из наиболее популярных и эффективных технологий такого рода является gRPC, но информации о ней не хватает. Так было, пока не вышла эта книга!
Наконец архитекторы и разработчики смогут подробно разобраться, как технология gRPC устроена «под капотом», и для этого не придется разгребать десятки устаревших ссылок в поисковике.
Поняв основы gRPC и изучив паттерны коммуникации, вы научитесь создавать приложения gRPC на Go и Java, запускать их в продакшене, а также разберетесь, как gRPC взаимодействует с экосистемой Kubernetes.
Целевая аудитория
Эта книга ориентирована прежде всего на разработчиков, которые занимаются созданием распределенных приложений и микросервисов с помощью разных технологий межпроцессного взаимодействия. Такие разработчики должны разбираться в основах gRPC, понимать, когда и как следует использовать данный протокол, иметь представление о рекомендуемых методиках развертывания gRPC-сервисов в промышленных условиях и т.д. Кроме того, представленный материал будет весьма полезен архитекторам, которые внедряют микросервисную или облачно-ориентированную архитектуру и проектируют механизмы взаимодействия сервисов: gRPC сравнивается с аналогичными технологиями и даются рекомендации о том, когда его стоит использовать, а когда — нет.
Мы исходим из того, что разработчики и архитекторы, читающие данную книгу, имеют общее представление о распределенных вычислениях, включая методы межпроцессного взаимодействия, сервис-ориентированную архитектуру (service-oriented architecture, SOA) и микросервисы.
Иногда при разработке реальных gRPC-приложений приходится реализовывать дополнительные возможности, такие как перехват входящих и исходящих RPC-сообщений, эластичная обработка сетевых задержек, обработка ошибок, обмен метаданными между сервисами и потребителями и т. д.
В этой главе вы познакомитесь с некоторыми ключевыми расширенными возможностями gRPC: с применением перехватчиков для перехвата RPC-сообщений на серверной и клиентской стороне, использованием крайних сроков для определения времени ожидания вызовов, обработкой ошибок на стороне сервера и клиента с учетом общепринятых рекомендаций. Кроме того, мы рассмотрим запуск нескольких сервисов на одном сервере с помощью мультиплексирования, обмен пользовательскими метаданными между приложениями, балансировку нагрузки и сопоставление имен при вызове других сервисов, а также сжатие RPC-вызовов для эффективного использования пропускной способности сети.
Начнем с обсуждения перехватчиков.
Иногда перед вызовом удаленной функции на клиентской или серверной стороне или после него нужно выполнить некие рутинные операции. На этот случай gRPC позволяет перехватывать вызов для выполнения таких задач, как ведение журнала, аутентификация, сбор метрик и пр., используя механизм расширения под названием «перехватчик» (interceptor). Установка перехватчиков в клиентском или серверном приложении возможна с помощью предоставленного gRPC простого API. Это один из ключевых способов расширения gRPC, который довольно хорошо подходит для реализации журналирования, аутентификации, авторизации, сбора метрик, трассировки и других потребительских требований.
Перехватчики можно разделить на две категории в зависимости от перехватываемых типов вызовов. В унарном RPC можно использовать унарные перехватчики, а в потоковом — потоковые. Они могут применяться как на серверной, так и на клиентской стороне. Сначала посмотрим, как они работают на стороне сервера.
В момент вызова клиентом удаленного метода gRPC-сервиса можно выполнить определенную логику, используя серверный перехватчик. Это помогает в ситуациях, когда перед удаленным вызовом, к примеру, нужно провести аутентификацию. Как видно на рис. 5.1, к любому gRPC серверу, который вы разрабатываете, можно подключить один или несколько перехватчиков. Например, чтобы подключить новый серверный перехватчик к сервису OrderManagement, вы можете реализовать перехватчик и зарегистрировать его при создании gRPC-сервера.
Унарные и потоковые перехватчики предназначены для соответствующих видов RPC. Сначала рассмотрим серверные унарные перехватчики.
Унарный перехватчик
Если вы хотите перехватить унарный удаленный вызов на стороне сервера, то вам необходимо реализовать серверный унарный перехватчик. В листинге 5.1
показан фрагмент кода на языке Go, в котором при создании gRPC-сервера реализуется и регистрируется функция типа UnaryServerInterceptor. Это тип серверных унарных перехватчиков со следующей сигнатурой:
Внутри этой функции вы имеете полный контроль над унарными удаленными вызовами, которые получает ваш gRPC-сервер.
Листинг 5.1. Серверный унарный перехватчик в gRPC
1. Перед вызовом: здесь вы можете перехватить сообщение до того, как будет вызван соответствующий удаленный метод.
2. Вызов RPC-метода с помощью UnaryHandler.
3. После вызова: здесь вы можете обработать ответ, сгенерированный в результате удаленного вызова.
4. Возвращение RPC-ответа.
5. Регистрация унарного перехватчика на gRPC-сервере.
Реализацию серверного унарного перехватчика обычно можно разделить на три этапа: предобработки, вызова RPC-метода, постобработки. Как понятно из названия, этап предобработки выполняется до вызова удаленного метода. На этапе постобработки пользователь может получить информацию о текущем RPC-вызове, проанализировав переданные аргументы, такие как контекст (ctx), запрос (req) и состояние сервера. Возможно даже модифицировать RPC-вызов.
Дальше используется обработчик UnaryHandler для вызова удаленного метода. Затем идет этап постобработки, на котором при необходимости можно обработать возвращаемый ответ и сообщение об ошибке; по его окончании вы должны вернуть ответ и сообщение об ошибке в виде параметров функции своего перехватчика. Если постобработка не требуется, то вы можете просто вернуть вызов обработчика (handler(ctx, req)).
Теперь обсудим потоковые перехватчики.
Потоковый перехватчик
Серверные потоковые перехватчики перехватывают любые вызовы потокового RPC, которые приходят на gRPC-сервер. Они состоят из двух этапов: предобработки и перехвата потоковой операции.
Во фрагменте кода на языке Go, показанном в листинге 5.2, перехватываются вызовы потокового RPC, направленные к сервису OrderManagement. Тип StreamServerInterceptor — это тип серверного потокового перехватчика, а orderServerStreamInterceptor — функция данного типа, имеющая следующую сигнатуру:
Как и в унарном перехватчике, этап предобработки позволяет перехватить вызов потокового RPC, пока он не дошел до реализации сервиса. Далее, чтобы завершить выполнение удаленного метода, можно вызвать StreamHandler. После этапа предобработки вы можете перехватить сообщение потокового RPC, воспользовавшись оберткой, реализующей интерфейс grpc.ServerStream. Эту обертку можно передать при вызове grpc.StreamHandler, используя выражение handler(srv, newWrappedStream(ss)). Интерфейс grpc.ServerStream перехватывает потоковые сообщения, отправляемые или принимаемые gRPC-сервисом. Он реализует функции SendMsg и RecvMsg, которые вызываются в момент получения или отправления сервисом сообщения потокового RPC.
Листинг 5.2. Серверный потоковый перехватчик в gRPC
1. Обертка для потока grpc.ServerStream.
2. Реализация функции RecvMsg, принадлежащей обертке; обрабатывает сообщения, принимаемые с помощью потокового RPC.
3. Реализация функции SendMsg, принадлежащей обертке; обрабатывает сообщения, отправляемые с помощью потокового RPC.
4. Создание экземпляра обертки.
5. Реализация потокового перехватчика.
6. Этап предобработки.
7. Вызов метода потокового RPC с помощью обертки.
8. Регистрация перехватчика.
Понять, как потоковый перехватчик ведет себя на серверной стороне, помогут следующие журнальные записи gRPC-сервера. Учитывая порядок, в котором они выводятся, можно проследить действия, выполняемые потоковым перехватчиком. Здесь вызывается удаленный потоковый метод SearchOrders:
В клиентских перехватчиках используется очень похожая терминология, с некоторыми незначительными отличиями, касающимися интерфейсов и сигнатур функций. Поговорим об этом подробно.
Об авторах
Касун Индрасири — архитектор программных систем, имеющий богатый опыт в области микросервисной и облачно-ориентированной архитектур, а также корпоративной интеграции. Занимает должность директора по архитектуре интеграции в WSO2 и отвечает за разработку WSO2 Enterprise Integrator. Касун написал книгу Microservices for Enterprise (Apress, 2018). Кроме того, он выступал на нескольких конференциях, включая O’Reilly Software Architecture Conference 2019 в Сан-Хосе и GOTO Con 2019 в Чикаго, а также на конференции WSO2. Касун живет в Сан-Хосе, штат Калифорния; организовал одну из крупнейших встреч для специалистов по микросервисам в районе залива Сан-Франциско, Silicon Valley Microservices, APIs and Integration.
Данеш Курупу — ведущий разработчик в WSO2, работает в области корпоративной интеграции и микросервисных технологий. Как проектировщик и разработчик он руководит внедрением поддержки gRPC в открытый облачно-ориентированный язык программирования Ballerina. Является участником сообщества и ключевым соавтором таких проектов, как WSO2 Microservices Framework for Java и WSO2 Governance Registry.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — gRPC
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Наконец архитекторы и разработчики смогут подробно разобраться, как технология gRPC устроена «под капотом», и для этого не придется разгребать десятки устаревших ссылок в поисковике.
Поняв основы gRPC и изучив паттерны коммуникации, вы научитесь создавать приложения gRPC на Go и Java, запускать их в продакшене, а также разберетесь, как gRPC взаимодействует с экосистемой Kubernetes.
Целевая аудитория
Эта книга ориентирована прежде всего на разработчиков, которые занимаются созданием распределенных приложений и микросервисов с помощью разных технологий межпроцессного взаимодействия. Такие разработчики должны разбираться в основах gRPC, понимать, когда и как следует использовать данный протокол, иметь представление о рекомендуемых методиках развертывания gRPC-сервисов в промышленных условиях и т.д. Кроме того, представленный материал будет весьма полезен архитекторам, которые внедряют микросервисную или облачно-ориентированную архитектуру и проектируют механизмы взаимодействия сервисов: gRPC сравнивается с аналогичными технологиями и даются рекомендации о том, когда его стоит использовать, а когда — нет.
Мы исходим из того, что разработчики и архитекторы, читающие данную книгу, имеют общее представление о распределенных вычислениях, включая методы межпроцессного взаимодействия, сервис-ориентированную архитектуру (service-oriented architecture, SOA) и микросервисы.
Структура издания
Эта книга написана так, что теоретический материал объясняется на примерах из реальной жизни. Мы активно используем демонстрационный код на Go и Java, чтобы читатель мог сразу получить практический опыт применения концепций, с которыми знакомится. Мы разделили эту книгу на восемь глав.
В главе 1 вы получите общее представление об основах gRPC и сможете сравнить эту технологию с аналогичными разновидностями межпроцессного взаимодействия, такими как REST, GraphQL и прочими механизмами RPC.
На страницах главы 2 вы получите свой первый практический опыт создания полноценного gRPC-приложения на Go или Java.
В главе 3 вы исследуете методы работы с gRPC на реальных примерах.
Если вы уже имеете опыт использования gRPC и хотите узнать, как устроена эта технология, то глава 4 для вас. Здесь мы пройдемся по каждому этапу взаимодействия сервера и клиента и покажем, как gRPC работает на уровне сети.
В главе 5 вы изучите некоторые из самых важных расширенных возможностей gRPC, такие как использование перехватчиков, работа с метаданными, мультиплексирование, балансировка нагрузки и т.д.
В главе 6 вы досконально изучите методы защиты коммуникационных каналов и узнаете, как аутентифицировать пользователей и управлять их доступом к gRPC-приложениям.
В главе 7 мы пройдемся по всему циклу разработки gRPC-приложений. Будут рассмотрены тестирование, интеграция с CI/CD, развертывание и запуск в Docker и Kubernetes, а также мониторинг.
В главе 8 мы обсудим некоторые полезные вспомогательные компоненты, разработанные с расчетом на gRPC. Большинство из этих проектов подходит для создания реальных gRPC-приложений.
В главе 1 вы получите общее представление об основах gRPC и сможете сравнить эту технологию с аналогичными разновидностями межпроцессного взаимодействия, такими как REST, GraphQL и прочими механизмами RPC.
На страницах главы 2 вы получите свой первый практический опыт создания полноценного gRPC-приложения на Go или Java.
В главе 3 вы исследуете методы работы с gRPC на реальных примерах.
Если вы уже имеете опыт использования gRPC и хотите узнать, как устроена эта технология, то глава 4 для вас. Здесь мы пройдемся по каждому этапу взаимодействия сервера и клиента и покажем, как gRPC работает на уровне сети.
В главе 5 вы изучите некоторые из самых важных расширенных возможностей gRPC, такие как использование перехватчиков, работа с метаданными, мультиплексирование, балансировка нагрузки и т.д.
В главе 6 вы досконально изучите методы защиты коммуникационных каналов и узнаете, как аутентифицировать пользователей и управлять их доступом к gRPC-приложениям.
В главе 7 мы пройдемся по всему циклу разработки gRPC-приложений. Будут рассмотрены тестирование, интеграция с CI/CD, развертывание и запуск в Docker и Kubernetes, а также мониторинг.
В главе 8 мы обсудим некоторые полезные вспомогательные компоненты, разработанные с расчетом на gRPC. Большинство из этих проектов подходит для создания реальных gRPC-приложений.
gRPC: расширенные возможности
Иногда при разработке реальных gRPC-приложений приходится реализовывать дополнительные возможности, такие как перехват входящих и исходящих RPC-сообщений, эластичная обработка сетевых задержек, обработка ошибок, обмен метаданными между сервисами и потребителями и т. д.
Чтобы сохранить однородность наших примеров, все фрагменты кода, представленные в этой главе, написаны на Go. Но если вы лучше разбираетесь в Java, то полноценные примеры на данном языке для тех же сценариев использования можно найти в репозитории кода книги.
В этой главе вы познакомитесь с некоторыми ключевыми расширенными возможностями gRPC: с применением перехватчиков для перехвата RPC-сообщений на серверной и клиентской стороне, использованием крайних сроков для определения времени ожидания вызовов, обработкой ошибок на стороне сервера и клиента с учетом общепринятых рекомендаций. Кроме того, мы рассмотрим запуск нескольких сервисов на одном сервере с помощью мультиплексирования, обмен пользовательскими метаданными между приложениями, балансировку нагрузки и сопоставление имен при вызове других сервисов, а также сжатие RPC-вызовов для эффективного использования пропускной способности сети.
Начнем с обсуждения перехватчиков.
Перехватчики
Иногда перед вызовом удаленной функции на клиентской или серверной стороне или после него нужно выполнить некие рутинные операции. На этот случай gRPC позволяет перехватывать вызов для выполнения таких задач, как ведение журнала, аутентификация, сбор метрик и пр., используя механизм расширения под названием «перехватчик» (interceptor). Установка перехватчиков в клиентском или серверном приложении возможна с помощью предоставленного gRPC простого API. Это один из ключевых способов расширения gRPC, который довольно хорошо подходит для реализации журналирования, аутентификации, авторизации, сбора метрик, трассировки и других потребительских требований.
Перехватчики доступны не для всех языков, которые поддерживаются в gRPC, и их реализация может варьироваться. В этой главе мы уделяем внимание только Go и Java.
Перехватчики можно разделить на две категории в зависимости от перехватываемых типов вызовов. В унарном RPC можно использовать унарные перехватчики, а в потоковом — потоковые. Они могут применяться как на серверной, так и на клиентской стороне. Сначала посмотрим, как они работают на стороне сервера.
Серверные перехватчики
В момент вызова клиентом удаленного метода gRPC-сервиса можно выполнить определенную логику, используя серверный перехватчик. Это помогает в ситуациях, когда перед удаленным вызовом, к примеру, нужно провести аутентификацию. Как видно на рис. 5.1, к любому gRPC серверу, который вы разрабатываете, можно подключить один или несколько перехватчиков. Например, чтобы подключить новый серверный перехватчик к сервису OrderManagement, вы можете реализовать перехватчик и зарегистрировать его при создании gRPC-сервера.
Унарные и потоковые перехватчики предназначены для соответствующих видов RPC. Сначала рассмотрим серверные унарные перехватчики.
Унарный перехватчик
Если вы хотите перехватить унарный удаленный вызов на стороне сервера, то вам необходимо реализовать серверный унарный перехватчик. В листинге 5.1
показан фрагмент кода на языке Go, в котором при создании gRPC-сервера реализуется и регистрируется функция типа UnaryServerInterceptor. Это тип серверных унарных перехватчиков со следующей сигнатурой:
func(ctx context.Context, req interface{}, info *UnaryServerInfo,
handler UnaryHandler) (resp interface{}, err error)
Внутри этой функции вы имеете полный контроль над унарными удаленными вызовами, которые получает ваш gRPC-сервер.
Листинг 5.1. Серверный унарный перехватчик в gRPC
// унарный перехватчик на стороне сервера
func orderUnaryServerInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler)
(interface{}, error) {
// логика перед вызовом
// получает информацию о текущем RPC-вызове путем
// анализа переданных аргументов
log.Println("======= [Server Interceptor] ", info.FullMethod) 1
// вызываем обработчик, чтобы завершить
// нормальное выполнение унарного RPC-вызова
m, err := handler(ctx, req) 2
// логика после вызова
log.Printf(" Post Proc Message : %s", m) 3
return m, err 4
}
// ...
func main() {
...
// регистрируем перехватчик на стороне сервера
s := grpc.NewServer(
grpc.UnaryInterceptor(orderUnaryServerInterceptor)) 5
...
1. Перед вызовом: здесь вы можете перехватить сообщение до того, как будет вызван соответствующий удаленный метод.
2. Вызов RPC-метода с помощью UnaryHandler.
3. После вызова: здесь вы можете обработать ответ, сгенерированный в результате удаленного вызова.
4. Возвращение RPC-ответа.
5. Регистрация унарного перехватчика на gRPC-сервере.
Реализацию серверного унарного перехватчика обычно можно разделить на три этапа: предобработки, вызова RPC-метода, постобработки. Как понятно из названия, этап предобработки выполняется до вызова удаленного метода. На этапе постобработки пользователь может получить информацию о текущем RPC-вызове, проанализировав переданные аргументы, такие как контекст (ctx), запрос (req) и состояние сервера. Возможно даже модифицировать RPC-вызов.
Дальше используется обработчик UnaryHandler для вызова удаленного метода. Затем идет этап постобработки, на котором при необходимости можно обработать возвращаемый ответ и сообщение об ошибке; по его окончании вы должны вернуть ответ и сообщение об ошибке в виде параметров функции своего перехватчика. Если постобработка не требуется, то вы можете просто вернуть вызов обработчика (handler(ctx, req)).
Теперь обсудим потоковые перехватчики.
Потоковый перехватчик
Серверные потоковые перехватчики перехватывают любые вызовы потокового RPC, которые приходят на gRPC-сервер. Они состоят из двух этапов: предобработки и перехвата потоковой операции.
Во фрагменте кода на языке Go, показанном в листинге 5.2, перехватываются вызовы потокового RPC, направленные к сервису OrderManagement. Тип StreamServerInterceptor — это тип серверного потокового перехватчика, а orderServerStreamInterceptor — функция данного типа, имеющая следующую сигнатуру:
func(srv interface{}, ss ServerStream, info *StreamServerInfo,
handler StreamHandler) error
Как и в унарном перехватчике, этап предобработки позволяет перехватить вызов потокового RPC, пока он не дошел до реализации сервиса. Далее, чтобы завершить выполнение удаленного метода, можно вызвать StreamHandler. После этапа предобработки вы можете перехватить сообщение потокового RPC, воспользовавшись оберткой, реализующей интерфейс grpc.ServerStream. Эту обертку можно передать при вызове grpc.StreamHandler, используя выражение handler(srv, newWrappedStream(ss)). Интерфейс grpc.ServerStream перехватывает потоковые сообщения, отправляемые или принимаемые gRPC-сервисом. Он реализует функции SendMsg и RecvMsg, которые вызываются в момент получения или отправления сервисом сообщения потокового RPC.
Листинг 5.2. Серверный потоковый перехватчик в gRPC
// Потоковый перехватчик на стороне сервера
// wrappedStream — обертка вокруг встроенного интерфейса
// grpc.ServerStream, которая перехватывает вызовы методов
// RecvMsg и SendMsg
type wrappedStream struct { 1
grpc.ServerStream
}
2
func (w *wrappedStream) RecvMsg(m interface{}) error {
log.Printf("====== [Server Stream Interceptor Wrapper] " +
"Receive a message (Type: %T) at %s",
m, time.Now().Format(time.RFC3339))
return w.ServerStream.RecvMsg(m)
}
3
func (w *wrappedStream) SendMsg(m interface{}) error {
log.Printf("====== [Server Stream Interceptor Wrapper] " +
"Send a message (Type: %T) at %v",
m, time.Now().Format(time.RFC3339))
return w.ServerStream.SendMsg(m)
}
4
func newWrappedStream(s grpc.ServerStream) grpc.ServerStream {
return &wrappedStream{s}
}
5
func orderServerStreamInterceptor(srv interface{},
ss grpc.ServerStream, info *grpc.StreamServerInfo,
handler grpc.StreamHandler) error {
log.Println("====== [Server Stream Interceptor] ",
info.FullMethod) 6
err := handler(srv, newWrappedStream(ss)) 7
if err != nil {
log.Printf("RPC failed with error %v", err)
}
return err
}
...
// регистрация перехватчика
s := grpc.NewServer(
grpc.StreamInterceptor(orderServerStreamInterceptor)) 8
...
1. Обертка для потока grpc.ServerStream.
2. Реализация функции RecvMsg, принадлежащей обертке; обрабатывает сообщения, принимаемые с помощью потокового RPC.
3. Реализация функции SendMsg, принадлежащей обертке; обрабатывает сообщения, отправляемые с помощью потокового RPC.
4. Создание экземпляра обертки.
5. Реализация потокового перехватчика.
6. Этап предобработки.
7. Вызов метода потокового RPC с помощью обертки.
8. Регистрация перехватчика.
Понять, как потоковый перехватчик ведет себя на серверной стороне, помогут следующие журнальные записи gRPC-сервера. Учитывая порядок, в котором они выводятся, можно проследить действия, выполняемые потоковым перехватчиком. Здесь вызывается удаленный потоковый метод SearchOrders:
[Server Stream Interceptor] /ecommerce.OrderManagement/searchOrders
[Server Stream Interceptor Wrapper] Receive a message
Matching Order Found : 102 -> Writing Order to the stream ...
[Server Stream Interceptor Wrapper] Send a message...
Matching Order Found : 104 -> Writing Order to the stream ...
[Server Stream Interceptor Wrapper] Send a message...
В клиентских перехватчиках используется очень похожая терминология, с некоторыми незначительными отличиями, касающимися интерфейсов и сигнатур функций. Поговорим об этом подробно.
Об авторах
Касун Индрасири — архитектор программных систем, имеющий богатый опыт в области микросервисной и облачно-ориентированной архитектур, а также корпоративной интеграции. Занимает должность директора по архитектуре интеграции в WSO2 и отвечает за разработку WSO2 Enterprise Integrator. Касун написал книгу Microservices for Enterprise (Apress, 2018). Кроме того, он выступал на нескольких конференциях, включая O’Reilly Software Architecture Conference 2019 в Сан-Хосе и GOTO Con 2019 в Чикаго, а также на конференции WSO2. Касун живет в Сан-Хосе, штат Калифорния; организовал одну из крупнейших встреч для специалистов по микросервисам в районе залива Сан-Франциско, Silicon Valley Microservices, APIs and Integration.
Данеш Курупу — ведущий разработчик в WSO2, работает в области корпоративной интеграции и микросервисных технологий. Как проектировщик и разработчик он руководит внедрением поддержки gRPC в открытый облачно-ориентированный язык программирования Ballerina. Является участником сообщества и ключевым соавтором таких проектов, как WSO2 Microservices Framework for Java и WSO2 Governance Registry.
Об обложке
На обложке изображена морская чернеть (Aythya marila), представитель семейства утиных. Размножается весной и летом в приполярной тундре Аляски, Канады и Европы, а затем мигрирует на юг, чтобы перезимовать на побережьях Северной Америки, Европы и Азии.
У самцов желтые глаза, ярко-синий клюв, черная голова с заметным темно-зеленым отливом, белые бока и рябая спина с частым чередованием серых и белых перьев. У самок менее яркий окрас, что защищает их в период гнездования: бледно-синий клюв, над которым находится небольшое белое пятно, а также коричневые голова и тело. Эти утки в среднем достигают около 50 см в длину, имеют размах крыльев 75 см и обычно весят 900 г.
Особи морской чернети спариваются весной. Самка откладывает в среднем восемь яиц в земляное гнездо, выстланное ее собственным пухом. Утята покидают гнездо сразу после вылупления и способны себя прокормить с самого рождения. Летать они начинают только спустя сорок, а то и больше дней и в этот период, несмотря на защиту матери, являются легкой добычей для хищных птиц и наземных хищников, таких как лисы.
Чернеть относится к так называемым нырковым уткам; она может добывать пищу не только на земле и на плаву, но и под водой. Как и у других нырковых уток, лапки у чернети сдвинуты ближе к задней части ее компактного туловища, что помогает ей грести во время ныряния. Морская чернеть может нырять на глубину до шести метров и находиться под водой около минуты, благодаря чему ей доступно больше еды, чем большинству других нырковых уток.
Несмотря на сокращение популяции в последние 40 лет, в Красной книге эти птицы помечены как «вид, вызывающий наименьшие опасения».
Многие животные, изображенные на обложках издательства O’Reilly, находятся под угрозой исчезновения: все они важны для нашей планеты.
Титульная цветная иллюстрация создана Карен Монтгомери на основе черно-белой гравюры из книги British Birds.
У самцов желтые глаза, ярко-синий клюв, черная голова с заметным темно-зеленым отливом, белые бока и рябая спина с частым чередованием серых и белых перьев. У самок менее яркий окрас, что защищает их в период гнездования: бледно-синий клюв, над которым находится небольшое белое пятно, а также коричневые голова и тело. Эти утки в среднем достигают около 50 см в длину, имеют размах крыльев 75 см и обычно весят 900 г.
Особи морской чернети спариваются весной. Самка откладывает в среднем восемь яиц в земляное гнездо, выстланное ее собственным пухом. Утята покидают гнездо сразу после вылупления и способны себя прокормить с самого рождения. Летать они начинают только спустя сорок, а то и больше дней и в этот период, несмотря на защиту матери, являются легкой добычей для хищных птиц и наземных хищников, таких как лисы.
Чернеть относится к так называемым нырковым уткам; она может добывать пищу не только на земле и на плаву, но и под водой. Как и у других нырковых уток, лапки у чернети сдвинуты ближе к задней части ее компактного туловища, что помогает ей грести во время ныряния. Морская чернеть может нырять на глубину до шести метров и находиться под водой около минуты, благодаря чему ей доступно больше еды, чем большинству других нырковых уток.
Несмотря на сокращение популяции в последние 40 лет, в Красной книге эти птицы помечены как «вид, вызывающий наименьшие опасения».
Многие животные, изображенные на обложках издательства O’Reilly, находятся под угрозой исчезновения: все они важны для нашей планеты.
Титульная цветная иллюстрация создана Карен Монтгомери на основе черно-белой гравюры из книги British Birds.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — gRPC
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
awfun
Interceptor-ы примеро одинаково работают в gRPC и в http клиентах. И все равно можно периодически видеть попытки реализовать логгирование через аспекты.