使用Spring Cloud Eureka实现服务注册与发现

文章目录
  1. 1. 什么是服务注册与发现
    1. 1.1. 服务注册与发现
  2. 2. 服务化早期的做法
    1. 2.1. 示例工程说明
    2. 2.2. Spring MVC中基于无状态的REST
  3. 3. 使用Eureka实现服务的注册与发现
    1. 3.1. 搭建注册中心-Eureka Server
    2. 3.2. 创建服务提供者
    3. 3.3. 创建服务消费者
  4. 4. 测试
    1. 4.1. 获取消费者获取服务端消费列表
  5. 5. 小结

摘要:由于目前,网上的Spring Cloud的学习的案列,比较凌乱而且没有形成整个体系,因此特开一个专题为跟我学Spring Cloud,希望帮助到有需要的人。本文主要介绍如何使用Spring Cloud中的Eureka组件快速实现微服务的服务注册与发现。至于安全模式和Eureka Server的HA,后面的文章会详细介绍。如果您觉得,有想了解的内容,参与评论留言。

什么是服务注册与发现

服务注册与发现

在服务化的早期,服务不是很多,服务的注册与发现并不是什么新鲜的名词,Nginx+内部域名服务器方式,甚至Nginx+host文件配置方式也能完成服务的注册与发现。服务上下线需要在nginx,服务器做相应的配置,一旦服务的IP端口发生变化,都需要在nginx上做相应的配置,为了解决这个问题引入服务注册中心。
服务注册,即服务在启动的时候就将服务的IP,端口,版本号等EndPoint注册到注册中心(Eueka,Zookeeper,Consul)对服务进行统一管理.
服务发现,简单的就是说,不管服务上下线,当对某个服务发起请求时,能够快速的从本地缓存或者注册中心的注册列表中,快速找到服务提供者。

服务化早期的做法

示例工程说明

Tips:代码示例:https://github.com/SoftwareKing/spring-cloud-study/tree/master/sc-eureka-first

参考工程

Spring MVC中基于无状态的REST

工程可以参考sc-rest-demo下面的sc-rest-provider和sc-rest-consumer,具体使用如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping("/sc")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
// 从属性文件中读取服务提供的URL
@Value("${order.orderServiceUrl}")
private String orderServiceUrl;
@GetMapping("/consumer/{id}")
public OrderModel getOrderInfo(@PathVariable Long id) {
// this.restTemplate.getForObject("http://localhost:8000/sc/order/" +
// id,OrderModel.class);
return this.restTemplate.getForObject(this.orderServiceUrl + "/sc/order/" + id,
OrderModel.class);
}
}

大家注意到没,把http://localhost:8000 ,硬编码到程序中,是不是比较low。可以采用上面代码中的方式:orderServiceUrl解决。但是这样还是比较low,下面介绍一下引入Eureka实现服务注册与发现的处理。

使用Eureka实现服务的注册与发现

搭建注册中心-Eureka Server

1.引入依赖

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
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<!-- 引入spring boot的依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<artifactId>sc-eureka-first-server-HA01</artifactId>
<name>sc-eureka-first-server-HA01</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 引入Spring Cloud Eureka依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- 引入spring cloud的依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 添加spring-boot的maven插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

  1. 在Resources目录下创建application.yml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    server:
    port: 8761 # 指定该Eureka实例的端口
    eureka:
    client:
    #表示是否将自己注册到Eureka Server上,默认为true,当前应用为Eureka Server所以无需注册
    registerWithEureka: false
    #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。
    fetchRegistry: false
    #Eureka Server的访问地址,服务注册和client获取服务注册信息均通过该URL,多个服务注册地址用,隔开
    serviceUrl:
    defaultZone: http://localhost:8761/eureka/
    # 参考文档:http://projects.spring.io/spring-cloud/docs/1.0.3/spring-cloud.html#_standalone_mode
    # 参考文档:http://my.oschina.net/buwei/blog/618756

3.创建Spring Boot主应用程序启动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.xujin.sc.eureka.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Eureka Server
* @author xujin
*/
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaServer {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServer.class, args);
}
}

启动Eureka server测试:
启动sc-eureka-first-server-HA01,访问http://localhost:8761/ ,如下图所示:

Eureka server

创建服务提供者

1.服务提供者,为了演示在这里提供一个简单的订单查询服务,如工程sc-eureka-first-provider01sc-eureka-first-provider02所示。
2.主程序入口代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.xujin.sc.eureka.first.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 服务提供者端,加上@EnableDiscoveryClient注解,完成服务注册。
* @author xujin
* @site http://xujin.org
*/
@SpringBootApplication
@EnableDiscoveryClient
// @EnableEurekaClient
public class OrderProviderSpringBootAppliaction {
public static void main(String[] args) {
SpringApplication.run(OrderProviderSpringBootAppliaction.class, args);
}
}

Tips:如果使用Eureka, 可以使用@EnableEurekaClient注解,但是推荐使用@EnableDiscoveryClient代替@EnableEurekaClient注解,因为@EnableDiscoveryClient是一个高度的抽象, 来自于spring-cloud-commons, 由于Spring Cloud选型是中立的因此抽象出该接口, 当服务注册中心选型改变为Eureka,ZK,Consul时,不需要修改原有代码中的注解。

3.服务提供者暴露的服务-OrderController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/sc/order/{id}")
public OrderModel findOrderById(@PathVariable Long id) {
OrderModel orderModel = orderService.findOrderByOrderId(id);
return orderModel;
}
}

启动服务提供者,把服务注册信息,注册到Eureka Server注册中心
启动sc-eureka-first-provider01,当启动其中一个服务后刷新Eureka Server会出现安全模式,如下图所示:
安全模式

启动sc-eureka-first-provider02,刷新Eureka Server如下图所示。
安全模式

创建服务消费者

服务消费者主要是一个简单的用户服务,用户服务查询订单服务的订单信息。
1.引入相应的依赖

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.xujin.sc</groupId>
<artifactId>sc-eureka-first-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sc-eureka-first-consumer</name>
<url>http://maven.apache.org</url>
<!-- 引入spring boot的依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- 引入spring cloud的依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 添加spring-boot的maven插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

2.主程序入口代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.xujin.sc.eureka.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//消费者端加入服务发现注解
@EnableDiscoveryClient
@SpringBootApplication
public class UserConsumerApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
}

  1. 消费者调用Controller。
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
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
// discoveryClient获取服务列表中,应用名为sc-eureka-first-provider一个服务注册信息
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient
.getInstances("sc-eureka-first-provider");
if (list != null && list.size() > 0) {
return String.valueOf(list.get(0).getUri());
}
return null;
}
@GetMapping("/sc/user/{id}")
public Order findByIdByEurekaServer(@PathVariable Long id) {
String providerServiceUrl = serviceUrl();
return this.restTemplate.getForObject(providerServiceUrl + "sc/order/" + id,
Order.class);
}
}

如上述代码,所示使用discoveryClient.getInstances("sc-eureka-first-provider")获取服务名为sc-eureka-first-provider的服务注册列表信息。

测试

先后启动sc-eureka-first-consumer,如没有异常,打开浏览器访问:http://localhost:8010/sc/user/2 ,debug如下所示可以看到
服务提供者端服务发现

在刷新一下Eureka Server,如图下所示,此时安全模式关闭。
Eureka Server

关于安全模式,在本篇文章中,暂不讨论,后面将会专写一篇文章介绍,请暂时忽略。

获取消费者获取服务端消费列表

  1. 使用EurekaClient获取服务注册信息

    1
    2
    3
    4
    5
    6
    7
    @Autowired
    private EurekaClient discoveryClient;
    public String serviceUrl() {
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
    return instance.getHomePageUrl();
    }
  2. 使用DiscoveryClient获取服务注册信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Autowired
    private DiscoveryClient discoveryClient;
    public String serviceUrl() {
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
    return list.get(0).getUri();
    }
    return null;
    }

    参考链接:https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc

小结

上面这个例子使用Eureka实现了服务的注册与发现,但是有一个问题就是获取服务注册列表的方式比较low并且太方便,还有一个问题就是没有使用负载均衡(Load Balance),这样就没法实现微服务的HA。在后面的文章将会介绍Eureka Server的HA和使用Robbin实现LB。。

如果您觉得文章不错,可以打赏我喝一杯咖啡!