SpringCloud(二)——Consul
前言
上一篇笔记中我们进行了基于netfix的SpringCloud基本微服务搭建,其中最重要的服务注册中心使用的是netfix原生的Eureka
,但是这个工具最新版本在早些年间就已经停止维护且闭源,所以现在注册中心也就不会再使用Eureka来作为微服务的注册中心使用。熟悉分布式(Dubbo)的使用的同学可能会了解zookeeper
,这个工具一般会作为分布式项目的注册中心,然而zookeeper是一款较早的工具,底层是基于Java语言实现的,且该工具没有提供原生的视图界面,使用起来较为不方便。
在这时,一款基于谷歌的go
语言为实现语言的注册中心工具——consul
的出现打破了这个僵局,他提供了一体化的服务注册管理功能,且提供了可视化操作界面,同时该工具还将Eureka
必须自己手动创建项目来生成服务注册中心的操作简化掉,使得搭建微服务注册中心的时候更加快捷,只需要启动consul
即可。
文章目录
Consul
前言中对consul已经有了介绍,在此不做赘述。
使用consul的时候,相比较Eureka做注册中心简单的是不需要通过构建项目、书写代码的方式来创建注册中心。我们可以通过consul官网提供的软件来直接快捷构建注册中心。
consul构建注册中心
官网下载consul工具
在官网中选择Windows
(学习阶段使用windows即可)
下载完毕后,将下载的安装包解压到指定的路径下(路径不要有中文)。好了,consul已经安装完毕了。不要奇怪,consul软件就这么一个.exe
包,但是它是由go
语言来实现的所以我们不能双击运行。需要用到cmd
命令提示符窗口来运行。
在此之前我们先来配置一下consul
的环境变量,和JDK的环境变量配置意义相同,都是为了在任何位置都可以运行该软件。
配置consul环境变量
我们找到环境变量中的path
,在其中加入consul对应的路径。比如:我的consul路径为D:\consul\consul.exe
,所以我们在path
中添加的就是D:\consul
路径。这个时候我们就可以在cmd的默认路径中使用命令符来启动consul。
consul启动
# consul启动基本命令(-dev表示测试启动,不要在生产环境中使用)
consul agent -dev
# 可以在命令符启动的时候指定consul注册中心的服务名
consul agent -dev -datacenter=aaa
输入完启动指令后,弹出一个新的窗口,并有如下运行界面且界面中的光标一直在闪烁,就证明我们启动成功。
图形化界面查看
在浏览器地址界面输入:localhost:8500
即可。
编写Consul服务
首先要在我们做好的父项目中搭建一个子项目,在其中我们依旧选择不选择任何maven骨架。
引入依赖
<!--springmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--consul client-->
<!--consul 客户端没有自我保护机制,如果服务宕机,则会自动断掉-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
书写配置
server:
port: 8082 # 定义启动端口号
spring:
application:
name: CONSULCLIENT8082 # 定义该项目运行服务名(与前一篇笔记中的服务名规则一致)
cloud:
consul:
host: localhost # 定义所要注册到的consul注册中心的地址
port: 8500 # 定义连接的consul服务中心的端口号
discovery:
service-name: ${spring.application.name} # 引入当前服务名作为consul注册名
书写启动类
@SpringBootApplication
// 通用的客户端注册注解,该注解会根据配置和依赖自动将客户端按照对应的设置注册到对应的注册中心
@EnableDiscoveryClient
public class ConsulClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulClientApplication.class,args);
}
}
这时候启动该项目,在运行结束后我们会在consul的图形化管理界面中看到我们名为:CONSULCLIENT8082
的服务。
这个时候我们注册的服务在界面显示中会有一个红色的×
,它表示该服务可能不可用,暂时不要管他。前文说过我们的consul默认没有自我保护机制。后文会说明。
补充说明
书写项目注册到consul中的时候,可以尝试将配置文件中关于consul的配置注释掉,再将入口类中关于consul服务的注解注释掉,然后在启动一下,查看是否可以注册到我们的consul服务中。即这些配置都是我们默认的,所以我们可以选择不写(???),大家千万不要省略,默认归默认,但是在未来可能会根据配置文件的不同来改变启动配置,也有可能会根据其他情况来修改配置中的数据,所以我们最好把配置文件写全了,提升代码的健壮性。
新桃检查依赖
项目注册到consul中会显示一个红叉叉,这时候说明项目没有心跳检查导致的,所以我们需要给项目增加一个心跳检查的依赖。
<!--consul 心跳检查依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加该依赖以后,再启动项目会发现在浏览器页面查看consul图形化管理器,会发现一切正常了。
集群
在本篇笔记中,仅说明服务的集群搭建,consul注册中心的集群搭建暂且不做说明。我们可以想上一篇笔记中说明的的Eureka注册中心的集群搭建一样来复制运行需要注册的服务。在此做简单的服务搭建说明(核心代码放在下面,其他的自行书写)。
用户类服务搭建
- 配置文件:
server.port=8084
spring.application.name=USER
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=${spring.application.name}
logging.level.com.lee.controller=debug
- 控制器:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/invokeDemo")
public String invokeDemo(){
return "User's demo is ok...";
}
}
订单类服务搭建
- 配置文件
server.port=8085
spring.application.name=ORDERS
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=${spring.application.name}
- 控制器:
@RequestMapping("/order")
@RestController
public class OrderController {
@Value("${server.port}")
private String port;
@RequestMapping("/demo")
public String demo(){
System.out.println("Order's demo is ok...");
return port;
}
}
启动测试
将这两个服务启动后,在consul界面中检查是否启动成功;然后在浏览器中访问这两个服务的接口,查看是否可以正确返回结果。
服务之间的调用
在服务调用中会从基础说起,不会一上来就直接使用工具类。如果想看如何使用负载均衡的方式来调用服务集群中的接口请移步至——————————————
RestTemplate
@RequestMapping("/invokeDemo")
public String invokeDemo(){
RestTemplate restTemplate = new RestTemplate();
// restTemplate 通过getForObject方法来发送http请求,去连接其他服务。其实这个时候我们并没有使用consul集群为服务间的调用提供的帮助,把consul关闭后也可以连接。
// restTemplate 使用的是Java原生的方式来发送Http请求
String result = restTemplate.getForObject("http://localhost:8085/order/demo",String.class);
return "User's demo is ok..." + result;
}
这个时候我们可以在浏览器调用User接口的时候查看到对应的返回结果以及端口号。好的,现在测试完毕,我们可以通过上一篇笔记中的方式对Orders服务进行集群处理,我这里对Orders
服务进行了两次复制启动(即三个服务集群)。当集群启动成功后,我们在consul中看到的服务如下图所示:
点击orders 服务后会发现在里面有三个节点:
之前我们的服务间调用并没有使用的consul,现在集群搭建完毕,需要在consul中获取到对应服务的集群列表。
// ServiceInstance是springcloud用来与注册中心连接并之心操作的一个返回值对象,它的作用是可以存储服务集群列表
// discoveryClient是用来从注册中心获取服务实例的工具类
List<ServiceInstance> orders = discoveryClient.getInstances("ORDERS");
// 使用迭代的方式来遍历获取到的集群服务列表的信息
orders.forEach(order ->{
log.debug("服务主机:{},服务端口:{},服务地址:{}",order.getHost(),order.getPort(),order.get
});
// 想了想,还是提醒一下,该日志记录的级别为dubug,一定要记得去配置文件中修改一下日志输出级别
可以先试用上述代码简单显示一下获取到的服务的集群列表。接下来我们可以使用随机数random
的方式来手动模拟随机负载均衡的方式。
public static ServiceInstance randomInstances(List<ServiceInstance> orders){
Random random = new Random();
return orders.get(random.nextInt(3));
}
@RequestMapping("/invokeDemo")
public String invokeDemo(){
RestTemplate restTemplate = new RestTemplate();
List<ServiceInstance> orders = discoveryClient.getInstances("ORDERS");
String result = restTemplate.getForObject(randomInstances(orders) + "/order/demo",String.class);
return "User's demo is ok..." + result;
}
Ribbon组件
Ribbon不用引入依赖,它在我们引入的Consul依赖中,所以我们只需要在项目中直接使用即可。
@Autowired
private LoadBalancerClient loadBalancerClient;
// 该对象在调用choose方法的时候,会默认按照轮循规则进行负载均衡的获取一个服务实例,返回给项目
ServiceInstance order = loadBalancerClient.choose("ORDERS");
String result = restTemplate.getForObject(order.getUri() + "/order/demo",String.class);
return "User's demo is ok..." + result;
这是我们会发现,调用服务轻松了很多,其实我们还可以更加快速的完成服务之间的调用。
// 每次调用其他服务的时候都需要使用RestTemplate对象,所以我们可以将它交给工厂来完成,同时在工厂构建这个对象时候为其添加注解来强化它的功能。
@Configuration
public class BeanConfig {
@Bean
// 该注解表示我们的RestTemplate对象带有了默认负载均衡的算法使用
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
这个时候在调用服务的时候可以这样写:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/invokeDemo")
public String invokeDemo(){
// 经过工厂创建的带有负载均衡的RestTemplate对象可以直接在调用方法中,将路径中加入需要调用的服务名,这个样子,该方法就可以根据提供的服务名来按照负载均衡的规则来选择一个服务实例进行连接操作。
String result = restTemplate.getForObject("http://ORDERS/order/demo",String.class);
return "User's demo is ok..." + result;
}
总结
本批笔记中记录的是consul的简单使用,同时也说明了简单负载均衡的集群选择。主要是理解思维,这方面的使用更注重思维的理解。