Spring Cloud 入门教程(一):服务治理(Eureka)(转)

Spring Cloud是一系列框架的集合,其基于Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,构建了服务治理(发现注册)、配置中心、消息总线、负载均衡、断路器、数据监控、分布式会话和集群状态管理等功能,为我们提供一整套企业级分布式云应用的完美解决方案。

Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。这些项目是Spring将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给我们开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

Spring Cloud 具有特性,以及适用于哪些场景等包含:

  • 基于版本的分布式配置管理
  • 服务注册与发现
  • 路由
  • 服务之间调用(依赖)
  • 负载均衡
  • 断路器
  • 全局锁(分布式锁)
  • 选主以及集群状态管理
  • 分布式消息服务

Spring Cloud的核心是服务治理。而服务治理主要通过整合Netflix的相关产品来实现这方面的功能,也就是Spring Cloud Netflix,在该项目中包括用于服务注册和发现的Eureka,调用断路器Hystrix,调用端负载均衡Ribbon,Rest客户端Feign,智能服务路由Zuul,用于监控数据收集和展示的Spectator、Servo、Atlas,用于配置读取的Archaius和提供Controller层Reactive封装的RxJava。除此之外,针对Feign和RxJava并不是Netiflix的产品,但也被整合到了Spring Cloud Netflix中。

接下来的几篇我将从Spring Cloud Netflix开始讲解如何搭建我们的分布式开发架构。

1. Hello, Spring Cloud!示例工程

我们所要搭建的Hello, Spring Cloud!系统架构图如下:

从结构图上可以看出有一下我们所构建的工程中有三种角色:

  • Eureka Server: 服务注册中心,负责服务列表的注册、维护和查询等功能;
  • Service Provider: 服务提供方,同时也是一个Eureka Client,负责将所提供的服务向Eureka Server进行注册、续约和注销等操作。注册时所提供的主要数据包括服务名、机器ip、端口号、域名等,从而能够使服务消费方能够找到;
  • Service Consumer: 服务消费方,同时也是一个Eureka Client,同样也会向Eureka Server注册本身所提供的服务。但在本示例工程中更多的是从Eureka Server中获取相应的服务列表,以便能够发起服务调用。

Service Provider(服务提供方)和Service Consumer(服务消费方)并不是一个严格的概念,往往服务消费方也是一个服务提供方,同时服务提供方也可能会调用其它服务方所提供的服务。当然在我们进行微服务构建时还是需要遵守业务层级之间的划分,尽量避免服务之间的循环依赖。

工程结构如下:

Ok! 既然工程结构和系统架构都清楚了,下面让我们开始撸起袖子写代码。

2. 构建parent工程

笔者在构建项目的时候喜欢先构建一个parent工程,该工程仅用来定义一个pom文件,后续工程的pom文件的皆继承该pom。在该pom中我们将定义各工程所共同使用的第三方依赖及相应版本定义,比如我们接下来的各工程中对Spring Cloud的依赖等。这样我们就可以统一对第三方依赖及基础信息定义进行管理,后续当我们需要升级第三方依赖时,只需要修改一个地方就可以了。

parent pom文件中的内容如下:


<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0modelVersion>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>1.5.2.RELEASEversion>
    parent>

    <groupId>twostepsfromjava.cloudgroupId>
    <artifactId>twostepsfromjava-cloud-parentartifactId>
    <version>1.0.0-SNAPSHOTversion>
    <packaging>pompackaging>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Dalston.SR1version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.5.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                configuration>
            plugin>
        plugins>
    build>    
project>
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

在本系列文章中我们使用的Spring Cloud的版本为:Dalston.SR1Spring Boot则是1.5.2.RELEASE

关于Spring Cloud的命名:由于Spring Cloud是诸多子项目集合的综合项目,原则上由其子项目维护自己的发布版本号,也就是我们常用的版本号,如:1.2.3.RELEASE1.1.4.RELEASE等。因此Spring Cloud为了避免版本号与其子项目的版本号混淆,所以没有采用版本号的方式,而是采用命名的方式。这些版本名称采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序。比如,最早的Release版本名称为Angel,第二个Release版本的名称为Brixton,以此类推……。而我们在本系列文章所使用的版本名称为:Dalston.SR1,也就是最新版本。后续版本名称根据项目中公布的分别为: EdgwareFinchley

另,Dalston.SR1中的SRservice releases的简写,而1则是该版本名称中的第1个版本。

具体关于Spring Cloud版本的命名说明可以参考这里.

3. 构建Eureka Server

3.1 编写pom.xml文件

我们将继承parent项目的pom.xml,并把artifactId定义为:service-discovery


<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloudgroupId>
        <artifactId>twostepsfromjava-cloud-parentartifactId>
        <version>1.0.0-SNAPSHOTversion>
        <relativePath>../parentrelativePath>
    parent>

    <artifactId>service-discoveryartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-eureka-serverartifactId>
        dependency>
    dependencies>    
project>
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这里我们直接继承parent项目中的pom,所以只需要声明我们需要的新增的spring-cloud-starter-eureka-server依赖即可。

3.2 编写启动类

@EnableEurekaServer
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

说明: 这里核心就是在启动类上添加@EnableEurekaServer,声明这是一个Eureka服务器。

3.3 编写配置文件

配置文件在resources目录下,默认名称为:application.properties(本系列中将采用properties文件格式,你也可以使用另外一种格式:yml)。

server.port=8260

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里为什么这么配,暂时先不解释,后续我会进行相关配置参数的解释。

3.4 启动服务器

接下来你可以在你的IDE中启动该服务。当然你也可以将该服务器打包成一个Fat Jar,然后通过java -jar的命令启动,如:

java -jar service-discovery-1.0.0-SNAPSHOT.jar
     
     
     
     
  • 1

说明: 如果需要打包成一个Fat Jar你需要修改pom.xml中的配置,增加如下内容:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

增加一个Spring Boot打包插件。这样编译出来的Jar包就可以通过上述命令直接运行。

3.5 查看服务器

你可以直接在浏览器中输入以下地址http://localhost:8260,在笔者的笔记本中界面如下:

Instance currently registered with Eureka部分可以看到现在尚未有任何实例注册进来。

4. 构建Eureka Client

Eureka服务器我们已经编写好了,接下来我们就可以编写一个Eureka的客户端了。这个客户端可能是一个服务提供者,也可能是一个服务消费者,甚至两者都是。

我们先编写一个简单的Eureka Client,该客户端提供一个简单的服务,就是调用/hello服务端点(EndPoint)时返回一个字符串Hello, Spring Cloud!

4.1 编写pom.xml文件

同样,我们继承自parent项目的pom.xml,这里将artifactId定义为:service-hello,也就是提供Hello服务。


<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloudgroupId>
        <artifactId>twostepsfromjava-cloud-parentartifactId>
        <version>1.0.0-SNAPSHOTversion>
        <relativePath>../parentrelativePath>
    parent>

    <artifactId>service-helloartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-eurekaartifactId>
        dependency>        
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

4.2 编写启动类

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

}
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

说明: 这里与service-discovery的唯一区别就是启动类上注解变成了@EnableDiscoveryClient,声明这是一个Eureka Client。

4.3 编写一个简单的API服务

@RestController
public class HelloEndpoint {
    protected Logger logger = LoggerFactory.getLogger(HelloEndpoint.class);

    @Autowired
    private EurekaInstanceConfig eurekaInstanceConfig;
    @Value("${server.port}")
    private int serverPort = 0;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        this.logger.info("/hello, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
        return "Hello, Spring Cloud! My port is " + String.valueOf(serverPort);
    }
}
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

该服务仅提供一个/hello服务端点,调用该服务后将返回一个字符串Hello, Spring Cloud!

4.4 编写配置文件

server.port=2100

spring.application.name=SERVICE-HELLO

eureka.client.service-url.defaultZone=http://localhost:8260/eureka
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5

说明: 这里spring.application.name必须要设置,服务消费者将通过该名称调用所提供的服务。 eureka.client.service-url也必须设置,表示我们要向那些Eureka服务器进行服务注册,这里可以声明多个Eureka服务器,具体我们将在后面关于Eureka高可用相关章节中进行详细说明。

4.5 启动服务器

同样启动该服务器。启动成功后,我们将在控制台上看到这么一句日志:

[DiscoveryClient-InstanceInfoReplicator-0] INFO  c.netflix.discovery.DiscoveryClient - DiscoveryClient_SERVICE-HELLO/192.168.0.105:SERVICE-HELLO:2100 - registration status: 204
     
     
     
     
  • 1

这时候我们回到浏览器,刷新http://localhost:8260,将会看到如下界面:

说明我们的服务已经在Eureka服务器上注册成功。

5. 构建服务消费者

到上一小节其实一个最简单的Eureka服务器和客户端就已经构建完毕了。为了让我们更能够体会到Eureka所发挥的作用,我们下面来构建一个服务消费者,该服务消费者将调用SERVICE-HELLO所提供的服务。

5.1 编写pom.xml文件

同样,我们继承自parent项目的pom.xml,这里将artifactId定义为:consumer-hello,也就是Hello服务消费者。


<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloudgroupId>
        <artifactId>twostepsfromjava-cloud-parentartifactId>
        <version>1.0.0-SNAPSHOTversion>
        <relativePath>../parentrelativePath>
    parent>

    <artifactId>consumer-helloartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-eurekaartifactId>
        dependency>        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-ribbonartifactId>
        dependency>        
    dependencies>    
project>
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

这里需要注意的是我们除了依赖spring-cloud-starter-eureka,还依赖了Spring Cloud中的另外一个子项目spring-cloud-starter-ribbon,该子项目提供客户端负载均衡功能,可以自动从Eureka服务器中获取服务提供者的地址列表,从而能够发起相应的调用。这个后面我们将详细进行说明,这里先引入进来就可以了。

5.2 编写启动类

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Service-Hello一样在启动类上注解了@EnableDiscoveryClient,说明这也是一个Eureka Client。

5.3 编写服务调用

@RestController
public class HelloController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
    }
}
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

该服务调用时一个标准的controllerhello()方法将通过restTemplate调用SERVICE-HELLO/hello服务并返回。

5.4 编写配置文件

server.port=8080

spring.application.name=consumer-hello

eureka.client.service-url.defaultZone=http://localhost:8260/eureka
     
     
     
     
  • 1
  • 2
  • 3
  • 4
  • 5

5.5 启动服务器

启动成功后,同样我们将在控制台上看到这么一句日志:

[DiscoveryClient-InstanceInfoReplicator-0] INFO  c.netflix.discovery.DiscoveryClient - DiscoveryClient_CONSUMER-HELLO/192.168.0.105:consumer-hello:8080 - registration status: 204
     
     
     
     
  • 1

然后我们回到浏览器,刷新http://localhost:8260,将会看到如下界面:

说明我们的两个服务都已经在Eureka服务器上注册成功。

5.6 验证服务调用

在浏览器中,我们输入http://localhost:8080/hello,也就是该服务所定义的端口server.port=8080,将会看到如下界面:

同时在Service-Hello的控制台中会打印下面一句日志:

[http-nio-2100-exec-1] INFO  i.t.c.s.hello.api.HelloEndpoint - /hello, instanceId:cd826dembp.lan:SERVICE-HELLO:2100, host:192.168.0.105
     
     
     
     
  • 1

Ok,到这里为止,我们的Hello, Spring Cloud!示例工程搭建完毕。

zhuan’h

你可以到这里下载本篇的代码。
下一篇: 《Spring Cloud入门教程(二):客户端负载均衡(Ribbon)》

        
            

你可能感兴趣的:(java,架构知识)