Zookeeper---作为服务注册中心

认识Zookeeper是一套分布式协调服务。

  优点:  

    简单:与文件系统类似,Znode的组织方式。

    多副本:一般再线上都是三副本或者五副本的形式,最少会有三个节点。

    有序:有序的操作,根据时间戳进行排序。

    快:读多写少的情况下比较快。

在Spring cloud 中使用Zookeeper最为服务注册中心。

  在pom引入spring-cloud-starter-zookeeper-discovery

        
            org.springframework.cloud
            spring-cloud-starter-zookeeper-discovery
        
     


org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
  
    pom
      import





  在application.properties中声明

#告诉服务器找到哪一个zookeeper作为服务注册中心,作为例子,在本地启动了一个2181,在生产中最少使用三副本
spring.cloud.zookeeper.connect-string=localhost:2181

  在bootstarp.properties 中定义

  

spring.application.name=waiter-service

开启DiscoveryClient:@EnableDiscoveryClient

使用zookeeper作为注册中心的问题

  《阿里巴巴为什么不用Zookeeper做服务发现》https://yq.aliyun.com/articles/599997

  《Eureka! Why You Shouldn’t Use ZooKeeper for Service Discovery》.

  核心思想

    在实践中,注册中心不能因为自身的任何原因破坏服务之间本身的可连通性

    注册中心需要AP,而Zookeeper是CP

      CAP:一致性,可用性,分区容忍性

    

通过Docker启动Zookeeper

  官方指引

    https://hub.docker.com/_/zookeeper

  获取镜像

    docker pull zookeeper:3.5

  运行Zookeeper镜像

    docker run --name zookeeper -p 2181:2181 -d zookeeper:3.5

  查看zookeeper日志: docker logs zookeeper 

  进入zookeeper容器 : docker exec -it zookeeper bash

  连接zookeeper:./zkCli.sh命令行连接

  

ls
ls [-s] [-w] [-R] path
[zk: localhost:2181(CONNECTED) 1] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /services
[waiter-service]
[zk: localhost:2181(CONNECTED) 3] ls /services/waiter-service
[cb8e9631-f020-4cfa-9236-73025dc04a4d]
[zk: localhost:2181(CONNECTED) 4] ls /services/waiter-service/cb8e9631-f020-4cfa-9236-73025dc04a4d
[]
[zk: localhost:2181(CONNECTED) 5] get /services/waiter-service/cb8e9631-f020-4cfa-9236-73025dc04a4d
{"name":"waiter-service","id":"cb8e9631-f020-4cfa-9236-73025dc04a4d","address":"DESKTOP-JMA2LS2","port":55825,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"waiter-service-1","name":"waiter-service","metadata":{}},"registrationTimeUTC":1570436655426,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}

 

Zookeeper---作为服务注册中心_第1张图片

 

 使用customer来发现zookeeper中注册的writer-service服务

  pom应用

        
            org.springframework.cloud
            spring-cloud-starter-zookeeper-discovery
        


    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

在application.properties中定义

server.port=0

#显示所有信息
management.endpoint.health.show-details=always

spring.cloud.zookeeper.connect-string=localhost:2181

bootstarp.properties

spring.application.name=customer-service

开启DiscoveryClient: @EnableDiscoveryClient

注入:HttpComponentsClientHttpRequestFactory 以及RestTemplate

@Bean
    public HttpComponentsClientHttpRequestFactory requestFactory() {
        PoolingHttpClientConnectionManager connectionManager =
                new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        connectionManager.setMaxTotal(200);
        connectionManager.setDefaultMaxPerRoute(20);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .evictIdleConnections(30, TimeUnit.SECONDS)
                .disableAutomaticRetries()
                // 有 Keep-Alive 认里面的值,没有的话永久有效
                //.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                // 换成自定义的
                .setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);

        return requestFactory;
    }
@LoadBalanced
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .setConnectTimeout(Duration.ofMillis(100))
                .setReadTimeout(Duration.ofMillis(500))
                .requestFactory(this::requestFactory)
                .build();
    }
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

import java.util.Arrays;

public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
    private final long DEFAULT_SECONDS = 30;

    @Override
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
                .stream()
                .filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
                        && StringUtils.isNumeric(h.getValue()))
                .findFirst()
                .map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
                .orElse(DEFAULT_SECONDS) * 1000;
    }
}
import geektime.spring.springbucks.customer.model.Coffee;
import geektime.spring.springbucks.customer.model.CoffeeOrder;
import geektime.spring.springbucks.customer.model.NewOrderRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Arrays;
import java.util.List;

@Component
@Slf4j
public class CustomerRunner implements ApplicationRunner {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        showServiceInstances();
        readMenu();
        Long id = orderCoffee();
        queryOrder(id);
    }

    /**
     * 打印discoveryClient信息
     * */
    private void showServiceInstances() {
        log.info("DiscoveryClient: {}", discoveryClient.getClass().getName());
        discoveryClient.getInstances("waiter-service").forEach(s -> {
            log.info("Host: {}, Port: {}", s.getHost(), s.getPort());
        });
    }

    private void readMenu() {
        ParameterizedTypeReference> ptr =
                new ParameterizedTypeReference>() {};
        ResponseEntity> list = restTemplate
                .exchange("http://waiter-service/coffee/", HttpMethod.GET, null, ptr);
        list.getBody().forEach(c -> log.info("Coffee: {}", c));
    }

    private Long orderCoffee() {
        NewOrderRequest orderRequest = NewOrderRequest.builder()
                .customer("Li Lei")
                .items(Arrays.asList("capuccino"))
                .build();
        RequestEntity request = RequestEntity
                .post(UriComponentsBuilder.fromUriString("http://waiter-service/order/").build().toUri())
                .body(orderRequest);
        ResponseEntity response = restTemplate.exchange(request, CoffeeOrder.class);
        log.info("Order Request Status Code: {}", response.getStatusCode());
        Long id = response.getBody().getId();
        log.info("Order ID: {}", id);
        return id;
    }

    private void queryOrder(Long id) {
        CoffeeOrder order = restTemplate
                .getForObject("http://waiter-service/order/{id}", CoffeeOrder.class, id);
        log.info("Order: {}", order);
    }
}

你可能感兴趣的:(Zookeeper---作为服务注册中心)