Spring Cloud Eureka

                       Spring Cloud基础教程Eureka

 Spring Cloud简介

Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud0 CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。

微服务架构

微服务架构”在这几年非常的火热,以至于关于微服务架构相关的开源产品被反复的提及(比如:netflix、dubbo),Spring Cloud也因Spring社区的强大知名度和影响力也被广大架构师与开发者备受关注。

那么什么是“微服务架构”呢?简单的说,微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API的方式互相调用。

对于“微服务架构”,大家在互联网可以搜索到很多相关的介绍和研究文章来进行学习和了解。也可以阅读始祖Martin Fowler的《Microservices》,本文不做更多的介绍和描述。 

注意:本文是建立在SpringBoot基础进行代码演示.

服务治理

在简单介绍了Spring Cloud和微服务架构之后,下面回归本文的主旨内容,如何使用Spring Cloud来实现服务治理。

由于Spring Cloud为服务治理做了一层抽象接口,所以在Spring Cloud应用中可以支持多种不同的服务治理框架,比如:Netflix Eureka、Consul、Zookeeper。在Spring Cloud服务治理抽象层的作用下,我们可以无缝地切换服务治理实现,并且不影响任何其他的服务注册、服务发现、服务调用等逻辑。

所以,下面我们通过介绍两种服务治理的实现来体会Spring Cloud这一层抽象所带来的好处。

Spring Cloud Eureka【Dalston版】

首先,我们来尝试使用Spring Cloud Eureka来实现服务治理。

Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。

下面,就来具体看看如何使用Spring Cloud Eureka实现服务治理。

github地址:[email protected]:13849141963/spring-cloud.git

创建“服务注册中心”

创建一个基础的Spring Boot工程,命名为springcloud-eureka,并在pom.xml中引入需要的依赖内容:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!--Eureka服务注册中心-->
          <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>

        <!--生产环境下给Eureka注册中心添加安全的用户认证-->
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->

        <!-- 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.yml配置文件中增加如下信息:

spring:
  application:
    #应用名称
    name: Eureka-application
    #Eureka注册中心访问权限  使用时在pom.xml文件中添加spring-boot-starter-security的座标
  #security:
    #user:
     # name: admin
     # password: admin
    
eureka:
  server:
    enable-self-preservation: false #关闭自我保护模式(默认为打开)
  instance:
    #服务注册中心实例的主机名
    hostname: 10.0.45.103
  client:
    #是否向服务注册中心注册自己
    register-with-eureka: false
    #是否检索服务
    fetch-registry: false
    serviceUrl:
      ##服务注册中心的配置内容,指定服务注册中心的位置
      #defaultZone: http://admin:admin@${eureka.instance.hostname}:${server.port}/eureka/  # 安全的注册地址
     defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    registry-fetch-interval-seconds: 30
    #开启客户端存活状态监测

server:
  #服务注册中心端口号
  port: 8088

#热部署
devtools:
  restart:
    #不加在这些配置文件
    exclude: static/**,templates/**
    enabled: true

1、eureka.client.register-with-eureka: 对于同一个进程,既可以作为注册中心的服务端,也可以作为注册中心的客户端,在本例中,注册中心提供单一的注册服务功能,不提供其他接口服务能力,所以设置为false,最终不作为服务实例注册在注册中心中供其他服务调用。 
2、eureka.server.enable-self-preservation
是否开启自我保护模式,默认为true。

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。

Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

当我们在本地调试基于Eureka的程序时,基本上都会碰到这样一个问题,在服务注册中心的信息面板中出现类似的红色警告信息:

实际上,该警告就是触发了Eureka Server的自我保护机制。之前我们介绍过,服务注册到Eureka Server之后,会维护一个心跳连接,告诉Eureka Server自己还活着。Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境中通常是网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,让这些实例不会过期,尽可能的保护这些注册信息。在这段保护 期间内实例出现问题,那么客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如可以使用请求重试、断路器等机制。 
由于本地调试很容易触发注册中心的保护机制,这会使得注册中心维护的服务实例不那么准确。所以,我们在本地进行开发的时候,可以使eureka.server.enable-self-preservation=false参数关闭保护机制,以确保注册中心可以将不可用的实例正确剔除。
3、eureka.server.eviction-interval-timer-in-ms
eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒。服务下线存在两种情况,一种是客户端主要发送“服务下线”的请求,另外一种是通过定时任务扫描,每隔固定的时间清理无效的节点信息。清理无效节点的策略由客户端决定,客户端节点会设置相应的上报时间和最大超时时间(最大超时时间必须超过上报时间,推介设置为3倍关系),eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance。
 

spring cloud服务发现注解之@EnableDiscoveryClient与@EnableEurekaClient的作用以及各自的区别

通过@EnableEurekaServer注解启动一个Eureka服务注册中心提供给其他应用进行对话。这一步非常的简单,只需要在一个普通的Spring Boot应用中添加这个注解就能开启此功能,比如下面的例子:启动

package com.zy.cn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer//该注解表示使用Eureka服务注册中心
@SpringBootApplication
public class SpringcloudEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudEurekaApplication.class, args);
    }
}

了与后续要进行注册的服务区分,这里将服务注册中心的端口通过server.port属性设置为1001。启动工程后,访问:http://10.0.45.103:8088/,可以看到下面的页面,其中还没有发现任何服务。

创建“服务提供方”

下面我们创建提供服务的客户端,并向服务注册中心注册自己。本文我们主要介绍服务的注册与发现,所以我们不妨在服务提供方中尝试着提供一个接口来获取当前所有的服务信息。

首先,创建一个基本的Spring Boot应用。命名为springcloud-provider,在pom.xml中,加入如下配置:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
       
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.26</version>
        </dependency>

      
        <!-- Spring boot 热部署 : 此热部署会遇到 java.lang.ClassCastException 异常 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--aop切面  做接口的安全校验-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>

        <!--在Eureka 注册中心注册服务-->
          <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其次,实现请求处理接口,接口内容

package com.zy.cn.controller;
import com.zy.cn.entity.User;
import com.zy.cn.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/***
 * 操作用户的接口
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    //@GetMapping("/queryAll") 等于下面的注解
    @RequestMapping(value = "/queryAll",method = RequestMethod.GET)
    public Object queryAll(){
        List<User> users = userService.queryAll();
        return users;
    }

    @RequestMapping(value = "/insert",method = RequestMethod.POST)
    public Object insert(@RequestBody User user){
        userService.inseret(user);
        return "OK";
    }

    @RequestMapping(value = "/find/{id}",method = RequestMethod.GET)
    public Object find(@PathVariable("id") Integer id){
        User user = userService.find(id);
        return user;
    }


}

我们在完成了服务内容的实现之后,再继续对application.yml做一些配置工作,具体如下:端口为8081

server:
  port: 8081
  servlet:
    context-path: /springcloud-provider
  tomcat:
    uri-encoding: UTF-8
#mybatis相关配置信息
mybatis:
  mapper-locations: com/zy/cn/mapper/*.xml
  type-aliases-package: com.zy.cn.entity


spring:
  application:
    #应用名称
    name: springcloud-provider
    #mysql相关配置信息
  datasource:
    url: jdbc:mysql://10.0.45.103/mysql-test?characterEncoding=utf8&serverTimezone=GMT%2B8
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    #http编码格式
  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

#热部署
devtools:
  restart:
    exclude: static/**,templates/**
    enabled: true
  jpa:
    database: MYSQL
    show-sql: true
#在Eureka注册中心进行注册服务
eureka:
  client:
    service-url:
      #注册地址
      defaultZone: http://10.0.45.103:8088/eureka

1、eureka.instance.lease-renewal-interval-in-seconds
lease-renewal-interval-in-seconds,表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量,默认30秒。

2、eureka.instance.lease-expiration-duration-in-seconds
lease-expiration-duration-in-seconds,表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,该值默认为90秒。

3、eureka.client.registry-fetch-interval-seconds
表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒。

最后在应用主类中通过加上@EnableEurekaClient注解,该注解能激活Eureka中的EurekaClient实现,这样才能实现Controller中对服务信息的输出。

package com.zy.cn;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@MapperScan("com.zy.cn.dao")
@EnableEurekaClient
@SpringBootApplication
public class SpringcloudProviderApplication  {
	public static void main(String[] args) {
		SpringApplication.run(SpringcloudProviderApplication.class, args);
	}
}

通过spring.application.name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。eureka.client.serviceUrl.defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。为了在本机上测试区分服务提供方和服务注册中心,使用server.port属性设置不同的端口。

启动该工程后,再次访问:http://10.0.45.103:8081/。可以如下图内容,我们定义的服务被成功注册了。

 当然,我们也可以通过直接访问springcloud-provider服务提供的/queryAll接口来获取当前的服务清单,只需要访问:http://10.0.45.103:8081/springcloud-provider/queryAll,我们可以得到如下输出返回:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章