对于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,并填写如下配置信息:
需要说明的是,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,结果如下:
接着修改“hello,dev”的jdbc.password的值为root,修改后无需重启项目,直接刷新页面,结果如下:
当然,在最新版的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中进行注册,如下:
接着启动消费端,此时使用PrettyZoo进行查看,发现消费者端也已经注册,结果如下:
接着访问:http://localhost:8081/consume/hello,结果如下:
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);
}
}
接着重启消费端后测试,如下所示:
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”,如下:
接着依据三份配置 启动服务,此时使用PrettyZoo进行查看,结果如下:
也就是说,此时的三个服务已被当作集群。
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);
}
}
接着重新启动服务消费端后,再次进行测试,发现每次请求的的结果均不同,如下: