Spring Cloud 与 Consul

Posted by aclyyx on 11-09,2018

Spring
统一开头:

最近的工作安排中,需要把曾经庞大的、臃肿的综合管理“大平台”拆分,引入微服务的概念;找找看看一段时间仍然不晓得微服务到底是个啥。浅薄的认为就是一个个小的、功能相对单一的服务程序。

在不明白为何物的情况下怎么快速上手?搜罗了许多文章后找到了【纯洁的微笑】的博客,里面东西不老少。其中推荐了几个开源的软件,从中找到了《Cloud-Admin》。

在《Cloud-Admin》中,分别使用到RabbitMQRedisMySQLConsul 于是开始逐个攻克。

Consul简介

Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

个人理解:Consul 是一个服务中心(注册中心),负责各个微服务之间的调用调度。有了新的供应服务到这里来注册一下;有消费端到这里来找找有没有需要的服务,有就拿去用。服务的注册、发现、熔断、负载、降级都在这里搞定。

Docker部署Consul

为了方便开发,在 Docker 上运行 Consul 容器来解决问题,通过docker search consul可以看到在镜像库中已经有了Consul镜像,在【docker doc】中也有相关的说明。

首先把镜像拽下来docker pull consul,拉取最新的版本,通过下面的命令创建并运行容器。

docker run -d --name springcloud-consul \
 -p 8500:8500 -p 8600:8600 -p 8600:8600/udp \
 consul
  • -d: 后台运行
  • --name springcloud-consul:指定容器的名字
  • -p xxxx:xxxx/udp:绑定宿主机和容器的UDP协议端口
  • -p xxxx:xxxx:绑定宿主机和容器的TCP协议端口
    8300/8301/8302:Consul内部通信使用的端口,单个主机环境中这三个端口用不到;
    8500:HTTP端口
    8600:DNS端口

运行上述命令后,就已经在Docker中启动了一个Consul容器,打开浏览器输入http://127.0.0.1:8500/ui/可以看到如下页面,证明Consul服务启动成功。
Consul服务启动成功

编写测试程序

测试程序参考了《springcloud(十三):注册中心 Consul 使用详解》文章。

开发工具:IntelliJ IDEA 2018.2.5
Java环境:1.8.0_162
SpringBoot版本:2.1.0.RELEASE

创建项目

今次首先在IDEA中创建一个空项目,之后再在其中创建3个Module,分别是一号供应商、二号供应商、需求方。

创建空项目

通过菜单在项目中创建“Module”

创建Module

一号供应商

创建 Module 向导

项目的Artifact填写为producer1(一号供应商),Next

创建Module

在向导中依赖这里分别选择Web -> WebCloud Discovery -> Consul DiscoveryOps -> Actuator

  • spring-boot-starter-actuator 健康检查依赖于此包。
  • spring-cloud-starter-consul-discovery Spring Cloud Consul 的支持。

创建Module

填写代码

Application主类中,添加注解@EnableDiscoveryClient表示支持服务发现。

@SpringBootApplication
@EnableDiscoveryClient
public class Producer1Application {

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

Spring Boot 配置文件中添加如下配置信息。

spring.application.name=spring-cloud-consul-producer1
server.port=8501
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#注册到consul的服务名称
spring.cloud.consul.discovery.serviceName=service-producer

创建一个Controller提供具体服务。

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello consul 1";
    }
}

运行一号供应商的Spring Boot程序,启动服务;刷新“http://127.0.0.1:8500/ui/”页面,可以看到Services里注册了一个service-producer服务,并有两个节点(猜测一个是注册节点、一个是心跳检测节点)。
添加了一个供应商

二号供应商

像一号供应商一样,如法炮制;不同的地方我们修改一下供应商的信息,把原先填“1”的地方都写“2”。

Spring Boot 配置文件中添加如下配置信息,注意端口号为8502

spring.application.name=spring-cloud-consul-producer2
server.port=8502
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#注册到consul的服务名称
spring.cloud.consul.discovery.serviceName=service-producer

同样创建一个Controller提供具体服务,这里返回的信息为“2”。

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello consul 2";
    }
}

运行二号供应商的Spring Boot程序,启动服务;刷新“http://127.0.0.1:8500/ui/”页面,可以看到Services里的service-producer服务又多了两个节点。至此,两个供应商都报名成功。
添加了一个供应商

需求方

创建 Module 向导

项目的Artifact填写为consumerNext

在向导中依赖这里分别选择Web -> WebCloud Discovery -> Consul Discovery

填写代码

Application主类无需修改。

创建测试 Controller 类 ServiceController。

@RestController
public class ServiceController {

    @Autowired
    private LoadBalancerClient loadBalancer;
    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 获取所有服务
     */
    @RequestMapping("/services")
    public Object services() {
        return discoveryClient.getInstances("service-producer");
    }

    /**
     * 从所有服务中选择一个服务(轮询)
     */
    @RequestMapping("/discover")
    public Object discover() {
        return loadBalancer.choose("service-producer").getUri().toString();
    }

    /**
     * 选择服务,享受供应商提供的服务(轮询)
     */
    @RequestMapping("/call")
    public String call() {
        ServiceInstance serviceInstance = loadBalancer.choose("service-producer");
        System.out.println("服务地址:" + serviceInstance.getUri());
        System.out.println("服务名称:" + serviceInstance.getServiceId());

        String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
        System.out.println(callServiceResult);
        return callServiceResult;
    }
}

Spring Boot 配置文件中添加如下配置信息。

spring.application.name=spring-cloud-consul-consumer
server.port=8503
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#设置不需要注册到 consul 中
spring.cloud.consul.discovery.register=false
spring.cloud.consul.discovery.instance-id=service-producer

项目结构

一号供应商与二号供应商接口类似,就不再展开。

项目结构

运行测试

查看已注册的供应商

浏览器访问http://localhost:8503/services返回如下信息,可以看到在服务中心注册了两个serviceIdservice-producer的服务,并且可以看到地址、端口等信息。

[{
    "serviceId": "service-producer",
    "host": "aclyyxdeimac.lan",
    "port": 8501,
    "secure": false,
    "metadata": { "secure": "false" },
    "uri": "http://aclyyxdeimac.lan:8501",
    "scheme": null
}, {
    "serviceId": "service-producer",
    "host": "aclyyxdeimac.lan",
    "port": 8502,
    "secure": false,
    "metadata": { "secure": "false" },
    "uri": "http://aclyyxdeimac.lan:8502",
    "scheme": null
}]

选择一个供应商查看服务地址(轮询)

浏览器访问http://localhost:8503/discover并反复刷新,返回如下两条信息交替显示。

http://aclyyxdeimac.lan:8501

http://aclyyxdeimac.lan:8502

享受服务(轮询)

浏览器访问http://localhost:8503/call并反复刷新,返回如下两条信息交替显示,说明两个供应商的不服务交替被调用并返回执行结果。

Hello consul 1

Hello consul 2

参考

《纯洁的微笑》Blog
Cloud-Admin
springcloud(十三):注册中心 Consul 使用详解 <--重点