天天看点

Zookeeper使用详解之配置管理和服务管理

作者:但求无Bug

对于zookeeper而言,其使用场景很多,如:数据发布订阅、分布式队列,负载均衡,分布式命名服务、配置管理,服务的注册发现和集群管理等。本文先介绍如何基于Zookeeper实现配置管理和服务管理。

一、 配置管理

所谓配置管理是指对组成集群的相关机器的配置进行管理。因为这些配置信息需进行动态改变,因此可利用发布/订阅模式让这些机器来订阅配置信息的变更,当配置信息发生改变时,这些机器就会得到相应通知,从而更新自己的配置。

下面给出SpringCloud基于zookeeper实现的配置中心实现。

1.1 依赖

对应的依赖如下:

<dependency>    
    <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>    
    <groupId>org.springframework.cloud</groupId>    
    <artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>           

1.2 实现

下面给出具体的实现类。

  • 配置实体类

首先为配置实体类,用于承接从配置文件中读取的配置信息,其声明如下:

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@RefreshScope
@Data
@ConfigurationProperties(prefix = "jdbc")
@Component
public class JDBCProperties {

    private String host;
    private String port;
    private String username;
    private String password;
}           
  • 配置

接着便是对应的配置。需要说明的是,配置中心相关的类需放置在boostrap.yml或bootstrap.properties中,如下:

server:
  port: 8080spring:
  profiles:
    active: dev
  application:
    name: hello
  cloud:
    zookeeper:
      connect-string: 192.168.217.128:2181
      enabled: true           
  • 测试接口类

接着给出对应的测试类:

import com.itheima.config.JDBCProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {

    @Autowired    
    private JDBCProperties jdbcProperties;

    @GetMapping("/jdbc")
    public String hello() {
        return jdbcProperties.toString();
    }
}           
  • 启动类

由于基于SpringBoot实现,因此需提供启动类,下面为对应的代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@EnableConfigurationProperties
@SpringBootApplication
public class ZkConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZkConfigApplication.class, args);
    }
}           

1.3 测试

首先使用PrettyZoo连接zookeeper,并填写如下配置信息:

Zookeeper使用详解之配置管理和服务管理

需要说明的是,config为固定的root路径,"hello"为spring.application.name的值,而"hello,dev"则表示hello应用的dev环境,hello下的子节点名即为各配置字段的键,而值即为各配置信息对应的值,下面为对应的配置信息值:

# hello
jdbc.host=localhost
jdbc.port=3306
jdbc.username=root
jdbc.password=root

# hello,dev
jdbc.host=192.168.157.122
jdbc.port=3306
jdbc.username=root
jdbc.password=123456           

接着启动编写的程序,访问:http://localhost:8080/hello/jdbc,结果如下:

Zookeeper使用详解之配置管理和服务管理

接着修改“hello,dev”的jdbc.password的值为root,修改后无需重启项目,直接刷新页面,结果如下:

Zookeeper使用详解之配置管理和服务管理

当然,在最新版的Spring Cloud Zookeeper Config中,增加了配置根路径以及环境分割符的配置。下面为最新版中bootstrap.yml对应的配置:

server:
  port: 8080spring:
  profiles:
    active: dev
  application:
    name: hello
  cloud:
    zookeeper:
      connect-string: 192.168.217.128:2181
      config:
        enabled: true        
        root: config
        profile-separator: ,
        name: ${spring.application.name}           

二、 服务管理

所谓的服务管理包括:服务注册和发现等,其是指对服务端的上下线做统一管理。其会将各工作服务器作为数据的发布方,向集群中注册一些信息,而监控服务器则来订阅这些工作服务器的基本信息。当工作服务器都基本信息发生变更时,如:服务上下线、服务器角色或服务范围改变等,各监控服务器可得到通知并迅速地进行响应。

所谓负载均衡是指通过某种手段,将对某种资源的访问分摊到不同节点中,以减轻单点的压力。最常见的为RPC中客户端的负载均衡。

下面讲一下基于Zookeeper实现的注册中心,其包含了:服务注册、服务发现和负载均衡等功能。

2.1 依赖

下面为基于zookeeper实现注册中心的相关依赖:

<dependency>    
    <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>    
    <groupId>org.springframework.cloud</groupId>    
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>           

2.2 实现

2.2.1 服务提供者

  • 配置信息

首先为服务提供端对应的配置信息:

spring:
  application:
    name: zookeeper-provider
  cloud:
    zookeeper:
      connect-string: 192.168.217.128:2181
      discovery:
        root: services           
  • 启动类

接着给出服务提供端启动类对应代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class ZookeeperProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZookeeperProviderApplication.class, args);
    }
}           
  • 测试接口类

服务提供端的测试接口类对应代码如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/provider")
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello, This is provider";
    }
}           

2.2.2 服务消费者

  • 配置信息

下面给出消费端项目的配置类:

server:
  port: 8081spring:
  application:
    name: zookeeper-consumer
  cloud:
    zookeeper:
      connect-string: 192.168.217.128:2181
      discovery:
        root: services           
  • 启动类

下面为消费端的启动类代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ZookeeperConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZookeeperConsumerApplication.class, args);
    }
}           
  • 远程访问配置类

下面为消费端对应的配置类代码:

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean    
    @LoadBalanced    
    public RestTemplate restTemplate() {
        return new RestTemplateBuilder().build();
    }
}           
  • 测试接口类

下面为消费端对应的测试接口类代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/consume")
public class HelloController {

    @Autowired    
    private RestTemplate restTemplate;

    @GetMapping("/hello")
    public String hello() {
        return restTemplate.getForObject("http://zookeeper-provider/provider/hello", String.class);
    }
}           

2.3 测试

首先启动服务提供端,启动后使用PrettyZoo查看Zookeeper信息,发现服务提供端已在zookeeper中进行注册,如下:

Zookeeper使用详解之配置管理和服务管理

接着启动消费端,此时使用PrettyZoo进行查看,发现消费者端也已经注册,结果如下:

Zookeeper使用详解之配置管理和服务管理

接着访问:http://localhost:8081/consume/hello,结果如下:

Zookeeper使用详解之配置管理和服务管理

2.4 改进

上述消费端使用RestTemplate来直接调用服务提供端,该方式对开发者而言有些繁琐,因此可使用OpenFeign来进行远程调用。

首先是增加OpenFeign的依赖,如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>           

接着是新增对应的Feign接口类,对应代码如下:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "zookeeper-provider", path = "/provider")
public interface HelloFeign {

    @GetMapping("/hello")
    public String hello();
}           

接着是对接口测试类和启动类进行修改,如下:

  • 测试类

测试类修改后如下:

import com.itheima.feigns.HelloFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/consume")
public class HelloController {

    @Autowired    
    private HelloFeign helloFeign;

    @GetMapping("/hello")
    public String hello() {
        return helloFeign.hello();
    }
}           
  • 启动类

启动类修改后如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class ZookeeperConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZookeeperConsumerApplication.class, args);
    }
}           

接着重启消费端后测试,如下所示:

Zookeeper使用详解之配置管理和服务管理

2.5 负载均衡策略

实际上OpenFeign已内置了负载均衡组件,之前使用Ribbon实现,而自从Ribbon放弃维护后,官方推荐使用LoadBalancer。这里演示如何修改负载均衡组件。

2.5.1 服务提供端修改

对于服务提供端而言,为了方便测试,需在返回信息中包含当前服务的端口,用以识别真正的调用者,修改后的代码如下:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/provider")
public class HelloController {

    @Value("${server.port}")
    private Integer port;

    @GetMapping("/hello")
    public String hello() {
        return "hello, This is provider, port:" + port;
    }
}           

接着便是复制两份启动配置,并传入启动参数“--server.port=808X”,如下:

Zookeeper使用详解之配置管理和服务管理

接着依据三份配置 启动服务,此时使用PrettyZoo进行查看,结果如下:

Zookeeper使用详解之配置管理和服务管理

也就是说,此时的三个服务已被当作集群。

2.5.2 服务消费端修改

首先在消费端添加负载均衡策略类,对应代码如下:

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomRuleConfig {

    @Bean    
    public IRule loadRule() {
        return new RandomRule();
    }
}           

接着修改启动类,增加引用自定义负载均衡策略的配置。启动类修改后如下:

import com.itheima.config.CustomRuleConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
@LoadBalancerClient(configuration = CustomRuleConfig.class)
public class ZookeeperConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZookeeperConsumerApplication.class, args);
    }
}           

接着重新启动服务消费端后,再次进行测试,发现每次请求的的结果均不同,如下:

Zookeeper使用详解之配置管理和服务管理
Zookeeper使用详解之配置管理和服务管理
Zookeeper使用详解之配置管理和服务管理

继续阅读