Successfully reported this slideshow.
Your SlideShare is downloading. ×

JPoint 2017 - Where is my service, dude?

JPoint 2017 - Where is my service, dude?

Download to read offline

How to replace the wheel of a car on the go? How to change the gear in the mechanism, so that everything does not break? And if at the same time the remaining gears are constantly falling off? And the mechanism is on fire? And you in hell?

In this talk, I will show how to update Java-microservices without unavailability for customers.

We will touch many different tools, such as HAProxy, Ansible, Vegeta, Mesos / Marathon, Ribbon / Eureka; there will be loads of configs and a bottomless ocean of pain of distributed systems, into which we will consistently sink.

How to replace the wheel of a car on the go? How to change the gear in the mechanism, so that everything does not break? And if at the same time the remaining gears are constantly falling off? And the mechanism is on fire? And you in hell?

In this talk, I will show how to update Java-microservices without unavailability for customers.

We will touch many different tools, such as HAProxy, Ansible, Vegeta, Mesos / Marathon, Ribbon / Eureka; there will be loads of configs and a bottomless ocean of pain of distributed systems, into which we will consistently sink.

More Related Content

Slideshows for you

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

JPoint 2017 - Where is my service, dude?

  1. 1. Где мой сервис, чувак?
  2. 2. Việt [вьет] 2
  3. 3. Việt [вьет] 3 ‣ Пишу код больше 10 лет ‣ Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  4. 4. Việt [вьет] 4 ‣ Пишу код больше 10 лет ‣ Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  5. 5. Việt [вьет] 5 ‣ Пишу код больше 10 лет ‣ Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  6. 6. 6
  7. 7. 7 DevOps
  8. 8. 8 Конфиги! Они везде
  9. 9. @EnableEverything @FixThis @FixThat @DoWhatever public class App { // no code - no cry } 9 :(
  10. 10. О чём доклад? ‣ Обновление микросервисов 
 без недоступности
 ‣ Тулзы ‣ Конфиги ‣ Заклятья 10
  11. 11. О чём доклад? ‣ Обновление микросервисов 
 без недоступности
 ‣ Тулзы ‣ Конфиги ‣ Заклятья 11
  12. 12. 12 Монолит Микросервисы
  13. 13. 13 UI UI UI
  14. 14. 14 UI UI UI API API API API API API API API API
  15. 15. 15 UI UI UI API API API API API API API API API API API API API
  16. 16. 16 UI UI UI API API API API API API API API API API API API API DB DB DB
  17. 17. 17 UI UI UI API API API API API API API API API API API API API DB DB DB
  18. 18. 18 UI UI UI API API API API API API API API API API API API API DB DB DB
  19. 19. 19 UI UI UI API API API API API API API API API API API API API DB DB DB
  20. 20. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣ Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 20
  21. 21. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣ Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 21
  22. 22. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣ Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 22
  23. 23. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣ Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 23 ? ? ?
  24. 24. Следствия ‣ В распределенных системах 
 отказы - обычное дело ‣ Нужно воспринимать это 
 как естественный атрибут системы ‣ Принять и осознанно проектировать систему 24
  25. 25. “Hope 
 is not 
 a strategy” 25
  26. 26. KISS FTW 27 cards API transactions API :8081 :8080 Client
  27. 27. KISS FTW 28 cards API transactions API :8081 :8080 Client
  28. 28. Где мои деньги?!
  29. 29. KISS FTW 30 cards API transactions API :8081 :8080 Client
  30. 30. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 31
  31. 31. vegeta KISS FTW 32 cards API:8081 :8080 ansible transactions API
  32. 32. vegeta $ echo "GET http://localhost” | vegeta attack | vegeta report 33
  33. 33. vegeta $ echo "GET http://localhost” | vegeta attack | vegeta report 34
  34. 34. vegeta $ echo "GET http://localhost” | vegeta attack | vegeta report 35
  35. 35. vegeta $ echo "GET http://localhost” | vegeta attack | vegeta report 
 Requests [total, rate] 500, 50.10 Duration [total, attack, wait] 9.9s, 9.9s, 8.2ms Latencies [mean, 50, 95, 99, max] 9.7ms, 7.4ms, … Bytes In [total, mean] 73260, 146.52 Bytes Out [total, mean] 0, 0.00 Success [ratio] 84.20% Status Codes [code:count] 500:79 200:421 36
  36. 36. ansible $ ansible-playbook -i inventory playbook.yml 37
  37. 37. ansible $ ansible-playbook -i inventory playbook.yml 38
  38. 38. ansible $ ansible-playbook -i inventory playbook.yml 39
  39. 39. ansible inventory [frontend] server1 server2 [backend] server3 [backend:vars] timeout = 2s 40
  40. 40. ansible inventory [frontend] server1 server2 [backend] server3 [backend:vars] timeout = 2s 41
  41. 41. ansible inventory [frontend] server1 server2 [backend] server3 [backend:vars] timeout = 2s 42
  42. 42. ansible inventory [frontend] server1 server2 [backend] server3 [backend:vars] timeout = 2s 43
  43. 43. ansible playbook - hosts: frontend tasks: - apt: name: haproxy update_cache: yes - service: name: haproxy enabled: yes state: started 44
  44. 44. ansible playbook - hosts: frontend tasks: - apt: name: haproxy update_cache: yes - service: name: haproxy enabled: yes state: started 45
  45. 45. ansible playbook - hosts: frontend tasks: - apt: name: haproxy update_cache: yes - service: name: haproxy enabled: yes state: started 46
  46. 46. В прод!
  47. 47. Упс 48 t :( old new
  48. 48. Клиенты уходят!
  49. 49. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 50
  50. 50. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 51
  51. 51. Proxy has arrived 52 cards API transactions API 
 oldHAProxy :8080 :8082
  52. 52. Временная избыточность 53 cards API transactions API 
 oldHAProxy transactions API
 new :8080 :8082 :8083
  53. 53. Временная избыточность 54 cards API transactions API 
 oldHAProxy transactions API
 new :8080 :8082 :8083
  54. 54. Ну норм чо 55 cards API HAProxy transactions API
 new :8080 :8083
  55. 55. HAProxy 56 haproxy -f /my.conf -D -p /my.pid
  56. 56. HAProxy 57 haproxy -f /my.conf -D -p /my.pid
  57. 57. HAProxy 58 haproxy -f /my.conf -D -p /my.pid
  58. 58. Обновляем конфиг HAProxy 59 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid)
  59. 59. Обновляем конфиг HAProxy 60 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid)
  60. 60. Обновляем конфиг HAProxy 61 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid) finish, please
  61. 61. Обновляем конфиг HAProxy 62 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid) finish, please OK!
  62. 62. Обновляем конфиг HAProxy 63 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid) finish, please OK! Unbind ports
  63. 63. Обновляем конфиг HAProxy 64 haproxy -f /my.conf -D -p /my.pid haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid) finish, please OK! Unbind ports Serve last connections
  64. 64. Обновляем конфиг HAProxy 65 haproxy -f /my.conf -D -p /my.pid -sf $(cat /my.pid)
  65. 65. Катай!
  66. 66. Ну почемууу? 67 t old new haproxy 1 2 3 4
  67. 67. Ну почемууу? 68 t old new haproxy 1 2 3 4
  68. 68. Ну почемууу? 69 t old new haproxy 1 2 3 4
  69. 69. Ну почемууу? 70 t old new haproxy 1 2 3 4
  70. 70. Ну почемууу? 71 t old new haproxy 1 2 3 4
  71. 71. Ну почемууу? 72 t old new haproxy 1 2 3 4
  72. 72. Ну почемууу? 73 t old new haproxy 1 2 3 4
  73. 73. Ну почемууу? 74 t old new haproxy 1 2 3 4
  74. 74. Ну почемууу? 75 t old new haproxy 1 2 3 4
  75. 75. Healthcheck идёт на помощь! @RequestMapping("/health")
 public String getHealth() {
 return "OK";
 } 76
  76. 76. Вжух!
  77. 77. Как получилось ‣ Победили недоступность :) ‣ Приложения потребовали только добавления healthcheck’ов :) ‣ Не валим приложения на старте :)
 ‣ Усложнился deployment :( 78
  78. 78. Как получилось ‣ Победили недоступность :) ‣ Приложения потребовали только добавления healthcheck’ов :) ‣ Не валим приложения на старте :)
 ‣ Усложнился deployment :( 79
  79. 79. 80 cards API transactions API HAProxy :8080 :8082
  80. 80. 81 cards API transactions API HAProxy :8080 :8082
  81. 81. 82 UI UI UI API API API API API API API API API API API API API DB DB DB
  82. 82. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 83
  83. 83. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 84
  84. 84. Mesos, Marathon & Co. 85 Host 1 Host 2 Host 5 Host 4 Host 3
  85. 85. Mesos, Marathon & Co. 86 Host 1 Host 2 Host 5 Host 3 Host 4 a a a a a a = mesos agent
  86. 86. Mesos, Marathon & Co. 87 Host 1 Host 2 Host 5 Host 3 Host 4 a mesos master a a a a
  87. 87. Mesos, Marathon & Co. 88 Host 1 Host 2 Host 5 Host 3 Host 4 a mesos master a a a a marathon
  88. 88. Mesos, Marathon & Co. 89 Host 1 Host 2 Host 5 Host 3 Host 4 a mesos master a a a a marathon app manifest
  89. 89. Mesos, Marathon & Co. 90 Host 1 Host 2 Host 5 Host 3 Host 4 a mesos master a a a a marathon app app app
  90. 90. А HAProxy кто будет настраивать? 91 marathon marathon-lb marathon_lb.py HAProxy
  91. 91. Ставь!
  92. 92. HAProxy socket flags ‣ SO_REUSEADDR - ignore TIME_WAIT ‣ SO_REUSEPORT - bind same IP:PORT 93
  93. 93. 94
  94. 94. spring-boot-starter-web 95 <project …> <artifactId>spring-boot-starter-web</artifactId> <!-- … --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> </dependencies> </project>
  95. 95. spring-boot-starter-web 96 <project …> <artifactId>spring-boot-starter-web</artifactId> <!-- … --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> </dependencies> </project>
  96. 96. Tomcat 97 public void bind() throws Exception { this.serverSock = ServerSocketChannel.open(); this.socketProperties.setProperties(this.serverSock.socket()); InetSocketAddress addr = // …; this.serverSock.socket().bind(addr, this.getBacklog()); // … }
  97. 97. Tomcat 98 public void bind() throws Exception { this.serverSock = ServerSocketChannel.open(); this.socketProperties.setProperties(this.serverSock.socket()); InetSocketAddress addr = // …; this.serverSock.socket().bind(addr, this.getBacklog()); // … }
  98. 98. Tomcat 99 public void bind() throws Exception { this.serverSock = ServerSocketChannel.open(); this.socketProperties.setProperties(this.serverSock.socket()); InetSocketAddress addr = // …; this.serverSock.socket().bind(addr, this.getBacklog()); // … }
  99. 99. Tomcat 100 public void bind() throws Exception { this.serverSock = ServerSocketChannel.open(); this.socketProperties.setProperties(this.serverSock.socket()); InetSocketAddress addr = // …; this.serverSock.socket().bind(addr, this.getBacklog()); // … }
  100. 100. Tomcat 101 public void bind() throws Exception { this.serverSock = ServerSocketChannel.open(); this.socketProperties.setProperties(this.serverSock.socket()); InetSocketAddress addr = // …; this.serverSock.socket().bind(addr, this.getBacklog()); // … }
  101. 101. Tomcat 102 public void setProperties(ServerSocket socket) throws SocketException { if (this.soReuseAddress != null) { socket.setReuseAddress(this.soReuseAddress.booleanValue()); } // … }
  102. 102. Tomcat 103 public void setProperties(ServerSocket socket) throws SocketException { if (this.soReuseAddress != null) { socket.setReuseAddress(true); } if (this.soReusePort != null) { socket.setReusePort(true); } // … }
  103. 103. Копай глубже!
  104. 104. linux/net/core/sock_reuseport.c 105
  105. 105. linux/net/core/sock_reuseport.c 106 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  106. 106. linux/net/core/sock_reuseport.c 107 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  107. 107. linux/net/core/sock_reuseport.c 108 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  108. 108. linux/net/core/sock_reuseport.c 109 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  109. 109. linux/net/core/sock_reuseport.c 110 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  110. 110. linux/net/core/sock_reuseport.c 111 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  111. 111. linux/net/core/sock_reuseport.c 112 struct sock *reuseport_select_sock(hash, …) { // … struct sock *sk2 = NULL; reuse = rcu_dereference(sk->sk_reuseport_cb); socks = READ_ONCE(reuse->num_socks); sk2 = reuse->socks[reciprocal_scale(hash, socks)]; // … return sk2; }
  112. 112. struct sock *__inet_lookup_listener(…) { u32 phash = 0; struct sock *result = NULL; phash = inet_ehashfn(net, daddr, hnum, saddr, sport); result = reuseport_select_sock(phash, …); // … return result; } linux/net/ipv4/inet_hashtables.c 113
  113. 113. struct sock *__inet_lookup_listener(…) { u32 phash = 0; struct sock *result = NULL; phash = inet_ehashfn(net, daddr, hnum, saddr, sport); result = reuseport_select_sock(phash, …); // … return result; } linux/net/ipv4/inet_hashtables.c 114
  114. 114. struct sock *__inet_lookup_listener(…) { u32 phash = 0; struct sock *result = NULL; phash = inet_ehashfn(net, daddr, hnum, saddr, sport); result = reuseport_select_sock(phash, …); // … return result; } linux/net/ipv4/inet_hashtables.c 115
  115. 115. static u32 inet_ehashfn(const struct net *net, const __be32 laddr, const __u16 lport, const __be32 faddr, const __be16 fport) { // … return __inet_ehashfn(laddr, lport, faddr, fport, inet_ehash_secret + net_hash_mix(net)); } linux/net/ipv4/inet_hashtables.c 116
  116. 116. static u32 inet_ehashfn(const struct net *net, const __be32 laddr, const __u16 lport, const __be32 faddr, const __be16 fport) { // … return __inet_ehashfn(laddr, lport, faddr, fport, inet_ehash_secret + net_hash_mix(net)); } linux/net/ipv4/inet_hashtables.c 117
  117. 117. static u32 inet_ehashfn(const struct net *net, const __be32 laddr, const __u16 lport, const __be32 faddr, const __be16 fport) { // … return __inet_ehashfn(laddr, lport, faddr, fport, inet_ehash_secret + net_hash_mix(net)); } linux/net/ipv4/inet_hashtables.c 118
  118. 118. struct sock *__inet_lookup_listener(const __be32 saddr, __be16 sport, …) { u32 phash = 0; struct sock *result = NULL; phash = inet_ehashfn(net, daddr, hnum, saddr, sport); result = reuseport_select_sock(phash, …); // … return result; } linux/net/ipv4/inet_hashtables.c 119
  119. 119. Here comes the pain ‣ Запросы на соединение приходят на случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 120
  120. 120. Here comes the pain ‣ Запросы на соединение приходят на случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 121
  121. 121. Here comes the pain ‣ Запросы на соединение приходят на случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 122
  122. 122. Here comes the pain ‣ Запросы на соединение приходят на случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 123
  123. 123. Решения? ‣ Ничего не делать ‣ Низкоуровневые заклятья для HAProxy ‣ Отказаться от HAProxy 124
  124. 124. Решения? ‣ Ничего не делать ‣ Низкоуровневые заклятья для HAProxy ‣ Отказаться от HAProxy 125
  125. 125. Решения? ‣ Ничего не делать ‣ Низкоуровневые заклятья для HAProxy ‣ Отказаться от HAProxy 126
  126. 126. Катимся к успеху ‣ In-place update ‣ Proxy ‣ Resource management ‣ Client side LB 127
  127. 127. We are server side 128 app 1 app 2 proxy
  128. 128. Лишнее звено 129 app 1 app 2 proxy
  129. 129. Client side! 130 app 1 app 2
  130. 130. A piece of proxy ‣ Список сервисов ‣ Балансировщик 131
  131. 131. A piece of proxy ‣ Список сервисов ‣ Балансировщик 132
  132. 132. Netflix Ribbon 133 HttpResourceGroup resourceGroup = Ribbon
 .createHttpResourceGroup( “transactionsClient", ClientOptions.create() .withConfigurationBasedServerList(“srv1:8080, srv2:8088”) );
  133. 133. Netflix Ribbon 134 HttpRequestTemplate requestTemplate = resourceGroup .newTemplateBuilder("requestTemplate") .withMethod(“POST") .withUriTemplate("/transactions") .build();
  134. 134. Netflix Ribbon 135 public class BaseLoadBalancer extends AbstractLoadBalancer ... { protected IRule rule = new RoundRobinRule(); protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>()); }
  135. 135. Netflix Ribbon 136 public class BaseLoadBalancer extends AbstractLoadBalancer ... { protected IRule rule = new RoundRobinRule(); protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>()); }
  136. 136. Netflix Ribbon 137 public class BaseLoadBalancer extends AbstractLoadBalancer ... { protected IRule rule = new RoundRobinRule(); protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>()); }
  137. 137. Netflix Ribbon 138 public class BaseLoadBalancer extends AbstractLoadBalancer ... { protected IRule rule = new RoundRobinRule(); protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>()); }
  138. 138. Netflix Ribbon 139 Timer lbTimer = // …; void setupPingTask() { lbTimer.schedule( new PingTask(), 0, pingIntervalSeconds * 1000); }
  139. 139. Netflix Ribbon 140 public Server chooseServer(Object key) { try { return rule.choose(key); } catch (Exception e) { log.warn("LoadBalancer: Error choosing server for key {}”, key, e); return null; } }
  140. 140. Netflix Ribbon 141 public Server chooseServer(Object key) { try { return rule.choose(key); } catch (Exception e) { log.warn("LoadBalancer: Error choosing server for key {}”, key, e); return null; } }
  141. 141. Netflix Ribbon 142 public class RoundRobinRule extends AbstractLoadBalancerRule { public Server choose(Object key) { nextServerIndex = incrementAndGetModulo(serverCount); return allServers.get(nextServerIndex); } }
  142. 142. Netflix Ribbon 143 private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; if (nextServerCyclicCounter.compareAndSet(current, next)) return next; } }
  143. 143. Netflix Ribbon 144 private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; if (nextServerCyclicCounter.compareAndSet(current, next)) return next; } }
  144. 144. Netflix Ribbon 145 private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; if (nextServerCyclicCounter.compareAndSet(current, next)) return next; } }
  145. 145. Spring cloud Netflix 146 app 1 eureka server app 2 app 3 app 4
  146. 146. Spring cloud Netflix 147 app 1 eureka server app 2 app 3 app 4
  147. 147. Eureka server 148 @SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main(String[] args) { new SpringApplicationBuilder(EurekaServer.class) .web(true) .run(args); } }
  148. 148. Eureka server 149 @SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main(String[] args) { new SpringApplicationBuilder(EurekaServer.class) .web(true) .run(args); } }
  149. 149. Eureka client 150 @SpringBootApplication @RestController @EnableEurekaClient public class CardsApi { public static void main(String... args) { SpringApplication.run(CardsApi.class, args); } }
  150. 150. Eureka client 151 @SpringBootApplication @RestController @EnableEurekaClient public class CardsApi { public static void main(String... args) { SpringApplication.run(CardsApi.class, args); } }
  151. 151. ‣ Прямая коммуникация ‣ Нет точки отказа в виде 
 прокси-сервера ‣ Можно задавать правила per-app
 ‣ Дополнительная логика на клиенте 152 Client-side Server-side ‣ Прозрачно для приложений ‣ Можно задавать глобальные правила
 
 ‣ Дополнительные точки отказа vs Load balancing
  152. 152. ‣ Прямая коммуникация ‣ Нет точки отказа в виде 
 прокси-сервера ‣ Можно задавать правила per-app
 ‣ Дополнительная логика на клиенте 153 Client-side Server-side ‣ Прозрачно для приложений ‣ Можно задавать глобальные правила
 
 ‣ Дополнительные точки отказа vs Load balancing
  153. 153. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 154
  154. 154. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 155
  155. 155. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 156
  156. 156. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 157
  157. 157. Спасибо за внимание! stereohorse/jpoint2017 stereohorse

×