【SpringCloud】SpringCloud(一)之Eureka

文章目录

前言

一、SpringCloud是什么?

二、Eureka入门

1.介绍

2.入门案例

编写EurekaServer

服务注册(user-service)

服务发现(user-consumer)

2.Eureka详解

2.1 基础架构

2.2 高可用的EurekaServer

2.3 Eureka客户端

2.4 服务下线、失效剔除和自我保护

前言

       微服务是一种架构方式,最终是通过技术架构去实现的,当前最火的莫过于SpringCloud.

  • SpringCloud作为Spring家族中的一员,有整个Spring全家桶靠山,背景十分强大
  •  Spring作为Java领域的前辈,功力深厚,有强力的技术团队支撑(技术强)
  • Spring框架可以说是当下程序员伴随成长的框架.(群众基础好),SpringCloud与Spring的各个框架无缝整合.
  • SpringBoot给我们开发带来了便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建

一、SpringCloud是什么?

        SpringCloud是Spring旗下的项目之一,Spring最擅长的是集成,将世界上最好的框架集成到自己的项目中,而SpringCloud也是一样,将现在市面上流行的一些技术整合到一起,实现了诸如:配置管理、服务发现、智能路由、负载均衡、熔断器、控制总线、集群状态等等功能,其主要设计的软件如下:

  • Eureka:注册中心

  • Zuul:服务网关

  • Ribbon:负载均衡

  • Feign:服务调用

  • Hystrix:熔断器

  • config: 配置中心

  • Bus : 消息总线

                                     

二、Eureka入门

1.介绍

        Eureka主要解决了服务的管理问题,在未使用Eureka的时候,生产者对外提供服务的时候,需要暴露自己的地址,而消费者需要记录服务提供者的地址。如果在将来提供者的地址发生变更,此时消费者需要及时更新。如果服务较少还可以,随着项目的增大,一个项目可能会拆分出十几甚至几十个微服务,如果此时还要人为的管理地址,在测试和发布的时候也会非常麻烦。

       这就好比是 网约车出现以前,人们出门叫车只能叫出租车。一些私家车想做出租却没有资格,被称为黑车。而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦,而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少引子,缺乏管理啊。

此时滴滴这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。

此时要叫车的人,只需要打开APP,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务,完美!

        Eureka就好比是滴滴,负责管理、记录服务提供者(生产者)的信息。服务调用者(消费者)无需自己寻找服务,而是将自己的需求告诉Eureka,然后Eureka将符合需求的服务告诉消费者。同时服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然将其从服务中剔除,实现了服务的自动注册、发现和监控。

  • Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。

  • 提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约

  • 消费者:服务调用方,会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。

  • 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态

Eureka和zookeeper的区别:

  • zookeeper是一个软件,Eureka是一个项目,这个项目需要自己开发
  • zookeeper是长连接,Eureka是短连接,默认会每隔30秒重新连接Eureka
  • zookeeper的集群的事务是强一致性的,Eureka的事务是柔性事务

  • zookeeper负载均衡策略默认是随机的,而Eureka的负载均衡策略默认是轮询

2.入门案例

项目结构:

父工程依赖:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.james.demo</groupId>
    <artifactId>cloud_demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>user_service</module>
        <module>user_consumer</module>
        <module>eureka-server</module>
        <module>zuul-demo</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <!--spring-cloud规定时间还是用格林威治版本-->
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <mapper.starter.version>2.1.4</mapper.starter.version>
        <mysql.version>5.1.47</mysql.version>
        <pageHelper.starter.version>1.2.5</pageHelper.starter.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 通用Mapper启动器 -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
</project>

编写EurekaServer

---->添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_demo</artifactId>
        <groupId>cn.james.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>eureka-server</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>

---->启动类

package cn.james.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer  //声明这个应用是一个RurekaServer
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class,args);
    }
}

---->配置类(application.yml)

server:
  port: 10086
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId),注意不要使用下划线,Eureka不识别
eureka:
  client:
    service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
      defaultZone: http://127.0.0.1:10086/eureka
    register-with-eureka: true  # fasle,不注册自己 true,表示注册自己
    fetch-registry: true  #false,不拉取服务 true表示拉取自己

---->启动服务,访问:http://localhost.10086

           

服务注册(user-service)

---->添加依赖

        在user-service中添加对Eureka客户端依赖

<!-- Eureka客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

---->编写启动类

      通过添加@EnableDiscoveryClient来开启Eureka客户端功能

@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserServiceDemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(UserServiceDemoApplication.class, args);
	}
}

---->编写配置类(application.yml)

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/heima
    username: root
    password: 123
  application:
    name: user-service # 应用名称
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka

---->controller类.UserMapper和实体类

controller:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        return userService.findById(id);
    }
}

UserMapper:

public interface UserMapper extends BaseMapper<User> {
}

User实体类:略

       重启服务注册启动类,然后访问Eureka监控页面,查看:http://localhost:10086发现如下界面:

服务发现(user-consumer)

      作用: 尝试从Eureka-Server中获取服务 

---->添加依赖

<!-- Eureka客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

---->客户端启动类

@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端  可以省略
public class ConsumerApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

---->配置类(application.yml)

server:
  port: 8080
spring:
  application:
    name: consumer # 应用名称
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka

---->controller

@RestController
@RequestMapping("/consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        // 根据服务id(spring.application.name),获取服务实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 取出一个服务实例
        ServiceInstance instance = instances.get(0);
        // 查询
        User user = restTemplate.getForObject(instance.getUri()+"/user/" + id, User.class);
        return user;
    }
}

       开启启动类,访问一下地址:http://localhost/consumer/id值,通过debug调试运行,生成一下的url值.

                         

最后获取访问结果.

2.Eureka详解

2.1 基础架构

  • 服务注册中心:Eureka的服务端应用,提供服务注册和发现功能,即刚刚建立的Eureka-server
  • 服务提供者: 提供服务的应用,可以是SpringBoot应用,也可以是其他任意技术实现,只要对外提供的是Rest风格服务即可,即我们刚刚建立的user-service
  • 服务消费者:消费应用从注册中心货物服务列表,从而得知每个服务方的信息,只从去哪里调用服务方,即我们刚建立的user-consumer.

2.2 高可用的EurekaServer

什么是高可用?(优质解释)

        高可用是分布式系统架构设计中必须考虑的因素之一,通常指设计减少系统不能提供服务的时间,假设系统一直能够提供服务,那么系统的可用性是100%,如果系统每运行100个单位时间,会有一个时间单位无法提供服务,我们说系统的可用性是99%.很多公司的高可用目标是4个9,也就是99.99%.

        EurekaServer在刚才的案例中只有一个,我们可以建立一个集群,形成高可用的Eureka中心.多个EurekaServer之间互相注册为服务,当服务提供者注册到EurekaServer集群中的某一个节点时,该节点会将服务的信息同步给集群中的每个节点,从而实现高可用的集群.因此,无论客户端访问到EurekaServer集群中任意一个节点,都可以获取到完整的服务列表信息.,作为消费者和生产者需要把信息注册到每个Eureka中.如果有多个Eureka,则每一个EurekaServer需要注册到其他几个Eureka服务中

例如:有两个分别为10086、10087,则:

  • 10086要注册到10087上

  • 10087要注册到10086上

        此时我们需要修改原来的EurekaServer的application.yml的配置:

server:
  port: 10086 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    service-url: # 配置其他Eureka服务的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10087/eureka

        所谓高可用注册中心,就是把EurekaServer自己也作为一个服务,注册到其他的EurekaServer上,多个EurekaServer之间互相发现对方,从而形成集群.此时我们需要配置一个和第一个一样(端口除外)的EurekaServer启动器.

步骤:

复制一个启动项

 

通过JVM参数覆盖配置文件配置:

 

-Dserver.port=10087 -Deureka.client.serviceUrl.defaultZone=http://127.0.0.1:10086/eureka启动即可。

3)启动测试:

4)生产者和消费者也需要注册服务到集群,因为EurekaServer不止一个,所以在注册服务的时候,需要修改配置文件的参数

eureka:
  client:
    service-url: # EurekaServer地址,多个地址以','隔开
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

2.3 Eureka客户端

服务提供者要向EurekaServer注册服务,并且完成服务续约等工作。

服务注册

       服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-erueka=true参数是否正确,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,Eureka Server会把这些信息保存到一个双层Map结构中。

  • 第一层Map的Key就是服务id,一般是配置中的spring.application.name属性

  • 第二层Map的key是服务的实例id。一般host+ serviceId + port,例如:locahost:user-service:8081

  • 值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

user-service默认注册时使用的是主机名,如果我们想用ip进行注册,可以在user-service的application.yml添加配置:

eureka:
  instance:
    ip-address: 127.0.0.1 # ip地址
    prefer-ip-address: true # 更倾向于使用ip,而不是host名
    instance-id: ${eureka.instance.ip-address}:${server.port} # 自定义实例的id

服务续约

        在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renewal);

有两个重要参数可以修改服务续约的行为:

eureka:
  instance:    
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90
  • lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒

  • lease-expiration-duration-in-seconds:服务失效时间,默认值90秒

也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。

获取服务列表

        当服务消费者启动时,会检测eureka.client.fetch-registry=true参数的值,如果为true,则会从Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:

eureka:
  client:
    registry-fetch-interval-seconds: 30

2.4 服务下线、失效剔除和自我保护

服务下线

       当服务进行正常关闭操作的时候,会触发一个服务下线的Rest请求给EurekaServer,告诉注册中心,“我要下线了”,服务中心收到请求的时候,将该服务置位下线状态

失效剔除

        当由于内存溢出或者网络故障等原因使得服务不能正常工作的时候,而此时注册中心未收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务中心会创建一个定时任务,默认每个一段时间内将当前清单中超时的服务剔除,这个操作叫做失效剔除。

自我保护 

        当我们关停一个服务的时候,就会在Eureka面板收到一条警告:

        这是触发了Eureka的自我保护机制,当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。

        以下配置可以关停自我保护:

eureka:
  server:
    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)

        关停之后,在此访问会显示如下界面:

1、正常下线的微服务会马上从eureka中消失,而非正常关闭的微服务不会马上从eureka中消失

2、 非正常关闭的微服务什么时候从eureka中消失呢?

        eureka发现一个微服务联系不上了,会每隔30秒持续3次(90秒),如果还是没有联系上,那么就会把这个微服务放到一个剔除微服务的定时任务中,这个定时任务每隔60秒执行一次,所以最多150秒这个微服务就会从eureka中消失。

3、有的微服务已经过了150秒后仍然没有从eureka中消失,是因为eureka默认有自我保护机制。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值