Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Service Discovery
Больше, чем кажется
2
3
4
Мнение докладчика может не совпадать с официальной
позицией его работодателя, начальника, коллег или других
специалистов...
Про Одноклассники
5
Олег Анастасьев
Распределенные системы в Одноклассниках
https://www.youtube.com/watch?v=yRYYR13ymik
Ни...
A что будет?
240 слайдов
12 демо
6
План такой
1. Новая реальность – новые
проблемы
2. Что такое Service Discovery и с
чем его едят
3. Как это сделано в Sprin...
А в чём именно проблема?
8
Database
JavaEE Cluster
Client
9
Так было когда-то
Big App
Static resource
Database
Remote EJB
10
Ресурсы и их типы в прошлом
Как было?
• Количество ресурсов мало
• Хост и порт, как правило,
статичны
• Конфигурация менялась редко
11
insert into resources_table values (key, endpoint)
12
Классическое решение проблемы
13
Ещё одно решение
<providers>
<provider id="provider1"
url="socket://10.0.1.1:8080/?weight="42"/>
<provider id="provider...
API
API instance
API instance
API instance
14
Путь к микросервисам
Some API
Another API
Instance 1
Another API
Instance 2
Another API
Instance 3
?
15
Кого же вызвать?
Статическая конфигурация
16
<providers>
<provider
id="provider{{ p.number }}"
url="socket://10.0.1.{{ p.number }}:8080/?we...
После набега
хипстеров
• Контейнеры, docker, облака
17
{
"id": "/hippo/rent-api",
"cpus": 1,
"mem": 1024,
"instances": 42,
"container": {
"docker": {
"image": "docker/hippo-rent...
{
"id": "/hippo/rent-api",
"cpus": 1,
"mem": 1024,
"instances": 42,
"container": {
"docker": {
"image": "docker/hippo-rent...
После набега
хипстеров
• Хост и порт, как правило,
заранее неизвестны
(ip-per-instance делает
статическим порт)
• Конфигур...
Service Discovery • Какой инстанс мы можем
вызвать?
21
22
Сервис Фото
Простая задача
Сервис Группы
Связи Группа-
Пользователей
Сервис Пользователи
Сервис Альбомы Группы Обложка ...
5 parallel service calls
23
Простая задача
Client
call
S1 S2 S3 S4 S5
24
Client
call
S1 S2 S3 S4 S5
25
p = 0.99*
26
Простая статистика
* 0.99 не имеет отношения к реальности и взята для упрощения
P = p5 = (0.99)5 ≈ 0.95
p – вероятность успеха вызова одного сервиса
27
Получается не очень
Service Discovery
• Какой инстанс мы можем
вызвать?
• Какой инстанс ЛУЧШЕ
подходит для вызова?
28
P = p5 = (0.999)5 ≈ 0.995
p – вероятность успеха вызова одного сервиса
29
Хотим как-то так
Service Discovery
• Какой инстанс мы можем
вызвать?
• Какой инстанс ЛУЧШЕ
подходит для вызова?
• Как мы можем увеличить
ве...
S1 S2 S3 S4 S5
31
STATE
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
32
STATE
FILTER
S1 S2 S4
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
33
STATE
FILTER
S2 S4Other DC
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
34
S1
STATE
FILTER
S2 S4
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
?
35
S1
Service Service Service Service Service
STATE
FILTER
S2 S4
RULE
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
36
S1
S4
Service Service Service Service Service
STATE
FILTER
S2 S4
RULE
Client
S1 S2 S3 S4 S5
call
S1 S2 S3 S4 S5
37
S4
Service Service Service Service Service
STATE
FILTER
S2 S4
RULE
Client
S1 S2 S3 S4 S5
call
S1 S2 S3 S4 S5
38
S4
P
S1 S2 S3 S4 S5
?
39
Service Registry
• Database
• Properties File
• Environment variables
40
Service Registry
• Database
• Properties File
• Environment variables
41
Service Registry
• Netflix Eureka
• Consul
• etcd
• Zookeeper
• …
42
Service A
Service Registry
Service B Service C
register
43
Service A Service B Service C
health check
44
Service Registry
Service A Service B Service C
heartbeat
45
Service Registry
Service A Service B Service C
health check
46
Service Registry
Service A Service B Service C
health check
47
Service Registry
Service A Service B Service C
deregister
48
Service Registry
Service A Service B Service C
deregister
graceful shutdown
49
Service Registry
Leader
Master Master
Leader-Election
Quorum (n + 1)/2
Distributed Locks
50
Client/Slave
Leader
Master Master
Client/Slave Client/Slave
horizontal scaling
51
Service Registry
• Netflix Eureka, Consul, etcd,
Zookeeper
• ???
52
{
"id": "/hippo/rent-api",
"cpus": 1,
"mem": 1024,
"instances": 42,
"container": {
"docker": {
"image": "docker/hippo-rent...
54
Marathon
Marathon
Marathon
Mesos
Master
Mesos
Master
Mesos
Master
Mesos Slave
Mesos Slave
ZK
ZK ZK
55
Marathon
Marathon
Marathon
Mesos
Master
Mesos
Master
Mesos
Master
Mesos Slave
Mesos Slave
Docker
Docker
ZK
ZK ZK
56
curl http://marathon.master/v2/apps/{appId}
57
Получаем данные
58
"tasks": [
{
"appId": "/test-marathon-app",
"host": "mesos-slave.dc2",
"id": "test-marathon-app.43ed8a70-…-0242ac110006...
59
"tasks": [
{
"appId": "/test-marathon-app",
"host": "mesos-slave.dc2",
"id": "test-marathon-app.43ed8a70-…-0242ac110006...
60
"tasks": [
{
"appId": "/test-marathon-app",
"host": "mesos-slave.dc2",
"id": "test-marathon-app.43ed8a70-…-0242ac110006...
61
"tasks": [
{
"appId": "/test-marathon-app",
"host": "mesos-slave.dc2",
"id": "test-marathon-app.43ed8a70-…-0242ac110006...
“Service Registry”
• Netflix Eureka, Consul, etcd,
Zookeeper
• Kubernetes
• Mesos+Marathon
• DCOS
• Own solution
62
Demo#0 Введение в Marathon
63
Spring Cloud
• Фреймворк для разработки
распределённых систем
• Реализует основные паттерны
• Имеет коннекторы к различным...
Client-Side Service Discovery
65
...
@EnableDiscoveryClient
...
public class Application {
@Autowired
private DiscoveryClient discoveryClient;
public List<...
...
@EnableDiscoveryClient
...
public class Application {
@Autowired
private DiscoveryClient discoveryClient;
public List<...
public interface DiscoveryClient {
String description();
ServiceInstance getLocalServiceInstance();
List<ServiceInstance> ...
69
Рекомендуемый интерфейс
https://www.safaribooksonline.com/library/view/cloud-native-java/9781449374631/ch16.html
Spring...
public interface DiscoveryClient {
String description();
@Deprecated
ServiceInstance getLocalServiceInstance();
List<Servi...
public interface DiscoveryClient {
String description();
ServiceInstance getLocalServiceInstance();
List<ServiceInstance> ...
public interface DiscoveryClient {
String description();
ServiceInstance getLocalServiceInstance();
List<ServiceInstance> ...
...
@EnableDiscoveryClient
...
public class Application {
@Autowired
private DiscoveryClient discoveryClient;
public List<...
Spring Cloud Core
Eureka Consul Marathon
74
Spring Cloud Core
Eureka Consul Marathon
Reference/Native implementation
Most complex load balancing
75
Spring Cloud Core
Eureka Consul Marathon
Out of the box PaaS
76
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return client.getAppTasks(ServiceIdConverter.conve...
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return client.getAppTasks(ServiceIdConverter.conve...
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return client.getAppTasks(ServiceIdConverter.conve...
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return client.getAppTasks(ServiceIdConverter.conve...
@Override
public List<ServiceInstance> getInstances(String serviceId) {
return client.getAppTasks(ServiceIdConverter.conve...
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/instances")
public List<ServiceInstance> instances()...
Demo#1
Наивная реализация
DiscoveryClient
83
Service Service Service Service Service
S2 S4
S2
LoadBalancerClient
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
New Service
register/der...
Шаблоны
регистрации
• 3d-party
• Self-registration
85
Docker Engine
86
3d-party registration pattern
Docker Engine
Service
register/deregister
87
3d-party registration pattern
Docker Engine
Service
register/deregister
88
Registrator
emit event
3d-party registration pattern
Docker Engine
Service
register/deregister
89
Registrator
emit event
Service Registry
native event
3d-party registration pa...
Service Definition
90
deploy manifest
PaaS
Нативная регистрация (PaaS)
Service Definition
91
deploy manifest
PaaS
Нативная регистрация (PaaS)
S1 S2 S3 S4 S5
run services
Self-Registration
• Реализация (как всегда)
зависит от выбранного реестра
сервисов
• Не нужна для PaaS-решений
92
SmartLifecycle
DiscoveryLifecycle EurekaDiscoveryClientConfiguration
ConsulLifecycle
93
SmartLifecycle
DiscoveryLifecycle EurekaDiscoveryClientConfiguration
ConsulLifecycle
94
@Deprecated
AutoServiceRegistration
AbstractServiceRegistration Eureka. No implementation
ConsulLifecycle
95
DiscoveryLifecycle/
AutoServiceRegistration
LifecycleProcessor
autoStartup on refresh
startBeans on start
Service Registry...
DiscoveryLifecycle/
AutoServiceRegistration
LifecycleProcessor
autoStartup on refresh
startBeans on start
Service Registry...
LifecycleProcessor
on RefreshScope event
stopBeans on stop
Service Registry
deregister
98
DiscoveryLifecycle/
AutoServiceR...
DiscoveryLifecycle/
AutoServiceRegistration
LifecycleProcessor
on RefreshScope event
stopBeans on stop
Service Registry
de...
Контейнеры • Docker etc.
100
Docker container
Internal IP External IP
101
?
> ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280stf0: ...
> ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280stf0: ...
spring:
cloud:
discovery:
lifecycle:
enabled: false
104
External Registrator
spring:
cloud:
inetutils:
ignored-interfaces:
- vbox*
- bridge*
- lo*
105
spring:
cloud:
inetutils:
ignored-interfaces:
- vbox*
- bridge*
- lo*
106
spring:
cloud:
inetutils:
preferred-interfaces:
- eth0
107
spring:
cloud:
discovery:
preferIpAddress: true (false)
108
Шаблоны
регистрации
• Выбирайте внешнюю
регистрацию сервисов, если
она идёт из коробки
• Саморегистрация имеет
подводные к...
Endpoints
• Предоставляют доступ к
технической информации:
метрики, environment, прокси
• Не только чтение, но и запись
110
http some.service/metrics
111
Метрики
http some.service/env
112
Environment. Чтение
http -v --form POST host:port/env PROP=VALUE
113
Environment. Запись
http some.service/marathon
114
Прокси
Demo#2 Индикаторы и эндпоиты
115
spring:
cloud:
marathon:
listOfServers: m1:8080,m2:8080,m3:8080
username: marathon
password: mesos
116
Marathon
spring:
cloud:
consul:
host: localhost
port: 8500
117
Но вот в Consul-е иначе
На каждом хосту есть consul-агент
spring:
cloud:
marathon:
listOfServers: m1:8080,m2:8080,m3:8080
username: marathon
password: mesos
118
Marathon. Авторизац...
spring:
cloud:
marathon:
listOfServers: m1:8080,m2:8080,m3:8080
token: <api_token>
119
Marathon. Авторизация#2
Marathon AppId: /group/path/app
Spring Cloud serviceId: group.path.app
120
Marathon. Маппинг
Marathon AppId: /group/path/app
Spring Cloud serviceId: group.path.app
121
Marathon. Маппинг
Marathon AppId: /group/path/app
Spring Cloud serviceId: group.path.app
122
Невалидный hostname
Боль реализации
• Разные конфигурации
подключения к реестрам
сервисов
• Специфические фичи,
зависящие от выбора реестра
се...
Demo#3
Отказоустойчивый
DiscoveryClient
124
Вся правда о
DiscoveryClient
• Не используется для
клиентской балансировки
• Участвует в формировании
гиперссылок, в healt...
LoadBalancer
• Основан на Netflix Ribbon
• В принципе может быть
использован вне стека Spring
Cloud
• В общем случае не ну...
Service Service Service Service Service
STATE
FILTER
S2 S4
RULE
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
127
Client
call S4
Service Service Service Service Service
S2 S4
S1 S2 S3 S4 S5
S1 S2 S3 S4 S5
ServerList
128
Client
call S4
Ribbon ServerList
• Static
• Configuration-based
• Discovery-based
129
test-service: #service name
ribbon: #namespace
listOfServers: host:8080,anotherHost:8081
ConfigurationBasedServerList
130
@Autowired
private LoadBalancerClient loadBalancer;
@RequestMapping("/url")
public String realUrl() throws IOException {
r...
@Autowired
private LoadBalancerClient loadBalancer;
@RequestMapping("/url")
public String realUrl() throws IOException {
r...
DiscoveryBasedServerList
133
List<MarathonServer> extractServiceInstances(App app) {
return app.getTasks()
.stream()
.map(...
DiscoveryBasedServerList
134
List<MarathonServer> extractServiceInstances(App app) {
return app.getTasks()
.stream()
.map(...
DiscoveryBasedServerList
135
List<MarathonServer> extractServiceInstances(App app) {
return app.getTasks()
.stream()
.map(...
DiscoveryBasedServerList
136
List<MarathonServer> extractServiceInstances(App app) {
return app.getTasks()
.stream()
.map(...
Ribbon Server vs Service Instance
137
Service InstanсeRibbon Server
Разное представление одного и того же
Demo#4 Переходим на Ribbon
138
Marathon. Ribbon-фишки
139
customer-service:
ribbon:
<your settings here>
ZonePattern: '.+.(.+)' # host.zone1 -> zone1
Met...
Marathon. Ribbon-фишки
140
customer-service:
ribbon:
<your settings here>
ZonePattern: '.+.(.+)' # host.zone1 -> zone1
Met...
Demo#5 Zones and queries
141
А жив ли мой
сервис?
• Что значит ”жив”?
• Как проверить живучесть?
142
S1 S2 S3 S4 S5
S2 S4
S1 S2 S3 S4 S5?
143
Server List
Load Balancer Client
S4
t t + 1
S1
S2
S3
144
t t + 1
S1
S2
S3
ServerList
S1
S2
S3
145
t t + 1
S1
S2
S3
ServerList
S1S1
S2
S3
146
t t + 1
S1
S2
S3
ServerList
S1 S1S1
S2
S3
147
t t + 1
S1
S2
S3
ServerList
S1 S1
t + 2
S1
S2
S3
148
S1 S2 S3 S4 S5
S2 S4
S1 S2 S3 S4 S5Ping ->
149
Server List
Load Balancer Client
S4
Pinger
(background thread)
LoadBalancer
background task
force if setServerList() is called
150
Pinger
(background thread)
LoadBalancer
background task
force if setServerList() is called
IPingStrategy
pingServers
151
Pinger
(background thread)
LoadBalancer
background task
force if setServerList() is called
IPingStrategy
pingServers
isAli...
IPingStrategy SerialPingStrategy
int numCandidates = servers.length;
boolean[] results = new boolean[numCandidates];
for (...
IPingStrategy SerialPingStrategy
int numCandidates = servers.length;
boolean[] results = new boolean[numCandidates];
for (...
IPingStrategy SerialPingStrategy
int numCandidates = servers.length;
boolean[] results = new boolean[numCandidates];
for (...
IPingStrategy SerialPingStrategy
int numCandidates = servers.length;
boolean[] results = new boolean[numCandidates];
for (...
NIWSDiscoveryPing
(Eureka)
IPing
instance.status.equals(
InstanceStatus.UP
)
ConsulPing
(Consul)
MarathonPing
(Marathon)
s...
IPingStrategy ?
Есть честная имплементация, но нет готовых её использований
IPing PingUrl
public boolean isAlive(Server se...
А жив ли мой
сервис?
• Проверять на уровне
приложения дорого
• Доверяем тому, что приходит
из реестра сервисов
159
Demo#6 ping, ping, ping
160
Отсеять плохое • Фильтры и их применение
161
S1 S2 S3 S4 S5
S2 S4
S1 S2 S3 S4 S5
Server List
Filter Chain ->
162
Load Balancer Client
S4
Cluster (Zone 1)
S1
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
163
Cluster (Zone 1)
S1
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
cheap call
164
Cluster (Zone 1)
S1
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
expensive call
165
Cluster (Zone 1)
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
unpredictable
S1
166
Cluster (Zone 1)
S1
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
more guaranteed result
167
ZoneAffinityFilter
getFilteredServers(servers)
LoadBalancerStats
getZoneSnaphot(servers)
168
DynamicServerListLoadBalancer
S1 S2 S3 S4 S5
S2 S4
S1 S2 S3 S4 S5LB Stats ->
Server List
Filter Chain
169
Load Balancer Client
S4
Server Stats
S1
S2
S3
Zone Snapshot
Server Stats
170
S1
S2
S3
Zone Snapshot
activeConnections
activeServers
circuitBreakerTrippedCount
171
ZoneAffinityFilter
LoadBalancerStats
enableZoneAffinity?
zoneAffinity:
maxLoadPerServer: 0.6
maxBlackOutServersPercentage:...
ZoneAffinityFilter
ZoneAffinityPredicate
исключаем инстансы
из других зон
173
zoneAffinity:
maxLoadPerServer: 0.6
maxBlack...
Cluster (Zone 1)
S1
S2
S3
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
cheap call
174
ZoneAffinityFilter
ZoneAffinityPredicate
не фильтруем
175
zoneAffinity:
maxLoadPerServer: 0.6
maxBlackOutServersPercentage...
Cluster (Zone 1)
S1
Cluster (Zone 2)
S4
S5
S6
Client
(Zone 1)
176
S2
S3
ZonePreferenceFilter
List servers = super.getFilteredServers();
List local = servers.filter(server ->
server.zone == zoneF...
ZonePreferenceFilter
List servers = super.getFilteredServers();
List local = servers.filter(server ->
server.zone == zoneF...
Цепочка
фильтров
• Помогает увеличить шансы на
корректное исполнение
запроса
• Отсеивает «плохие» и
«дорогие» инстансы
179
Demo#7 Пишем свой фильтр
180
Правило выбора • Последняя миля
181
S1 S2 S3 S4 S5
S2 S4
S1 S2 S3 S4 S5
?
182
Server List
Filter Chain
Rule
Load Balancer Client
S4
IRule
RoundRobin
ZoneAvoidance
default
Weighted Random
BestAvailability AvailabilityFilter
183
IRule
RoundRobin
ZoneAvoidance
Weighted Random
BestAvailability AvailabilityFiltering
184
upstream test-marathon-app {
server host1 weight=3;
server host2 weight=1;
server host3 weight=1;
}
Как правило вес предоп...
Weight Task
(background thread)
Weighted Rule
background task
LoadBalancerStats
calculate
serverWeight = summ(avgServerRes...
Weight Task
(background thread)
Weighted Rule
background task
LoadBalancerStats
calculate
serverWeight = summ(avgServerRes...
188
Slow Instance Fast Instance
100 ms 10 ms
189
Slow Instance Fast Instance
100 ms 10 ms5 000 requests
190
Slow Instance Fast Instance
100 ms 10 ms5 000 requests
Round Robin Rule
Slow: 2500 and Fast: 2500
Average time: 70
191
Slow Instance Fast Instance
100 ms 10 ms5 000 requests
Round Robin Rule
Slow: 2500 and Fast: 2500
Average time: 70
192
Slow Instance Fast Instance
100 ms 10 ms5 000 requests
Round Robin Rule
Slow: 2500 and Fast: 2500
Average time: 70
Wei...
193
Slow Instance Fast Instance
100 ms 10 ms1 000 requests
Round Robin Rule
Slow: 500 and Fast: 500
Average time: 70
Weigh...
194
Slow Instance Fast Instance
100 ms 10 ms1 000 requests
Round Robin Rule
Slow: 500 and Fast: 500
Average time: 70
Weigh...
195
Slow Instance Fast Instance
100 ms 10 ms1 000 requests
Round Robin Rule
Slow: 500 and Fast: 500
Average time: 70
Weigh...
test-service: #service name
ribbon: #namespace
ServerWeightTaskTimerInterval: 30000 #ms
Давайте разбираться
196
test-service: #service name
ribbon: #namespace
ServerWeightTaskTimerInterval: 30000 #ms
Weights [0.0, 0.0]
Давайте разбира...
test-service: #service name
ribbon: #namespace
ServerWeightTaskTimerInterval: 1000 #ms
Давайте разбираться
198
test-service: #service name
ribbon: #namespace
ServerWeightTaskTimerInterval: 1000 #ms
Weights [12.285123016785462, 120.30...
Demo#8 WeightedResponseRule
200
IRule
RoundRobin
ZoneAvoidance
Weighted Random
BestAvailability AvailabilityFiltering
201
Best Available Rule LoadBalancerStats
calculate
chosen -> activeConnections == min(activeConnections)
202
Best Available Rule LoadBalancerStats
calculate
chosen -> activeConnections == min(activeConnections)
203
Меньше активных ...
204
Slow Instance Fast Instance
100 ms 10 ms1 000 requests
Round Robin Rule
Slow: 500 and Fast: 500
Average time: 70
Weigh...
Demo#9 BestAvailableRule
205
IRule
RoundRobin
ZoneAvoidance
Weighted Random
BestAvailability AvailabilityFiltering
206
Availability Filtering
Rule
LoadBalancerStats
calculate
filtered -> activeConnections < maxActiveConnections
filtered -> !...
IRule
RoundRobin
ZoneAvoidance
Weighted Random
BestAvailability AvailabilityFiltering
208
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
209
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
CompositePredicateBuilder
int minimal...
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
CompositePredicateBuilder
int minimal...
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
CompositePredicateBuilder
int minimal...
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
CompositePredicateBuilder
int minimal...
Zone Avoidance Rule Composite Predicate
filter
Zone Predicate Availability Predicate
next
214
triggeringLoadPerServerThreshold: 0.2
avoidZoneWithBlackoutPercetage: 0.99999d
worstZone -> loadPerServer == max(loadPerSe...
triggeringLoadPerServerThreshold: 0.2
avoidZoneWithBlackoutPercetage: 0.99999d
zoneToAvoid -> circuitBreakerTrippedCount /...
Demo#10 ZoneAvoidanceRule
217
Правила
• Ещё больше увеличивают
шансы найти «хороший»
инстанс
• По-умолчанию «хитрые»
правила не используются
218
Default Rule • Почему ZoneAvoidance?
219
Best Availability
• Не всегда предсказуем
• Использовать с осторожностью
220
Speculative Retry
• Надёжен в эксплуатации
• Все операции должны быть
идемпотентными
• Количество вызовов x2
221
Client
call#1
S1 S2 S3 S4 S5
222
call#2
Speculative retry
Client
call#1
S1 S2 S3 S4 S5
223
call#2
Speculative retry
224
Client
S1 S2 S3 S4 S5
Speculative retry
P = p2 = 1 - (1 - 0.99)2 ≈ 0.9999
Speculative Retry
• Надёжен в эксплуатации
• Все операции должны быть
идемпотентными
• Количество вызовов x2
225
Gateway
• Server-Side Discovery Pattern
• Netflix Zuul
226
Server-Side Service Discovery
227
228
Netflix Zuul
229
Netflix Zuul
Demo#11
Zuul
Без регистрации и смс
230
Gateway
• Можно получить «из коробки»
• Zuul может уступить место
spring-cloud-gateway
231
Выводы#1
• Облака, контейнеры,
динамическая привязка
ресурсов приводят к
необходимости решения
задачи обнаружения сервисов...
Выводы#2
• В Spring Cloud-е много готовых
решений, но их использование
будет отличаться в
зависимости от выбранного
реестр...
Выводы#3
• От нас многое прячут в недрах
конфигурации, и если
покопаться, то можно найти
интересные возможности
• Изучение...
Выводы#4
• Выбирайте внешнюю
регистрацию сервисов, если
она идёт из коробки
• Саморегистрация имеет
подводные камни, на ко...
Выводы#5
• API Gateway можно сделать
парой аннотаций за счёт
тесной интеграции с Ribbon и
DiscoveryClient
236
Ссылки на библиотеки
237
Spring Cloud (Service Registry)
https://github.com/spring-cloud/spring-cloud-netflix
https://gith...
Почитать
238
Service Discovery: More Than It Seems (part 1 and part2)
https://dzone.com/users/1298451/aatarasoff.html
Попробовать Demo
239
https://github.com/aatarasoff/spring-cloud-service-discovery-demo
240
Upcoming SlideShare
Loading in …5
×

Service Discovery. More that it seems

There is a problem of finding the best instance of a service in distributed systems with dynamic configuration. Nowadays, there are many products for the configuration storage and service discovery. It should be mentioned at least Netflix Eureka, Consul, etc or good old Zookeeper. These products can keep and give configuration, manage service instances lifecycle and some of them even can be as dynamic DNS service. But main question is not about what instance may be called at the certain time. It is about what instance is better for call? This means that smart load balancing top on service discovery is required. Spring Cloud project allows to integrate these products to your project and provides powerful solutions for typical problems, that make cloud native services developing easier. This talk will review the internal structure of SpringCloud implementation of Client-Side Service Discovery and Client Load Balancing patterns. It also will include specific details of concrete implementations with examples from official libraries and the author’s own library.

  • Be the first to comment

Service Discovery. More that it seems

  1. 1. Service Discovery Больше, чем кажется
  2. 2. 2
  3. 3. 3
  4. 4. 4 Мнение докладчика может не совпадать с официальной позицией его работодателя, начальника, коллег или других специалистов. Доклад не связан с инженерными решениями в Одноклассниках и базируется на проектах и библиотеках с открытым исходным кодом, личном опыте автора по их использованию или созданию. Все представленные в докладе сведения, примеры, выводы и другую информацию вы можете использовать на свой страх и риск. За все ваши действия ответственность несёте только вы сами. Все персонажи вымышлены, совпадения случайны.
  5. 5. Про Одноклассники 5 Олег Анастасьев Распределенные системы в Одноклассниках https://www.youtube.com/watch?v=yRYYR13ymik Никита Духовный Балансировка нагрузки и отказоустойчивость в Одноклассниках https://ok.ru/video/352920275667
  6. 6. A что будет? 240 слайдов 12 демо 6
  7. 7. План такой 1. Новая реальность – новые проблемы 2. Что такое Service Discovery и с чем его едят 3. Как это сделано в Spring Cloud на примерах 7
  8. 8. А в чём именно проблема? 8
  9. 9. Database JavaEE Cluster Client 9 Так было когда-то
  10. 10. Big App Static resource Database Remote EJB 10 Ресурсы и их типы в прошлом
  11. 11. Как было? • Количество ресурсов мало • Хост и порт, как правило, статичны • Конфигурация менялась редко 11
  12. 12. insert into resources_table values (key, endpoint) 12 Классическое решение проблемы
  13. 13. 13 Ещё одно решение <providers> <provider id="provider1" url="socket://10.0.1.1:8080/?weight="42"/> <provider id="provider2" url="socket://10.0.1.2:8080/?weight="100"/> </providers>
  14. 14. API API instance API instance API instance 14 Путь к микросервисам
  15. 15. Some API Another API Instance 1 Another API Instance 2 Another API Instance 3 ? 15 Кого же вызвать?
  16. 16. Статическая конфигурация 16 <providers> <provider id="provider{{ p.number }}" url="socket://10.0.1.{{ p.number }}:8080/?weight="100"/> </providers>
  17. 17. После набега хипстеров • Контейнеры, docker, облака 17
  18. 18. { "id": "/hippo/rent-api", "cpus": 1, "mem": 1024, "instances": 42, "container": { "docker": { "image": "docker/hippo-rent-api:1.17.0", "portMappings": [ { "containerPort": 8080, "servicePort": 0 } ] } } } Динамическая конфигурация (PAAS) 18
  19. 19. { "id": "/hippo/rent-api", "cpus": 1, "mem": 1024, "instances": 42, "container": { "docker": { "image": "docker/hippo-rent-api:1.17.0", "portMappings": [ { "containerPort": 8080, "servicePort": 0 } ] } } } Динамическая конфигурация (PAAS) 19
  20. 20. После набега хипстеров • Хост и порт, как правило, заранее неизвестны (ip-per-instance делает статическим порт) • Конфигурация меняется постоянно, потому что continuous delivery и прочий DevOps 20
  21. 21. Service Discovery • Какой инстанс мы можем вызвать? 21
  22. 22. 22 Сервис Фото Простая задача Сервис Группы Связи Группа- Пользователей Сервис Пользователи Сервис Альбомы Группы Обложка для фотоальбома
  23. 23. 5 parallel service calls 23 Простая задача
  24. 24. Client call S1 S2 S3 S4 S5 24
  25. 25. Client call S1 S2 S3 S4 S5 25
  26. 26. p = 0.99* 26 Простая статистика * 0.99 не имеет отношения к реальности и взята для упрощения
  27. 27. P = p5 = (0.99)5 ≈ 0.95 p – вероятность успеха вызова одного сервиса 27 Получается не очень
  28. 28. Service Discovery • Какой инстанс мы можем вызвать? • Какой инстанс ЛУЧШЕ подходит для вызова? 28
  29. 29. P = p5 = (0.999)5 ≈ 0.995 p – вероятность успеха вызова одного сервиса 29 Хотим как-то так
  30. 30. Service Discovery • Какой инстанс мы можем вызвать? • Какой инстанс ЛУЧШЕ подходит для вызова? • Как мы можем увеличить вероятность успеха одиночного вызова? 30
  31. 31. S1 S2 S3 S4 S5 31
  32. 32. STATE S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 32
  33. 33. STATE FILTER S1 S2 S4 S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 33
  34. 34. STATE FILTER S2 S4Other DC S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 34 S1
  35. 35. STATE FILTER S2 S4 S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 ? 35 S1
  36. 36. Service Service Service Service Service STATE FILTER S2 S4 RULE S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 36 S1 S4
  37. 37. Service Service Service Service Service STATE FILTER S2 S4 RULE Client S1 S2 S3 S4 S5 call S1 S2 S3 S4 S5 37 S4
  38. 38. Service Service Service Service Service STATE FILTER S2 S4 RULE Client S1 S2 S3 S4 S5 call S1 S2 S3 S4 S5 38 S4 P
  39. 39. S1 S2 S3 S4 S5 ? 39
  40. 40. Service Registry • Database • Properties File • Environment variables 40
  41. 41. Service Registry • Database • Properties File • Environment variables 41
  42. 42. Service Registry • Netflix Eureka • Consul • etcd • Zookeeper • … 42
  43. 43. Service A Service Registry Service B Service C register 43
  44. 44. Service A Service B Service C health check 44 Service Registry
  45. 45. Service A Service B Service C heartbeat 45 Service Registry
  46. 46. Service A Service B Service C health check 46 Service Registry
  47. 47. Service A Service B Service C health check 47 Service Registry
  48. 48. Service A Service B Service C deregister 48 Service Registry
  49. 49. Service A Service B Service C deregister graceful shutdown 49 Service Registry
  50. 50. Leader Master Master Leader-Election Quorum (n + 1)/2 Distributed Locks 50
  51. 51. Client/Slave Leader Master Master Client/Slave Client/Slave horizontal scaling 51
  52. 52. Service Registry • Netflix Eureka, Consul, etcd, Zookeeper • ??? 52
  53. 53. { "id": "/hippo/rent-api", "cpus": 1, "mem": 1024, "instances": 42, "container": { "docker": { "image": "docker/hippo-rent-api:1.17.0", "portMappings": [ { "containerPort": 8080, "servicePort": 0 } ] } } } Динамическая конфигурация (PAAS) 53
  54. 54. 54 Marathon Marathon Marathon Mesos Master Mesos Master Mesos Master Mesos Slave Mesos Slave ZK ZK ZK
  55. 55. 55 Marathon Marathon Marathon Mesos Master Mesos Master Mesos Master Mesos Slave Mesos Slave Docker Docker ZK ZK ZK
  56. 56. 56
  57. 57. curl http://marathon.master/v2/apps/{appId} 57 Получаем данные
  58. 58. 58 "tasks": [ { "appId": "/test-marathon-app", "host": "mesos-slave.dc2", "id": "test-marathon-app.43ed8a70-…-0242ac110006", "ipAddresses": [ { "ipAddress": "172.17.0.10", "protocol": "IPv4" }], "ports": [11786], "state": "TASK_RUNNING", "healthCheckResults": [{"alive": true}] }, { ... } ]
  59. 59. 59 "tasks": [ { "appId": "/test-marathon-app", "host": "mesos-slave.dc2", "id": "test-marathon-app.43ed8a70-…-0242ac110006", "ipAddresses": [ { "ipAddress": "172.17.0.10", "protocol": "IPv4" }], "ports": [11786], "state": "TASK_RUNNING", "healthCheckResults": [{"alive": true}] }, { ... } ]
  60. 60. 60 "tasks": [ { "appId": "/test-marathon-app", "host": "mesos-slave.dc2", "id": "test-marathon-app.43ed8a70-…-0242ac110006", "ipAddresses": [ { "ipAddress": "172.17.0.10", "protocol": "IPv4" }], "ports": [11786], "state": "TASK_RUNNING", "healthCheckResults": [{"alive": true}] }, { ... } ]
  61. 61. 61 "tasks": [ { "appId": "/test-marathon-app", "host": "mesos-slave.dc2", "id": "test-marathon-app.43ed8a70-…-0242ac110006", "ipAddresses": [ { "ipAddress": "172.17.0.10", "protocol": "IPv4" }], "ports": [11786], "state": "TASK_RUNNING", "healthCheckResults": [{"alive": true}] }, { ... } ]
  62. 62. “Service Registry” • Netflix Eureka, Consul, etcd, Zookeeper • Kubernetes • Mesos+Marathon • DCOS • Own solution 62
  63. 63. Demo#0 Введение в Marathon 63
  64. 64. Spring Cloud • Фреймворк для разработки распределённых систем • Реализует основные паттерны • Имеет коннекторы к различным третьим решениям 64
  65. 65. Client-Side Service Discovery 65
  66. 66. ... @EnableDiscoveryClient ... public class Application { @Autowired private DiscoveryClient discoveryClient; public List<String> services() { return discoveryClient.getServices(); } } 66 Одна аннотация спасет мир
  67. 67. ... @EnableDiscoveryClient ... public class Application { @Autowired private DiscoveryClient discoveryClient; public List<String> services() { return discoveryClient.getServices(); } } 67 Одна аннотация спасет мир
  68. 68. public interface DiscoveryClient { String description(); ServiceInstance getLocalServiceInstance(); List<ServiceInstance> getInstances(String serviceId); List<String> getServices(); } 68 Рекомендуемый интерфейс
  69. 69. 69 Рекомендуемый интерфейс https://www.safaribooksonline.com/library/view/cloud-native-java/9781449374631/ch16.html Spring Cloud provides the DiscoveryClient abstraction to make it easy for clients to work with different types of service registries. Spring Cloud plugs the DiscoveryClient abstraction into various parts of the stack, making its use almost transparent. The abstraction is simple enough that you could adapt Spring Cloud to work with another service registry if you’d like to. Conceptually, the DiscoveryClient is read-only.
  70. 70. public interface DiscoveryClient { String description(); @Deprecated ServiceInstance getLocalServiceInstance(); List<ServiceInstance> getInstances(String serviceId); List<String> getServices(); } 70 Рекомендуемый интерфейс
  71. 71. public interface DiscoveryClient { String description(); ServiceInstance getLocalServiceInstance(); List<ServiceInstance> getInstances(String serviceId); List<String> getServices(); } 71 Рекомендуемый интерфейс
  72. 72. public interface DiscoveryClient { String description(); ServiceInstance getLocalServiceInstance(); List<ServiceInstance> getInstances(String serviceId); List<String> getServices(); } 72 Рекомендуемый интерфейс
  73. 73. ... @EnableDiscoveryClient ... public class Application { @Autowired private DiscoveryClient discoveryClient; public List<String> services() { return discoveryClient.getServices(); } } 73 Одна аннотация спасет мир
  74. 74. Spring Cloud Core Eureka Consul Marathon 74
  75. 75. Spring Cloud Core Eureka Consul Marathon Reference/Native implementation Most complex load balancing 75
  76. 76. Spring Cloud Core Eureka Consul Marathon Out of the box PaaS 76
  77. 77. @Override public List<ServiceInstance> getInstances(String serviceId) { return client.getAppTasks(ServiceIdConverter.convertToMarathonId(serviceId)) .getTasks() .stream() .filter(task -> null == task.getHealthCheckResults() || task.getHealthCheckResults() .stream() .allMatch(HealthCheckResult::isAlive) ) .map(task -> new DefaultServiceInstance( ServiceIdConverter.convertToServiceId(task.getAppId()), task.getHost(), task.getPorts().stream().findFirst().orElse(0), false )) .collect(Collectors.toList()); } 77 Marathon. Имплементация
  78. 78. @Override public List<ServiceInstance> getInstances(String serviceId) { return client.getAppTasks(ServiceIdConverter.convertToMarathonId(serviceId)) .getTasks() .stream() .filter(task -> null == task.getHealthCheckResults() || task.getHealthCheckResults() .stream() .allMatch(HealthCheckResult::isAlive) ) .map(task -> new DefaultServiceInstance( ServiceIdConverter.convertToServiceId(task.getAppId()), task.getHost(), task.getPorts().stream().findFirst().orElse(0), false )) .collect(Collectors.toList()); } 78 Marathon. Имплементация
  79. 79. @Override public List<ServiceInstance> getInstances(String serviceId) { return client.getAppTasks(ServiceIdConverter.convertToMarathonId(serviceId)) .getTasks() .stream() .filter(task -> null == task.getHealthCheckResults() || task.getHealthCheckResults() .stream() .allMatch(HealthCheckResult::isAlive) ) .map(task -> new DefaultServiceInstance( ServiceIdConverter.convertToServiceId(task.getAppId()), task.getHost(), task.getPorts().stream().findFirst().orElse(0), false )) .collect(Collectors.toList()); } 79 Marathon. Имплементация
  80. 80. @Override public List<ServiceInstance> getInstances(String serviceId) { return client.getAppTasks(ServiceIdConverter.convertToMarathonId(serviceId)) .getTasks() .stream() .filter(task -> null == task.getHealthCheckResults() || task.getHealthCheckResults() .stream() .allMatch(HealthCheckResult::isAlive) ) .map(task -> new DefaultServiceInstance( ServiceIdConverter.convertToServiceId(task.getAppId()), task.getHost(), task.getPorts().stream().findFirst().orElse(0), false )) .collect(Collectors.toList()); } 80 Marathon. Имплементация
  81. 81. @Override public List<ServiceInstance> getInstances(String serviceId) { return client.getAppTasks(ServiceIdConverter.convertToMarathonId(serviceId)) .getTasks() .stream() .filter(task -> null == task.getHealthCheckResults() || task.getHealthCheckResults() .stream() .allMatch(HealthCheckResult::isAlive) ) .map(task -> new DefaultServiceInstance( ServiceIdConverter.convertToServiceId(task.getAppId()), task.getHost(), task.getPorts().stream().findFirst().orElse(0), false )) .collect(Collectors.toList()); } 81 Marathon. Имплементация
  82. 82. @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/instances") public List<ServiceInstance> instances() { return discoveryClient.getInstances("someservice"); } 82 Marathon. Использование
  83. 83. Demo#1 Наивная реализация DiscoveryClient 83
  84. 84. Service Service Service Service Service S2 S4 S2 LoadBalancerClient S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 New Service register/deregister 84
  85. 85. Шаблоны регистрации • 3d-party • Self-registration 85
  86. 86. Docker Engine 86 3d-party registration pattern
  87. 87. Docker Engine Service register/deregister 87 3d-party registration pattern
  88. 88. Docker Engine Service register/deregister 88 Registrator emit event 3d-party registration pattern
  89. 89. Docker Engine Service register/deregister 89 Registrator emit event Service Registry native event 3d-party registration pattern
  90. 90. Service Definition 90 deploy manifest PaaS Нативная регистрация (PaaS)
  91. 91. Service Definition 91 deploy manifest PaaS Нативная регистрация (PaaS) S1 S2 S3 S4 S5 run services
  92. 92. Self-Registration • Реализация (как всегда) зависит от выбранного реестра сервисов • Не нужна для PaaS-решений 92
  93. 93. SmartLifecycle DiscoveryLifecycle EurekaDiscoveryClientConfiguration ConsulLifecycle 93
  94. 94. SmartLifecycle DiscoveryLifecycle EurekaDiscoveryClientConfiguration ConsulLifecycle 94 @Deprecated
  95. 95. AutoServiceRegistration AbstractServiceRegistration Eureka. No implementation ConsulLifecycle 95
  96. 96. DiscoveryLifecycle/ AutoServiceRegistration LifecycleProcessor autoStartup on refresh startBeans on start Service Registry register 96
  97. 97. DiscoveryLifecycle/ AutoServiceRegistration LifecycleProcessor autoStartup on refresh startBeans on start Service Registry register 97 emit InstanceRegistered Event DiscoveryClientHealthIndicator
  98. 98. LifecycleProcessor on RefreshScope event stopBeans on stop Service Registry deregister 98 DiscoveryLifecycle/ AutoServiceRegistration
  99. 99. DiscoveryLifecycle/ AutoServiceRegistration LifecycleProcessor on RefreshScope event stopBeans on stop Service Registry deregister close or shutdown Real service discovery client 99
  100. 100. Контейнеры • Docker etc. 100
  101. 101. Docker container Internal IP External IP 101 ?
  102. 102. > ifconfig lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280stf0: flags=0<> mtu 1280 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 en1: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 en2: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304 awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484 bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500 102
  103. 103. > ifconfig lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280stf0: flags=0<> mtu 1280 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 en1: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 en2: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304 awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484 bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500 Low index 103
  104. 104. spring: cloud: discovery: lifecycle: enabled: false 104 External Registrator
  105. 105. spring: cloud: inetutils: ignored-interfaces: - vbox* - bridge* - lo* 105
  106. 106. spring: cloud: inetutils: ignored-interfaces: - vbox* - bridge* - lo* 106
  107. 107. spring: cloud: inetutils: preferred-interfaces: - eth0 107
  108. 108. spring: cloud: discovery: preferIpAddress: true (false) 108
  109. 109. Шаблоны регистрации • Выбирайте внешнюю регистрацию сервисов, если она идёт из коробки • Саморегистрация имеет подводные камни, на которые можно натолкнуться 109
  110. 110. Endpoints • Предоставляют доступ к технической информации: метрики, environment, прокси • Не только чтение, но и запись 110
  111. 111. http some.service/metrics 111 Метрики
  112. 112. http some.service/env 112 Environment. Чтение
  113. 113. http -v --form POST host:port/env PROP=VALUE 113 Environment. Запись
  114. 114. http some.service/marathon 114 Прокси
  115. 115. Demo#2 Индикаторы и эндпоиты 115
  116. 116. spring: cloud: marathon: listOfServers: m1:8080,m2:8080,m3:8080 username: marathon password: mesos 116 Marathon
  117. 117. spring: cloud: consul: host: localhost port: 8500 117 Но вот в Consul-е иначе На каждом хосту есть consul-агент
  118. 118. spring: cloud: marathon: listOfServers: m1:8080,m2:8080,m3:8080 username: marathon password: mesos 118 Marathon. Авторизация#1
  119. 119. spring: cloud: marathon: listOfServers: m1:8080,m2:8080,m3:8080 token: <api_token> 119 Marathon. Авторизация#2
  120. 120. Marathon AppId: /group/path/app Spring Cloud serviceId: group.path.app 120 Marathon. Маппинг
  121. 121. Marathon AppId: /group/path/app Spring Cloud serviceId: group.path.app 121 Marathon. Маппинг
  122. 122. Marathon AppId: /group/path/app Spring Cloud serviceId: group.path.app 122 Невалидный hostname
  123. 123. Боль реализации • Разные конфигурации подключения к реестрам сервисов • Специфические фичи, зависящие от выбора реестра сервисов 123
  124. 124. Demo#3 Отказоустойчивый DiscoveryClient 124
  125. 125. Вся правда о DiscoveryClient • Не используется для клиентской балансировки • Участвует в формировании гиперссылок, в health-check эндпоинтах и в получении списка сервисов в реализации edge-сервера 125
  126. 126. LoadBalancer • Основан на Netflix Ribbon • В принципе может быть использован вне стека Spring Cloud • В общем случае не нуждается в Service Registry 126
  127. 127. Service Service Service Service Service STATE FILTER S2 S4 RULE S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 127 Client call S4
  128. 128. Service Service Service Service Service S2 S4 S1 S2 S3 S4 S5 S1 S2 S3 S4 S5 ServerList 128 Client call S4
  129. 129. Ribbon ServerList • Static • Configuration-based • Discovery-based 129
  130. 130. test-service: #service name ribbon: #namespace listOfServers: host:8080,anotherHost:8081 ConfigurationBasedServerList 130
  131. 131. @Autowired private LoadBalancerClient loadBalancer; @RequestMapping("/url") public String realUrl() throws IOException { return loadBalancer.reconstructURI( instance, new URI("http://"+ serviceId +"/me") ).toString(); } Будет заменено реальным хостом и портом 131
  132. 132. @Autowired private LoadBalancerClient loadBalancer; @RequestMapping("/url") public String realUrl() throws IOException { return loadBalancer.reconstructURI( instance, new URI("http://"+ serviceId +"/me") ).toString(); } > curl service:8080/url http://{host,anotherHost}:8080/me 132
  133. 133. DiscoveryBasedServerList 133 List<MarathonServer> extractServiceInstances(App app) { return app.getTasks() .stream() .map(task -> { Collection<HealthCheckResult> healthChecks = null != task.getHealthCheckResults() ? task.getHealthCheckResults() : new ArrayList<>(); return new MarathonServer( task.getHost(), task.getPorts().stream().findFirst().orElse(0), healthChecks ).withZone(extractZoneFromHostname(task.getHost())); }) .collect(Collectors.toList()); }
  134. 134. DiscoveryBasedServerList 134 List<MarathonServer> extractServiceInstances(App app) { return app.getTasks() .stream() .map(task -> { Collection<HealthCheckResult> healthChecks = null != task.getHealthCheckResults() ? task.getHealthCheckResults() : new ArrayList<>(); return new MarathonServer( task.getHost(), task.getPorts().stream().findFirst().orElse(0), healthChecks ).withZone(extractZoneFromHostname(task.getHost())); }) .collect(Collectors.toList()); }
  135. 135. DiscoveryBasedServerList 135 List<MarathonServer> extractServiceInstances(App app) { return app.getTasks() .stream() .map(task -> { Collection<HealthCheckResult> healthChecks = null != task.getHealthCheckResults() ? task.getHealthCheckResults() : new ArrayList<>(); return new MarathonServer( task.getHost(), task.getPorts().stream().findFirst().orElse(0), healthChecks ).withZone(extractZoneFromHostname(task.getHost())); }) .collect(Collectors.toList()); }
  136. 136. DiscoveryBasedServerList 136 List<MarathonServer> extractServiceInstances(App app) { return app.getTasks() .stream() .map(task -> { Collection<HealthCheckResult> healthChecks = null != task.getHealthCheckResults() ? task.getHealthCheckResults() : new ArrayList<>(); return new MarathonServer( task.getHost(), task.getPorts().stream().findFirst().orElse(0), healthChecks ).withZone(extractZoneFromHostname(task.getHost())); }) .collect(Collectors.toList()); }
  137. 137. Ribbon Server vs Service Instance 137 Service InstanсeRibbon Server Разное представление одного и того же
  138. 138. Demo#4 Переходим на Ribbon 138
  139. 139. Marathon. Ribbon-фишки 139 customer-service: ribbon: <your settings here> ZonePattern: '.+.(.+)' # host.zone1 -> zone1 MetaDataFilter: API_VERSION: '!=V3' # not equal to V3 ENVIRONMENT: '==DEV' # equal to DEV; CUSTOMER_ENTITY: V1 # equal to V1; default is equals
  140. 140. Marathon. Ribbon-фишки 140 customer-service: ribbon: <your settings here> ZonePattern: '.+.(.+)' # host.zone1 -> zone1 MetaDataFilter: API_VERSION: '!=V3' # not equal to V3 ENVIRONMENT: '==DEV' # equal to DEV; CUSTOMER_ENTITY: V1 # equal to V1; default is equals
  141. 141. Demo#5 Zones and queries 141
  142. 142. А жив ли мой сервис? • Что значит ”жив”? • Как проверить живучесть? 142
  143. 143. S1 S2 S3 S4 S5 S2 S4 S1 S2 S3 S4 S5? 143 Server List Load Balancer Client S4
  144. 144. t t + 1 S1 S2 S3 144
  145. 145. t t + 1 S1 S2 S3 ServerList S1 S2 S3 145
  146. 146. t t + 1 S1 S2 S3 ServerList S1S1 S2 S3 146
  147. 147. t t + 1 S1 S2 S3 ServerList S1 S1S1 S2 S3 147
  148. 148. t t + 1 S1 S2 S3 ServerList S1 S1 t + 2 S1 S2 S3 148
  149. 149. S1 S2 S3 S4 S5 S2 S4 S1 S2 S3 S4 S5Ping -> 149 Server List Load Balancer Client S4
  150. 150. Pinger (background thread) LoadBalancer background task force if setServerList() is called 150
  151. 151. Pinger (background thread) LoadBalancer background task force if setServerList() is called IPingStrategy pingServers 151
  152. 152. Pinger (background thread) LoadBalancer background task force if setServerList() is called IPingStrategy pingServers isAlive(server) IPing 152
  153. 153. IPingStrategy SerialPingStrategy int numCandidates = servers.length; boolean[] results = new boolean[numCandidates]; for (int i = 0; i < numCandidates; i++) { results[i] = false; /* Default answer is DEAD. */ if (ping != null) { results[i] = ping.isAlive(servers[i]); } } return results; 153
  154. 154. IPingStrategy SerialPingStrategy int numCandidates = servers.length; boolean[] results = new boolean[numCandidates]; for (int i = 0; i < numCandidates; i++) { results[i] = false; /* Default answer is DEAD. */ if (ping != null) { results[i] = ping.isAlive(servers[i]); } } return results; 154
  155. 155. IPingStrategy SerialPingStrategy int numCandidates = servers.length; boolean[] results = new boolean[numCandidates]; for (int i = 0; i < numCandidates; i++) { results[i] = false; /* Default answer is DEAD. */ if (ping != null) { results[i] = ping.isAlive(servers[i]); } } return results; 155
  156. 156. IPingStrategy SerialPingStrategy int numCandidates = servers.length; boolean[] results = new boolean[numCandidates]; for (int i = 0; i < numCandidates; i++) { results[i] = false; /* Default answer is DEAD. */ if (ping != null) { results[i] = ping.isAlive(servers[i]); } } return results; 156 И что делать, если пинг слишком долгий?
  157. 157. NIWSDiscoveryPing (Eureka) IPing instance.status.equals( InstanceStatus.UP ) ConsulPing (Consul) MarathonPing (Marathon) server.isPassingChecks() server.isPassingChecks() Кругом один обман  157
  158. 158. IPingStrategy ? Есть честная имплементация, но нет готовых её использований IPing PingUrl public boolean isAlive(Server server) { URL url = constructUrl(server); HttpResponse response = httpClient.execute(createReq(url)); return response.getStatusLine().getStatusCode() == 200; } 158
  159. 159. А жив ли мой сервис? • Проверять на уровне приложения дорого • Доверяем тому, что приходит из реестра сервисов 159
  160. 160. Demo#6 ping, ping, ping 160
  161. 161. Отсеять плохое • Фильтры и их применение 161
  162. 162. S1 S2 S3 S4 S5 S2 S4 S1 S2 S3 S4 S5 Server List Filter Chain -> 162 Load Balancer Client S4
  163. 163. Cluster (Zone 1) S1 S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) 163
  164. 164. Cluster (Zone 1) S1 S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) cheap call 164
  165. 165. Cluster (Zone 1) S1 S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) expensive call 165
  166. 166. Cluster (Zone 1) S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) unpredictable S1 166
  167. 167. Cluster (Zone 1) S1 S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) more guaranteed result 167
  168. 168. ZoneAffinityFilter getFilteredServers(servers) LoadBalancerStats getZoneSnaphot(servers) 168 DynamicServerListLoadBalancer
  169. 169. S1 S2 S3 S4 S5 S2 S4 S1 S2 S3 S4 S5LB Stats -> Server List Filter Chain 169 Load Balancer Client S4 Server Stats
  170. 170. S1 S2 S3 Zone Snapshot Server Stats 170
  171. 171. S1 S2 S3 Zone Snapshot activeConnections activeServers circuitBreakerTrippedCount 171
  172. 172. ZoneAffinityFilter LoadBalancerStats enableZoneAffinity? zoneAffinity: maxLoadPerServer: 0.6 maxBlackOutServersPercentage: 0.8 minAvailableServers: 2 172
  173. 173. ZoneAffinityFilter ZoneAffinityPredicate исключаем инстансы из других зон 173 zoneAffinity: maxLoadPerServer: 0.6 maxBlackOutServersPercentage: 0.8 minAvailableServers: 2
  174. 174. Cluster (Zone 1) S1 S2 S3 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) cheap call 174
  175. 175. ZoneAffinityFilter ZoneAffinityPredicate не фильтруем 175 zoneAffinity: maxLoadPerServer: 0.6 maxBlackOutServersPercentage: 0.8 minAvailableServers: 2
  176. 176. Cluster (Zone 1) S1 Cluster (Zone 2) S4 S5 S6 Client (Zone 1) 176 S2 S3
  177. 177. ZonePreferenceFilter List servers = super.getFilteredServers(); List local = servers.filter(server -> server.zone == zoneFromConfig ); if (!local.isEmpty()) { return local; } return servers; super.getFilteredServers() DynamicServerListLoadBalancer 177 default (local) zone ZoneAffinityFilter
  178. 178. ZonePreferenceFilter List servers = super.getFilteredServers(); List local = servers.filter(server -> server.zone == zoneFromConfig ); if (!local.isEmpty()) { return local; } return servers; super.getFilteredServers() DynamicServerListLoadBalancer 178 ZoneAffinityFilter
  179. 179. Цепочка фильтров • Помогает увеличить шансы на корректное исполнение запроса • Отсеивает «плохие» и «дорогие» инстансы 179
  180. 180. Demo#7 Пишем свой фильтр 180
  181. 181. Правило выбора • Последняя миля 181
  182. 182. S1 S2 S3 S4 S5 S2 S4 S1 S2 S3 S4 S5 ? 182 Server List Filter Chain Rule Load Balancer Client S4
  183. 183. IRule RoundRobin ZoneAvoidance default Weighted Random BestAvailability AvailabilityFilter 183
  184. 184. IRule RoundRobin ZoneAvoidance Weighted Random BestAvailability AvailabilityFiltering 184
  185. 185. upstream test-marathon-app { server host1 weight=3; server host2 weight=1; server host3 weight=1; } Как правило вес предопределен 185 Типичный конфиг с весами
  186. 186. Weight Task (background thread) Weighted Rule background task LoadBalancerStats calculate serverWeight = summ(avgServerResponseTime)- avgServerResponseTime 186
  187. 187. Weight Task (background thread) Weighted Rule background task LoadBalancerStats calculate serverWeight = summ(avgServerResponseTime)- avgServerResponseTime 187 Больше время ответа – меньше вес
  188. 188. 188 Slow Instance Fast Instance 100 ms 10 ms
  189. 189. 189 Slow Instance Fast Instance 100 ms 10 ms5 000 requests
  190. 190. 190 Slow Instance Fast Instance 100 ms 10 ms5 000 requests Round Robin Rule Slow: 2500 and Fast: 2500 Average time: 70
  191. 191. 191 Slow Instance Fast Instance 100 ms 10 ms5 000 requests Round Robin Rule Slow: 2500 and Fast: 2500 Average time: 70
  192. 192. 192 Slow Instance Fast Instance 100 ms 10 ms5 000 requests Round Robin Rule Slow: 2500 and Fast: 2500 Average time: 70 Weighted Rule Slow: 634 and Fast: 4366 Average time: 27
  193. 193. 193 Slow Instance Fast Instance 100 ms 10 ms1 000 requests Round Robin Rule Slow: 500 and Fast: 500 Average time: 70 Weighted Rule ? ?
  194. 194. 194 Slow Instance Fast Instance 100 ms 10 ms1 000 requests Round Robin Rule Slow: 500 and Fast: 500 Average time: 70 Weighted Rule Slow: 500 and Fast: 500 Average time: 72
  195. 195. 195 Slow Instance Fast Instance 100 ms 10 ms1 000 requests Round Robin Rule Slow: 500 and Fast: 500 Average time: 70 Weighted Rule Slow: 500 and Fast: 500 Average time: 72
  196. 196. test-service: #service name ribbon: #namespace ServerWeightTaskTimerInterval: 30000 #ms Давайте разбираться 196
  197. 197. test-service: #service name ribbon: #namespace ServerWeightTaskTimerInterval: 30000 #ms Weights [0.0, 0.0] Давайте разбираться 197
  198. 198. test-service: #service name ribbon: #namespace ServerWeightTaskTimerInterval: 1000 #ms Давайте разбираться 198
  199. 199. test-service: #service name ribbon: #namespace ServerWeightTaskTimerInterval: 1000 #ms Weights [12.285123016785462, 120.30885719400065] Давайте разбираться 199
  200. 200. Demo#8 WeightedResponseRule 200
  201. 201. IRule RoundRobin ZoneAvoidance Weighted Random BestAvailability AvailabilityFiltering 201
  202. 202. Best Available Rule LoadBalancerStats calculate chosen -> activeConnections == min(activeConnections) 202
  203. 203. Best Available Rule LoadBalancerStats calculate chosen -> activeConnections == min(activeConnections) 203 Меньше активных соединений -> больше шансов на успех
  204. 204. 204 Slow Instance Fast Instance 100 ms 10 ms1 000 requests Round Robin Rule Slow: 500 and Fast: 500 Average time: 70 Weighted Rule Slow: 500 and Fast: 500 Average time: 72 Best Available Rule Slow: 152 and Fast: 847 Average time: 38
  205. 205. Demo#9 BestAvailableRule 205
  206. 206. IRule RoundRobin ZoneAvoidance Weighted Random BestAvailability AvailabilityFiltering 206
  207. 207. Availability Filtering Rule LoadBalancerStats calculate filtered -> activeConnections < maxActiveConnections filtered -> !isCircuitBreakerTripped Availability Predicate filter 207 Почти как round-robin
  208. 208. IRule RoundRobin ZoneAvoidance Weighted Random BestAvailability AvailabilityFiltering 208
  209. 209. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate 209
  210. 210. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate CompositePredicateBuilder int minimalFilteredServers = 1 float minimalFilteredPercentage = 0 predicate.filter(servers) 210
  211. 211. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate CompositePredicateBuilder int minimalFilteredServers = 1 float minimalFilteredPercentage = 0 int count = predicate.filter(servers).size() //2 211
  212. 212. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate CompositePredicateBuilder int minimalFilteredServers = 1 float minimalFilteredPercentage = 0 int count = predicate.filter(servers).size() //2 count >= minimalFilteredServers count >= minimalFilteredPercentage * count 212
  213. 213. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate CompositePredicateBuilder int minimalFilteredServers = 1 float minimalFilteredPercentage = 0 int count = predicate.filter(servers).size() //0 count >= minimalFilteredServers count >= minimalFilteredPercentage * count 213
  214. 214. Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate next 214
  215. 215. triggeringLoadPerServerThreshold: 0.2 avoidZoneWithBlackoutPercetage: 0.99999d worstZone -> loadPerServer == max(loadPerServer) && loadPerServer > triggeringLoadPerServerThreshold zones.remove(worstZone) Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate 215
  216. 216. triggeringLoadPerServerThreshold: 0.2 avoidZoneWithBlackoutPercetage: 0.99999d zoneToAvoid -> circuitBreakerTrippedCount / instanceCount >= avoidZoneWithBlackoutPercetage zones.remove(zoneToAvoid) Zone Avoidance Rule Composite Predicate filter Zone Predicate Availability Predicate 216
  217. 217. Demo#10 ZoneAvoidanceRule 217
  218. 218. Правила • Ещё больше увеличивают шансы найти «хороший» инстанс • По-умолчанию «хитрые» правила не используются 218
  219. 219. Default Rule • Почему ZoneAvoidance? 219
  220. 220. Best Availability • Не всегда предсказуем • Использовать с осторожностью 220
  221. 221. Speculative Retry • Надёжен в эксплуатации • Все операции должны быть идемпотентными • Количество вызовов x2 221
  222. 222. Client call#1 S1 S2 S3 S4 S5 222 call#2 Speculative retry
  223. 223. Client call#1 S1 S2 S3 S4 S5 223 call#2 Speculative retry
  224. 224. 224 Client S1 S2 S3 S4 S5 Speculative retry P = p2 = 1 - (1 - 0.99)2 ≈ 0.9999
  225. 225. Speculative Retry • Надёжен в эксплуатации • Все операции должны быть идемпотентными • Количество вызовов x2 225
  226. 226. Gateway • Server-Side Discovery Pattern • Netflix Zuul 226
  227. 227. Server-Side Service Discovery 227
  228. 228. 228 Netflix Zuul
  229. 229. 229 Netflix Zuul
  230. 230. Demo#11 Zuul Без регистрации и смс 230
  231. 231. Gateway • Можно получить «из коробки» • Zuul может уступить место spring-cloud-gateway 231
  232. 232. Выводы#1 • Облака, контейнеры, динамическая привязка ресурсов приводят к необходимости решения задачи обнаружения сервисов • Но основная цель – это предсказуемая и стабильная работа всей системы в целом 232
  233. 233. Выводы#2 • В Spring Cloud-е много готовых решений, но их использование будет отличаться в зависимости от выбранного реестра сервисов 233
  234. 234. Выводы#3 • От нас многое прячут в недрах конфигурации, и если покопаться, то можно найти интересные возможности • Изучение базовых решений (Netflix Ribbon. и т.д.) даст возможность эффективнее решать задачи 234
  235. 235. Выводы#4 • Выбирайте внешнюю регистрацию сервисов, если она идёт из коробки • Саморегистрация имеет подводные камни, на которые можно натолкнуться 235
  236. 236. Выводы#5 • API Gateway можно сделать парой аннотаций за счёт тесной интеграции с Ribbon и DiscoveryClient 236
  237. 237. Ссылки на библиотеки 237 Spring Cloud (Service Registry) https://github.com/spring-cloud/spring-cloud-netflix https://github.com/spring-cloud/spring-cloud-consul Spring Cloud (Paas) https://github.com/aatarasoff/spring-cloud-marathon https://github.com/fabric8io/spring-cloud-kubernetes
  238. 238. Почитать 238 Service Discovery: More Than It Seems (part 1 and part2) https://dzone.com/users/1298451/aatarasoff.html
  239. 239. Попробовать Demo 239 https://github.com/aatarasoff/spring-cloud-service-discovery-demo
  240. 240. 240

×