SpringCloud学习1(Spring Cloud Netflix)微服务、注册中心Eureka

微服务

SOA -> 微服务(服务的细粒度)SpringCloud是生态。
模块化化开发
微服务架构的4个核心问题?

  1. 这么多服务如何访问?负载均衡算法(轮询)
  2. 服务之间的通信方式?Http、RPC
  3. 这么多服务,如何治理?服务注册与发现。
  4. 服务挂了怎么办?断路器(熔断降级)

解决方案:

  1. Spring Cloud NetFlix(一站式解决方案)
    • api网关:zuul
    • 通信:Feign 基于HTTP Client(http通信方式,同步阻塞)
    • 注册中心(服务注册于发现):Eureka
    • 熔断Hystrix
  2. Spring Cloud Alibaba (一站式解决方案,更简单成本更低)
  3. Apache Dubbo Zookeeper(半自动的取药整合)
    • API:无。可以用前面俩种的网关
    • 通信:Dubbo(RPC、非阻塞)
    • 注册中心:Zookeeper
    • 熔断:姐住Hystrix

其他概念:服务网格ServerMesh

什么是微服务?
微服务之间如何独立通信?
SpringCloud和Dubbo区别?
SpringBoot和SpringCloud的理解?
什么是服务熔断?服务降级?
微服务的优缺点?开发中的坑?
微服务技术栈?
eureka和zookeeper都可以提供注册发现,二者区别?

微服务与微服务架构

微服务

强调的是服务的大小,关注某一个点,是具体解决某一个问题/提供落地对应服务的一个应用。可以看作一个工程的模块。

微服务架构

微服务架构是一种架构模式/风格。提倡将单一的应用程序根据业务拆分成独立的小服务,彻底解耦。每个服务独立的运行在各自的进程。服务之间相互协调,互相配置。各服务独立构建、部署。避免统一集中管理。可以有独立的语言、数据库。

微服务优缺点:

优点

  1. 单一职责原则。 开发简单、效率高
  2. 每个服务高内聚,低耦合。开发部署都是独立的。
  3. 易于和第三方集成,允许容易灵活的方式集成自动部署。
  4. 微服务允许利用融合新技术
  5. 微服务只是业务逻辑代码,不会和HTML、CSS页面混合
  6. 每个微服务都有自己的存储能力,数据库,也可以统一。

缺点

  1. 开发人员处理分布式系统复杂
  2. 多服务增加运维成本
  3. 系统部署依赖
  4. 服务通信成本上升
  5. 数据一致性问题
  6. 系统集成测试问题
  7. 性能监控问题

微服务技术栈

在这里插入图片描述

区别

在这里插入图片描述

在这里插入图片描述

SpringCloud和SpringBoot关系

  • SpringBoot专注于快速开发单个个体的服务。
  • SpringCloud关注全局的为欸服务协调治理框架。 将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等集成服务。
  • SpringBoot可以离开SpringCloud独立使用,但是SpringCloud必须依赖SpringBoot。

Dubbo和SpringCloud技术选型

1. 分布式+服务治理Dubbo

目前成熟的互联网架构:应用服务化拆分+消息中间件

2. Dubbo和SpringCloud对比

在这里插入图片描述
最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用基于Http的Rest方式。 这两种方式各有千秋。Http方式牺牲了性能,但避免了RPC带来的问题。且Rest相比RPC更为灵活。
SpringCloud官方的兼容性更好,而Dubbo组装的方式自由度较高。
定位不一样:Dubbo是一个RPC框架,SpringCloud的目标是微服务下的一站式解决方案。

互联网架构图

在这里插入图片描述

文档

  1. SpringCloud中文网:https://www.springcloud.cc/
  2. SpringCloud中国社区:http://www.springcloud.cn/
  3. 文档:https://www.springcloud.cc/spring-cloud-greenwich.html
  4. 官网:https://cloud.spring.io/

SpringCloud版本选择

在这里插入图片描述在这里插入图片描述

注册中心Eureka(服务注册与发现)

什么是Eureka

Netflix在设计Eureka遵从AP原则。
Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于Rest的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册于发现对于微服务来说是非常重要的。有了服务注册发现,只需要使用服务的标识,就可以访问到服务,而不需要修改服务调用的配置文件。类似的Dubbo注册中心和Zookeeper。

原理

Eureka基本架构:

  • springcloud封装了Netflix的Eureka模块来实现服务注册于发现。
  • Eureka采用CS架构设计,EurekaServer作为服务注册功能的服务器,是服务注册中心
  • 而系统的其他微服务应用。使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护者可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块(eg:Zuul)可以通过EurekaServer来发现系统中的其他微服务应用,并执行相关逻辑。

俩大组件:EurekaServer和EurekaClient

  • EurekaServer提供服务注册发现,各个节点启动,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
  • EurekaClient是一个Java客户端,用于简化EurekaServer交互,客户端同时具备一个内置使用轮询负载算法的负载均衡器。在应用启动后,会向EurekaServer发送心跳(默认周期30秒)。如果EurekaServer在多个心跳周期内没有收到某个节点的心跳,EurekaServer会将这个服务从注册表中移除(默认周期90秒)。

Eureka三大角色

  • Eureka Server:提供服务注册与发现
  • Service Provider:将自身服务注册到Eureka中,从而使得消费者可以找到发现服务。
  • Service Consumer:服务消费方从Eureka中获取注册服务列表,从而找到消费服务。

Eureka自我保护机制

当某一时刻的某个微服务不可用,eureka不会立即清理,仍然会对这个服务信息进行保存。

  • 默认情况,eurekaServer在一定事件没有接收到该微服务心跳,EurekaServer会注销该服务实例。但是如果网络故障,或无法通信,则需要危险警告。因为微服务本身是健康的,此时不应该注销服务。Eureka通过自我保护机制,来进入自我保护模式,一旦进入该模式,EurekaServer会保护服务注册表中的信息,不删除数据注销。当网络恢复,则退出自我保护模式。
  • 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当收到心跳数,重新恢复到阈值以上时,EurekaServer自动退出保护模式。
  • 综上述,自我保护模式是一种应对网络异常的安全保护措施。使得Eureka更加健壮稳定。
  • 禁用自我保护模式:eureka.server.enable-self-preservation = false

编码与配置

EurekaServer注册中心编码配置

在这里插入图片描述

创建一个Maven父工程cloud_parent,并引入依赖

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

<name>cloud1_parent</name>
<packaging>pom</packaging>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <lombok.version>1.18.26</lombok.version>
    <junit.version>4.13.2</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <spring.boot.version>2.2.0</spring.boot.version>
    <spring.cloud.version>2021.0.1</spring.cloud.version>
</properties>
<dependencies>
    <!-- spring cloud依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring.cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
</dependencies>

<!-- spring cloud的依赖要在这里导入,否则报错 -->
<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>

创建eureka工程并引入依赖 cloud1_eureka

<?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>cloud1_parent</artifactId>
        <groupId>com.hx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud1_eureka</artifactId>
    <name>cloud1_eureka</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
<!--        监控信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

在eureka工程中编写配置

spring:
  profiles:
    active: dev-8881
---
server:
  port: 8881
spring:
  application:
    name: eureka-server1
  config:
    activate:
      on-profile: dev-8881
eureka:
  instance:
    # Eureka服务端实例主机名称
    hostname: 172.17.55.129
  server:
    enable-self-preservation: false   #关闭自我保护(当服务器小于一定数量时,若有服务器宕机,不会被移除)
  client:
    # 是否向Eureka注册自己
    register-with-eureka: false
    # 如果是false表示自己是注册中心
    fetch-registry: false
    serviceUrl:      # 服务注册地址
    	defaultZone: http://${
    
    eureka.instance.hostname}:${
    
    server.port}/eureka/

在启动类加上@EnableEurekaServer注解

@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaApp.class, args);
    }
}

集群配置

注意:这里eureka的hostname不可以相同,否则无法注册上来1

spring:
  profiles:
    active: dev-8881
---
server:
  port: 8881
spring:
  application:
    name: eureka-server1
  config:
    activate:
      on-profile: dev-8881
eureka:
  instance:
    # Eureka服务端实例主机名称
    hostname: 172.17.55.129
  server:
    enable-self-preservation: false   #关闭自我保护(当服务器小于一定数量时,若有服务器宕机,不会被移除)
  client:
    # 是否向Eureka注册自己
    register-with-eureka: false
    # 如果是false表示自己是注册中心
    fetch-registry: false
    serviceUrl:
      defaultZone: http://127.0.0.1:8881/eureka/,http://127.0.0.1:8882/eureka/    # 注册地址

---
server:
  port: 8882
spring:
  application:
    name: eureka-server2
  config:
    activate:
      on-profile: dev-8882
eureka:
  instance:
    hostname: 127.0.0.1    # Eureka服务端实例主机名称
  client:
    register-with-eureka: false   # 是否向Eureka注册自己
    fetch-registry: false   # 如果是false表示自己是注册中心
    serviceUrl:
      defaultZone: http://localhost:8881/eureka/,http://localhost:8882/eureka/    # 注册地址

启动

在这里插入图片描述在这里插入图片描述

EurekaClient编码配置

创建一个api和provider(服务提供者)工程

创建cloud1_api工程并引入依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.hx</groupId>
        <artifactId>cloud1_parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hx</groupId>
    <artifactId>cloud1_api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud1_api</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-android</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

在这里插入图片描述

创建cloud1_provider工程并引入依赖

<?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>cloud1_parent</artifactId>
        <groupId>com.hx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud1_provider</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.hx</groupId>
            <artifactId>cloud1_api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
    </dependencies>
</project>
server:
  port: 9991
spring:
  application:
    name: cloud1-provider
logging:
  level:
    com.hx: debug
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8881/eureka/,http://localhost:8882/eureka/
  instance:
    instance-id: cloud-prod-1
#    配置主机地址
    hostname: localhost

在这里插入图片描述在这里插入图片描述

@RestController
public class HiController {
    
    
    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private OrderService orderService;

    @RequestMapping("/")
    public String hi() {
    
    
        return "<h1>HI Spring Cloud 1</h1>";
    }

    @RequestMapping("getOrder")
    public Order getOrder() {
    
    
        String id = UUID.randomUUID().toString();
        Order order = orderService.getOrder(id);
        return order;
    }

    /**
     * 获取服务清单
     *
     * @return
     */
    @RequestMapping("getClients")
    public List<String> getClients() {
    
    
        // 获取服务清单
        List<String> services = discoveryClient.getServices();
        for (String service : services) {
    
    
            // 获取具体微服务实例信息
            List<ServiceInstance> instances = discoveryClient.getInstances(service);
            for (ServiceInstance instance : instances) {
    
    
                System.out.println("instance.getHost() = " + instance.getHost());
                System.out.println("instance.getPort() = " + instance.getPort());
                System.out.println("instance.getUri() = " + instance.getUri());
                System.out.println("instance.getServiceId() = " + instance.getServiceId());
            }
        }
        return services;
    }
}

Eureka的CAP原则与Zookeeper对比

ACID原则

A(automicity) C(Consistency) I(Isolation) D(Durability)
原子性、一致性、隔离性、持久性

CAP原则:

C:(Consistency)强一致性
A:(Avaliability)可用性
P:(Partition tolerance)分区容错性

CAP核心原理

  • 一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个要求。
  • 根据CAP原理,将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类。最多满足俩个,不可能同时满足。
    • CA:单点集群,满足一致性、可用性的系统。但可扩展性较差
    • CP:满足一致性、分区容错性,但性能不是很高
    • AP:满足可用性、分区容错性,但对一致性要求较低

作为服务注册中心,Eureka比zookeeper好在哪里?

一个分布式系统无法同时满足CAP原则。
由于分区容错性P在分布式系统必须保证,因此只可以在CA之间进行权衡

  • zookeeper保证CP原则
  • Eureka保证AP原则

Eureka可以很好的应对网络故障导致的部分节点失恋问题,而不会像zk一样使得整个服务的无法使用

Zookeeper保证CP原则

当注册中心查询服务列表时,可以容忍注册中心返回的是几分钟前的信息。但不可以接收服务down机不可用。即服务注册功能对可用性的要求要高于一致性。但zk会出现一种情况,当master节点因为网络故障与其他节点失联,剩余节点会重新选举leader作为master节点。问题在于,选举leader节点事件太长(30s-120s)且全局期间zk集群不可用,这会导致在选举时,注册服务瘫痪。在云部署环境下,由于网络问题使得zk集群失去master节点丢失发生概率较大,虽然最空可以恢复服务,但是漫长选举过程是不可接受的。

Eureka保证AP原则

Eureka各个节点都是平等关系,几个节点挂掉不会影响正常节点工作,剩余的节点依然会提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则自动切换到其他节点,只要有一台Eureka可用,则保证了注册服务可用性,不过查询信息可能不是最新的。此外,Eureka的自我保护机制,如果15min内超过85%的节点没有正常心跳,那么eureka认为客户端于注册中心出现网络故障。此时

  1. Eureka不再从注册列表移除因为长时间没有收到心跳而应该过期的服务
  2. Eureka仍然可以接收新服务的注册于查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其他节点

踩坑:


  1. 在Eureka中的集群配置需要注意hostname配置不可以相同。 ↩︎

猜你喜欢

转载自blog.csdn.net/qq_40366738/article/details/129825689