欢迎访问Spring Cloud中国社区

《重新定义Spring Cloud实战》由Spring Cloud中国社区倾力打造,基于Spring Cloud的Finchley.RELEASE版本,本书内容宽度足够广、深度足够深,而且立足于生产实践,直接从生产实践出发,包含大量生产实践的配置。欢迎加微信Software_King进群答疑,国内谁在使用Spring Cloud?欢迎登记

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

xujin · 5年前 · 1722 ·

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

1.什么是服务注册与发现

1.1 服务注册与发现

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

2.1 服务化早期的做法

2.2 示例工程说明

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

参考工程

2.3 Spring MVC中基于无状态的REST

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

@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实现服务注册与发现的处理。

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

3.1 搭建注册中心-Eureka Server

1.引入依赖

<?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
    ```yml
    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主应用程序启动代码
```java
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

3.2 创建服务提供者

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

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

@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如下图所示。
安全模式

3.3 创建服务消费者

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

 <?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.主程序入口代码

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。
@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获取服务注册信息
    ```java
    @Autowired
    private EurekaClient discoveryClient;

public String serviceUrl() {
InstanceInfo instance = discoveryClient.getNextServerFromEureka(“STORES”, false);
return instance.getHomePageUrl();
}

 2. 使用DiscoveryClient获取服务注册信息
 ```java
 @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。。