今天我们要学习的是consul在soringcloud中的使用。首先学习consul之前,我们应该看看consul的官网,对它有一个初步的认识。
consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。
CLIENT表示consul的client模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息。
SERVER表示consul的server模式,表明这个consul是个server,这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。
中间那个SERVER下面有LEADER的字眼,表明这个SERVER是它们的老大,它和其它SERVER不一样的一点是,它需要负责同步注册的信息给其它的SERVER,同时也要负责各个节点的健康监测。
server节点之间的数据一致性保证,一致性协议使用的是raft,而zookeeper用的paxos,etcd采用的也是taft。
consul采用http和dns协议,etcd只支持http
consul支持两种方式实现服务注册,一种是通过consul的服务注册http API,由服务自己调用API实现注册,另一种方式是通过json个是的配置文件实现注册,将需要注册的服务以json格式的配置文件给出。consul官方建议使用第二种方式。
consul支持两种方式实现服务发现,一种是通过http API来查询有哪些服务,另外一种是通过consul agent 自带的DNS(8600端口),域名是以NAME.service.consul的形式给出,NAME即在定义的服务配置文件中,服务的名称。DNS方式可以通过check的方式检查服务。
Consul使用gossip协议管理成员关系、广播消息到整个集群,他有两个gossip pool(LAN pool和WAN pool),LAN pool是同一个数据中心内部通信的,WAN pool是多个数据中心通信的,LAN pool有多个,WAN pool只有一个。
consul agent -dev
consul join IP
consul members
consul leave
点击“download”下载:使用命令启动
consul agent -dev
启动成功之后在地址:http://localhost:8500
使用HTTP API 注册个服务,使用[接口API](https://www.consul.io/api/agent/service.html API)调用
调用 http://localhost:8500/v1/agent/service/register PUT 注册一个服务。request body:
{
"ID": "userServiceId", //服务id
"Name": "userService", //服务名
"Tags": [ //服务的tag,自定义,可以根据这个tag来区分同一个服务名的服务
"primary",
"v1"
],
"Address": "127.0.0.1",//服务注册到consul的IP,服务发现,发现的就是这个IP
"Port": 9000, //服务注册consul的PORT,发现的就是这个PORT
"EnableTagOverride": false,
"Check": { //健康检查部分
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com", //指定健康检查的URL,调用后只要返回20X,consul都认为是健康的
"Interval": "10s" //健康检查间隔时间,每隔10s,调用一次上面的URL
}
}
使用curl调用
curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{
"ID": "userServiceId",
"Name": "userService",
"Tags": [
"primary",
"v1"
],
"Address": "127.0.0.1",
"Port": 8000,
"EnableTagOverride": false,
"Check": {
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com",
"Interval": "10s"
}
}'
结果
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 288 0 0 100 288 0 18000 --:--:-- --:--:-- --:--:-- 18000HTTP/1.1 200 OK
Vary: Accept-Encoding
Date: Wed, 26 Dec 2018 05:11:32 GMT
Content-Length: 0
刚刚注册了名为userService的服务,我们现在发现(查询)下这个服务
curl http://127.0.0.1:8500/v1/catalog/service/userService
返回的响应:
curl http://127.0.0.1:8500/v1/catalog/service/userService
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 891 100 891 0 0 28741 0 --:--:-- --:--:-- --:--:-- 28741[
{
"ID": "9b831a00-ae68-d575-5e51-df193897b834",
"Node": "vip-PC",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": ""
},
"ServiceKind": "",
"ServiceID": "userServiceId",
"ServiceName": "userService",
"ServiceTags": [
"primary",
"v1"
],
"ServiceAddress": "127.0.0.1",
"ServiceWeights": {
"Passing": 1,
"Warning": 1
},
"ServiceMeta": {},
"ServicePort": 8000,
"ServiceEnableTagOverride": false,
"ServiceProxyDestination": "",
"ServiceProxy": {},
"ServiceConnect": {},
"CreateIndex": 88,
"ModifyIndex": 88
}
]
基本的服务发现和注册我们已经弄清楚了。接下来我来看看Spring Cloud 整合consul的使用。
引入依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
com.lidong
spring-cloud-consul-producer
1.0.0
spring-cloud-consul-producer
Demo project for Spring Boot
1.8
Greenwich.RC2
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-consul-discovery
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
对配置文件做一个简单的介绍,我们使用的是最新版的springboot2.1.1,springcloud.Greenwich.RC2版本。
其中:
spring-boot-starter-actuator 健康检查依赖于此包。
spring-cloud-starter-consul-discovery Spring Cloud consul的服务发现支持。
server:
port: 9001 #提供者的端口
spring:
application:
name: spring-cloud-consul-producer
cloud:
consul:
host: localhost
port: 8500
discovery:
tags: dev
serviceName: spring-cloud-consul-producer # 注册到consul的服务名称
healthCheckPath: /actuator/health
healthCheckInterval: 15s
healthCheckUrl: http://127.0.0.1:9001/actuator/health
register: true
prefer-ip-address: false
consul的地址和端口号默认是127.0.0.1:8500,如果没有配置hosts,默认的地址localhost,consul服务会占用8500端口
server.port :9001 服务的提供者的端口
spring.application.name 是指注册到 consul的服务名称,后期客户端会根据这个名称来进行服务调用。
spring.application.cloud.discovery.discovery.host: localhost
spring.application.cloud.discovery.discovery. port:8500
添加 @EnableDiscoveryClient 注解,开启服务发现支持。
package com.lidong.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 开启服务发现
*/
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudLidongProviderApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudLidongProviderApplication.class, args);
}
}
新建 ConsulProducerController,提供 sayHello 接口, 返回一个hello—>字符串。
package com.lidong.provider.service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 创建服务
*/
@RestController
public class ConsulProducerController {
@Value("${server.port}")
private Integer port;
/**
* 服务接口
* @param name
* @return
*/
@RequestMapping("/hello")
public String sayHello(@RequestParam("name")String name) {
return "hello ---> "+name+" port -->"+port;
}
}
启动项目:
2018-12-26 13:21:42.984 INFO 20248 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9001 (http) with context path ''
2018-12-26 13:21:42.994 INFO 20248 --- [ main] o.s.c.c.s.ConsulServiceRegistry : Registering service with consul: NewService{id='spring-cloud-consul-producer-9001', name='spring-cloud-consul-producer', tags=[dev, secure=false], address='vip-PC', meta=null, port=9001, enableTagOverride=null, check=Check{script='null', interval='15s', ttl='null', http='http://127.0.0.1:9001/actuator/health', method='null', header={}, tcp='null', timeout='null', deregisterCriticalServiceAfter='null', tlsSkipVerify=null, status='null'}, checks=null}
2018-12-26 13:21:43.008 INFO 20248 --- [ main] l.p.SpringCloudLidongProviderApplication : Started SpringCloudLidongProviderApplication in 4.09 seconds (JVM running for 4.755)
服务提供者发布成功。
这时候,我们在控制台会发现服务列表中有一个名字为spring-cloud-consul-producer的服务
引入依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
com.lidong
spring-cloud-consul-consumer
0.0.1-SNAPSHOT
spring-cloud-consul-consumer
Demo project for Spring Boot
1.8
Greenwich.RC2
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-consul-discovery
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
server:
port: 9002 #服务消费者的端口
spring:
application:
name: spring-cloud-consul-consumer
cloud:
consul:
host: localhost
port: 8500
discovery:
tags: dev
register: false #设置不需要注册到 consul 中
healthCheckPath: /actuator/health
healthCheckInterval: 15s
healthCheckUrl: http://127.0.0.1:9002/actuator/health
consul的地址和端口号默认是 127.0.0.1:8500,如果没有配置hosts,默认的地址localhost,consul服务会占用8500接口
server.port :9006 服务的消费者的端口
spring.application.cloud.consul.discovery.register: false
package com.lidong.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudConsulConsumerApplication {
@Autowired
private RestTemplateBuilder builder;
@LoadBalanced
@Bean// 添加负载均衡支持,很简单,只需要在RestTemplate上添加@LoadBalanced注解,那么RestTemplate即具有负载均衡的功能,如果不加@LoadBalanced注解的话,会报java.net.UnknownHostException:springboot-h2异常,此时无法通过注册到Eureka Server上的服务名来调用服务,因为RestTemplate是无法从服务名映射到ip:port的,映射的功能是由LoadBalancerClient来实现的。
public RestTemplate restTemplate() {
return builder.build();
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsulConsumerApplication.class, args);
}
}
package com.lidong.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 创建服务的消费者
*/
@RestController
public class ConsumerController {
private static final String SERVICE_NAME = "spring-cloud-consul-producer";
@Autowired
private DiscoveryClient discoveryClient;
/**
* 获取所有服务
*/
@RequestMapping("/services")
public Object services() {
return discoveryClient.getInstances(SERVICE_NAME);
}
/**
* 消费服务
*/
@RequestMapping("/callSayHello")
public String services(@RequestParam("name") String name) {
ServiceInstance serviceInstance = (ServiceInstance) discoveryClient.getInstances(SERVICE_NAME);
String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
System.out.println(callServiceResult);
return callServiceResult;
}
}
http://localhost:9002/services
获取服务列表的结果
[{"instanceId":"spring-cloud-consul-producer-9001","serviceId":"spring-cloud-consul-producer","host":"vip-PC","port":9001,"secure":false,"metadata":{"dev":"dev","secure":"false"},"uri":"http://vip-PC:9001","scheme":null}]
测试请求的url
http://localhost:9002/callSayHello?name=9002
消费的结果
hello ---> 9002 port -->9001
源码地址
https://github.com/lidong1665/spring-cloud-learning-example