Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JPoint 2017

JPoint 2017

Avatar for Viet Nguyen

Viet Nguyen

April 07, 2017

More Decks by Viet Nguyen

Other Decks in Technology

Transcript

  1. Việt [вьет] 3 ‣ Пишу код больше 10 лет ‣

    Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  2. Việt [вьет] 4 ‣ Пишу код больше 10 лет ‣

    Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  3. Việt [вьет] 5 ‣ Пишу код больше 10 лет ‣

    Начинал как фронтендер ‣ Написал распределенный компилятор - полюбил бекенд ‣ Больше 6 лет на Java
  4. 6

  5. 15 UI UI UI API API API API API API

    API API API API API API API
  6. 16 UI UI UI API API API API API API

    API API API API API API API DB DB DB
  7. 17 UI UI UI API API API API API API

    API API API API API API API DB DB DB
  8. 18 UI UI UI API API API API API API

    API API API API API API API DB DB DB
  9. 19 UI UI UI API API API API API API

    API API API API API API API DB DB DB
  10. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣

    Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 20
  11. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣

    Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 21
  12. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣

    Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 22
  13. А за окном… ‣ Сеть отваливается ‣ Серваки отваливаются ‣

    Отвечают с задержкой ‣ Все такое неоднородное ‣ Толпа вредных админов 23 ? ? ?
  14. Следствия ‣ В распределенных системах 
 отказы - обычное дело

    ‣ Нужно воспринимать это 
 как естественный атрибут системы ‣ Принять и осознанно проектировать систему 24
  15. 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
  16. ansible playbook - hosts: frontend tasks: - apt: name: haproxy

    update_cache: yes - service: name: haproxy enabled: yes state: started 44
  17. ansible playbook - hosts: frontend tasks: - apt: name: haproxy

    update_cache: yes - service: name: haproxy enabled: yes state: started 45
  18. ansible playbook - hosts: frontend tasks: - apt: name: haproxy

    update_cache: yes - service: name: haproxy enabled: yes state: started 46
  19. Обновляем конфиг HAProxy 59 haproxy -f /my.conf -D -p /my.pid

    haproxy -f /my.conf -D -p /my.pid \ -sf $(cat /my.pid)
  20. Обновляем конфиг HAProxy 60 haproxy -f /my.conf -D -p /my.pid

    haproxy -f /my.conf -D -p /my.pid \ -sf $(cat /my.pid)
  21. Обновляем конфиг HAProxy 61 haproxy -f /my.conf -D -p /my.pid

    haproxy -f /my.conf -D -p /my.pid \ -sf $(cat /my.pid) finish, please
  22. Обновляем конфиг 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!
  23. Обновляем конфиг 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
  24. Обновляем конфиг 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
  25. Как получилось ‣ Победили недоступность :) ‣ Приложения потребовали только

    добавления healthcheck’ов :) ‣ Не валим приложения на старте :)
 ‣ Усложнился deployment :( 78
  26. Как получилось ‣ Победили недоступность :) ‣ Приложения потребовали только

    добавления healthcheck’ов :) ‣ Не валим приложения на старте :)
 ‣ Усложнился deployment :( 79
  27. 82 UI UI UI API API API API API API

    API API API API API API API DB DB DB
  28. Mesos, Marathon & Co. 86 Host 1 Host 2 Host

    5 Host 3 Host 4 a a a a a a = mesos agent
  29. Mesos, Marathon & Co. 87 Host 1 Host 2 Host

    5 Host 3 Host 4 a mesos master a a a a
  30. Mesos, Marathon & Co. 88 Host 1 Host 2 Host

    5 Host 3 Host 4 a mesos master a a a a marathon
  31. Mesos, Marathon & Co. 89 Host 1 Host 2 Host

    5 Host 3 Host 4 a mesos master a a a a marathon app manifest
  32. 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
  33. 94

  34. 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>
  35. 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>
  36. 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()); // … }
  37. 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()); // … }
  38. 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()); // … }
  39. 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()); // … }
  40. 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()); // … }
  41. Tomcat 102 public void setProperties(ServerSocket socket) throws SocketException { if

    (this.soReuseAddress != null) { socket.setReuseAddress(this.soReuseAddress.booleanValue()); } // … }
  42. Tomcat 103 public void setProperties(ServerSocket socket) throws SocketException { if

    (this.soReuseAddress != null) { socket.setReuseAddress(true); } if (this.soReusePort != null) { socket.setReusePort(true); } // … }
  43. 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; }
  44. 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; }
  45. 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; }
  46. 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; }
  47. 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; }
  48. 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; }
  49. 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; }
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. Here comes the pain ‣ Запросы на соединение приходят на

    случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 120
  58. Here comes the pain ‣ Запросы на соединение приходят на

    случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 121
  59. Here comes the pain ‣ Запросы на соединение приходят на

    случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 122
  60. Here comes the pain ‣ Запросы на соединение приходят на

    случайный socket ‣ Старый процесс HAProxy тоже получает запросы на connect ‣ При большом RPS соединения кладутся в очередь ‣ Старый HAProxy может завершиться, 
 когда в очереди уже есть соединения :( 123
  61. 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>()); }
  62. 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>()); }
  63. 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>()); }
  64. 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>()); }
  65. Netflix Ribbon 139 Timer lbTimer = // …; void setupPingTask()

    { lbTimer.schedule( new PingTask(), 0, pingIntervalSeconds * 1000); }
  66. 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; } }
  67. 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; } }
  68. Netflix Ribbon 142 public class RoundRobinRule extends AbstractLoadBalancerRule { public

    Server choose(Object key) { nextServerIndex = incrementAndGetModulo(serverCount); return allServers.get(nextServerIndex); } }
  69. 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; } }
  70. 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; } }
  71. 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; } }
  72. Eureka server 148 @SpringBootApplication @EnableEurekaServer public class EurekaServer { public

    static void main(String[] args) { new SpringApplicationBuilder(EurekaServer.class) .web(true) .run(args); } }
  73. Eureka server 149 @SpringBootApplication @EnableEurekaServer public class EurekaServer { public

    static void main(String[] args) { new SpringApplicationBuilder(EurekaServer.class) .web(true) .run(args); } }
  74. Eureka client 150 @SpringBootApplication @RestController @EnableEurekaClient public class CardsApi {

    public static void main(String... args) { SpringApplication.run(CardsApi.class, args); } }
  75. Eureka client 151 @SpringBootApplication @RestController @EnableEurekaClient public class CardsApi {

    public static void main(String... args) { SpringApplication.run(CardsApi.class, args); } }
  76. ‣ Прямая коммуникация ‣ Нет точки отказа в виде 


    прокси-сервера ‣ Можно задавать правила per-app
 ‣ Дополнительная логика на клиенте 152 Client-side Server-side ‣ Прозрачно для приложений ‣ Можно задавать глобальные правила
 
 ‣ Дополнительные точки отказа vs Load balancing
  77. ‣ Прямая коммуникация ‣ Нет точки отказа в виде 


    прокси-сервера ‣ Можно задавать правила per-app
 ‣ Дополнительная логика на клиенте 153 Client-side Server-side ‣ Прозрачно для приложений ‣ Можно задавать глобальные правила
 
 ‣ Дополнительные точки отказа vs Load balancing
  78. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает

    длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 154
  79. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает

    длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 155
  80. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает

    длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 156
  81. Выдохнули ‣ In-place обновление - самое простое. 
 Но вызывает

    длинные окна недоступности. ‣ Недоступность можно обеспечить настройками прокси 
 и оркестрацией с healthcheck’ами ‣ Для large-scale оркестрации можно использовать 
 Mesos/Marathon + Marathon-LB ‣ Альтернатива проксям - Client-side Service Discovery 157