SpringCloud-注册中心

1 分布式系统介绍

企业架构的演进

  • 单体应用架构
  • 垂直应用架构
  • 分布式应用架构 SOA架构
  • 微服务应用架构

单体应用架构

,大部分web工程(包含前端页面,web层代码,service层代码,dao层代码)是将所有的功能模块,打包到一起并放在一个web容器中运行。

image-20210617145311188

优点:

所有的功能集成在一个项目工程中

项目架构简单,前期开发成本低,周期短,小型项目的首选。

缺点:

全部功能集成在一个工程中,对于大型项目不易开发、扩展及维护。

系统性能扩展只能通过扩展集群结点,成本高、有瓶颈。

技术栈受限。

.1.2 垂直应用架构

将应用拆成互不相干的几个应用,以提升效率

image-20210617145405248

优点

项目架构简单,前期开发成本低,周期短,小型项目的首选。

通过垂直拆分,原来的单体项目不至于无限扩大

不同的项目可采用不同的技术

缺点

全部功能集成在一个工程中,对于大型项目不易开发、扩展及维护。

系统性能扩展只能通过扩展集群结点,成本高、有瓶颈。

.1.3 分布式SOA架构

SOA

全称为 Service-Oriented Architecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署组合和使用

一个服务通常以独立的形式存在于操作系统进程中。

在功能的角度,把业务逻辑抽象成可复用、可组装的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。

特点

分布式、可重用、扩展灵活、松耦合

当垂直应用越来越多,应用之间交互不可避免,将**核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,**使前端应用能更快速的响应多变的市场需求

image-20210617145605344

优点

  • 抽取公共的功能为服务,提高开发效率

  • 对不同的服务进行集群化部署解决系统压力

  • 基于ESB/DUBBO减少系统耦合

缺点

抽取服务的粒度较大

服务提供方与调用方接口耦合度较高

1.4 微服务架构

image-20210617145641058

  • 通过服务的原子化拆分,以及微服务的独立打包、部署和升级,小团队的交付周期将缩短,运维成本也将大幅度下降
  • 微服务遵循单一原则。微服务之间采用Restful等轻量协议传输

缺点

微服务过多,服务治理成本高,不利于系统维护。

分布式系统开发的技术成本高(容错、分布式事务等)。

SOA与微服务之间的关系

SOA( Service Oriented Architecture )“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程
中。各个服务之间 通过网络调用

微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。
这些小应用之间通过服务完成交互和集成。

image-20210617145802636

分布式核心知识

1 分布式系统中的远程调用

在微服务架构中,通常存在多个服务之间的远程调用的需求。远程调用通常包含两个部分:序列化和通信协议

常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等,目前主流的

远程调用技术有基于HTTP的RESTful接口以及基于TCP的RPC协议

RestFul 的推荐介绍

怎样用通俗的语言解释REST,以及RESTful? - 慕课网的回答 - 知乎 https://www.zhihu.com/question/28557115/answer/584744188

怎样用通俗的语言解释REST,以及RESTful? - Mingqi的回答 - 知乎 https://www.zhihu.com/question/28557115/answer/146660220

(1) Restful 接口

REST,即Representational State Transfer的缩写,如果一个架构符合REST原则,就称它为RESTful架构。

资源(Resources)

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在你可以用一个URI(统一资源定位符)指向它,
每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符

表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格
式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源
的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示
格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变
化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因
此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:
GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源
(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

image-20210617151843997

(2 )RPC

RPC(Remote Procedure Call ) 一种进程间通信方式。允许像调用本地服务一样调用远程服务

RPC

框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。

image-20210617151929097

CAP理论

现如今,对于多数大型互联网应用,分布式系统(distributed system)正变得越来越重要。分布式系统的最大难点,就是各个节点的状态如何同步。CAP 定理是这方面的基本定理,也是理解分布式系统的起点。

image-20210617154012704

zookeeper 保证的是CP 原则,允许一段时间内服务不可用

Consistency(一致性):数据一致更新,所有数据的变化都是同步的
Availability(可用性):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
Partition tolerance(分区容忍性):某个节点的故障,并不影响整个系统的运行

image-20210617154141705

在一个分布式系统当中,分区容忍性和可用性是最基本的需求,所以在分布是系统中,我们的系统最当关注的就是A(可用性)P(容忍性),通过补偿的机制寻求数据的一致性

分布式系统一般保证AP

2 SpringCloud的介绍

1常见的微服务框架

1 SpringCloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

Spring Cloud并没有重复制造轮子,它只是将目前各家

公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

2 ServiceComb

image-20210617154706050

3 ZeroC ICE

image-20210617154734882

2 SpringCloud 中的相关概念

1 服务注册与发现

服务注册:服务实例将自身服务信息注册到注册中心。这部分服务信息包括服务所在主机IP和提供服务的Port,以及暴露服务自身状态以及访问协议等信息。
服务发现:服务实例请求注册中心获取所依赖服务信息。服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。

image-20210617154901295

2 负载均衡

负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。

image-20210617154921703

3 熔断

image-20210617154936410

4 链路追踪

image-20210617160038284

5 API网关

image-20210617160121780

image-20210617160128857

3 SpringCloud的核心组件

Spring Cloud Netflix组件

image-20210617160210867

Spring Cloud Alibaba组件

image-20210617160224118

Spring Cloud原生及其他组件

image-20210617160236045

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容(json数据)转换为java对象并绑定到Controller方法的参数上。

@ResponseBody注解用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

3 搭建一个微服务环境

详细参考代码

1.1 搭建数据库

创建shop 数据库

用户表

CREATE TABLE `tb_user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(40) DEFAULT NULL COMMENT '用户名',
 `password` varchar(40) DEFAULT NULL COMMENT '密码',
 `age` int(3) DEFAULT NULL COMMENT '年龄',
 `balance` decimal(10,2) DEFAULT NULL COMMENT '余额',
 `address` varchar(80) DEFAULT NULL COMMENT '地址',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

商品表

CREATE TABLE `tb_product` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `product_name` varchar(40) DEFAULT NULL COMMENT '名称',
 `status` int(2) DEFAULT NULL COMMENT '状态',
 `price` decimal(10,2) DEFAULT NULL COMMENT '单价',
 `product_desc` varchar(255) DEFAULT NULL COMMENT '描述',
 `caption` varchar(255) DEFAULT NULL COMMENT '标题',
 `inventory` int(11) DEFAULT NULL COMMENT '库存',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

订单表

CREATE TABLE `tb_order` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `user_id` int(11) DEFAULT NULL COMMENT '用户id',
 `product_id` int(11) DEFAULT NULL COMMENT '商品id',
 `number` int(11) DEFAULT NULL COMMENT '数量',
 `price` decimal(10,2) DEFAULT NULL COMMENT '单价',
 `amount` decimal(10,2) DEFAULT NULL COMMENT '总额',
 `product_name` varchar(40) DEFAULT NULL COMMENT '商品名',
 `username` varchar(40) DEFAULT NULL COMMENT '用户名',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

1.2 搭建环境

(1)创建父工程shop_parent

在IDEA中创建父工程 shop_parent 并引入坐标

 	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.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>
    </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-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


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

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


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

(2)创建微服务工程模块

product_service

order_service

完整项目在

1 导入依赖


    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>

2 配置application.yaml

product_service

server:
  port: 9011 #端口
spring:
  application:
    name: service-product #服务名称
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true

order_service

server:
  port: 9002 #端口
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true # 开启Spring Cloud的重试功能
  application:
    name: service-order #服务名称
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true

测试 http://localhost:9011/product/1

image-20210624212936986

1.3 RestTemplate 介绍

前面编写了两个微服务,订单服务,和商品服务,此时订单要调用商品服务的HTTP接口,这时候可以使用http请求的相关工具类完成,如常见的HttpClient,OkHttp,当然也可以使用Spring提供的RestTemplate

Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的

RestTemplate是一种更优雅的调用RESTful服务的方式。

在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具

有默认行为的简化方法

虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,

RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。

2 RestTemplate方法介绍

image-20210618145049428

3 订单服务中使用RestTemplate 调用服务

1 在order-service 启动类中配置RestTemplate

public class OrderApplication {


	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}

2 编写订单方法

@GettMapping("/{id}")
 public String order(Integer num) {
     //通过restTemplate调用商品微服务
     Product object =
    restTemplate.getForObject("http://127.0.0.1:9011/product/1", Product.class);
     System.out.println(object);
     return "操作成功";
 }

3 测试访问

http://localhost:9002/order/buy/1

image-20210624213830420

我们把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题

  • 应用场景有局限
  • 无法动态调整

应该怎么解决呢,就需要通过注册中心动态的对服务注册和服务发现

4 Eureka

4.1 微服务注册中心

注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。

注册中心的作用

  • 服务发现
  • 服务配置
  • 服务健康检查

image-20210624214220153

4.2 常见的注册中心

Zookeeper

image-20210624214318528

Eureka
Eureka是在Java语言上,基于Restful Api开发的服务注册与发现组件,Springcloud Netflix中的重要组件

Nacos

Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是注册中心 + 配置中心的组合,提供简单易用的特性集,帮助我们解决微服务开发必会涉及到的服务注册

与发现,服务配置,服务管理等问题。Nacos 还是 Spring Cloud Alibaba 组件之一,负责服务注册与发现

Consul

Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件,采用Raft算法保证服务的一致性,且支持健康检查。

对比

image-20210617185843583

4.3 Eureka的原理

image-20210617190609230

image-20210617190623974

Eureka 有三个角色组成

  • Eureka Server

提供服务注册和发现

  • Service Provider

将自身服务注册到Eureka,从而使服务消费方能够找到

  • Service Consumer

Eureka 包含两个组件 Eureka Server 和 Eureka Client

Eureka Client

是一个Java客户端,用于简化与Eureka Server的交互

每个服务提供者和服务消费者都会有一个Eureka Client ,Eureka Client 负责和 Eureka Server 交互

Eureka Server

  • 提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;

  • 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒);

  • Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。

image-20210617190542438

Eureka 保证的AP 原则, 不保证数据一致性,当Eureka Client 失去心跳时,注册中心会删除提供的服务信息,但是服务消费者之前依旧保留着服务消费者的信息

4.4 Eureka 的使用步骤

4.4.1 创建eureka_server子模块

2 导入依赖


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

3 配置application.yaml

spring:
  application:
    name: eureka-server
server:
  port: 9000 #端口
#配置eureka server
eureka:
  client:
    register-with-eureka: false #是否将自己注册到注册中心
    fetch-registry: false #是否从eureka中获取注册信息
    service-url: #配置暴露给Eureka Client的请求地址
      defaultZone: http://127.0.0.1:9000/eureka/

4 配置启动类

在 cn.itcast.eureka 下创建启动类 EurekaServerApplication

@SpringBootApplication
//EnableEurekaServer : 激活Eureka Server端配置
@EnableEurekaServer
public class EurekaServerApplication {
 public static void main(String[] args) {
 SpringApplication.run(EurekaServerApplication.class, args);
 }
}

5 访问http://localhost:9000/

image-20210617204554951

Eureka客户端

4.4. 2 服务注册到Eureka注册中心

(1) 商品模块中引入坐标

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

(2) 配置application.yml文件

eureka:
 client:
   serviceUrl: # eureka server的路径
     defaultZone: http://localhost:9000/eureka/
 instance:
   prefer-ip-address: true #使用ip注册ddress: true #使用ip注册

(3) 修改启动类添加服务注册注解

@SpringBootApplication
//@EnableDiscoveryClient
//@EnableEurekaClient
public class UserApplication {
 public static void main(String[] args) {
 SpringApplication.run(UserApplication.class, args);
 }
}

从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient ****

省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。

访问http://localhost:9000/ 成功注册

image-20210625093051194

4.5 Eureka中的自我保护

微服务第一次注册成功之后,每30秒会发送一次心跳将服务的实例信息注册到注册中心。通知 Eureka Server 该实例仍然存在。如果超过90秒没有发送更新,则服务器将从注册信息中将此服务移除

Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)

验证完自我保护机制开启后,并不会马上呈现到web上,而是默认需等待 5 分钟(可以通过eureka.server.wait-time-in-ms-when-sync-empty 配置),即 5 分钟后你会看到下面的提示信息:

image-20210618145446219

如果关闭自我保护

通过设置 eureka.enableSelfPreservation=false 来关闭自我保护功能

4.6 Eureka中的元数据

  • 标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。

  • 自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问

在程序中可以使用DiscoveryClient 获取指定微服务的所有标准元数据信息

注意是org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 包下的

1 注入DiscoverClient 并获取Eureka中的元数据

 @Autowired
    private DiscoveryClient discoveryClient;


    @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) {
        Product product = null;
        List<ServiceInstance> instances = discoveryClient.getInstances();
        for (ServiceInstance instance : instances) {
            System.out.println(instance);
        }
        ServiceInstance serviceInstance = instances.get(0);

        product = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/1",Product.class);
        return product;
    }

5 Eureka 高级

5.1 EurekaServer 高可用集群

如果Eureka Server宕机时,某些微服务也出现了不可用的情况,Eureka Server中的缓存若不被刷新,就可能会影响到微服务的调用,甚至影响到整个应用系统的高可用。因此,在生成环境中,通常会部署一个高可用的Eureka Server集群

Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server实例会彼此增量地同步信息,从而确保所有节点数据一致。事实上,节点之间相互注册是Eureka Server的默认行为。

image-20210618153022081

1 Server间的相互注册

1 准备另外一个Eureka-Server 并相互注册

  • 复制一个Eureka Server 重命名 然后粘贴
  • 在工程中导入该moudle
  • 修改pom 文件中的artifactId 名
  • 在父依赖中添加该模块
  • 重新加载项目结构

出现的问题

Spring Boot中找不到org.springframework.boot.SpringApplication程序包

File>>setings>>Maven>>Runner

image-20210618185451774

巨坑

这里有两个EurekaServer

  • eureka_server8000
  • eureka_server 端口为9000

eureka_server8000 配置application.yaml

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9000/eureka/
      

eureka_server 配置application.yaml

spring:
  application:
    name: eureka-server
server:
  port: 9000
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9001/eureka/

2 将服务注册到Eureka Server中

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册

5.2 Eureka中的常见问题

5.2.1 服务注册到Eureka Server的过程较慢

大致含义:服务的注册涉及到心跳,默认心跳间隔为30s。在实例、服务器、客户端都在本地缓存中具有相同的元数据之前,服务不可用于客户端发现(所以可能需要3次心跳)。可以通过配置

**eureka.instance.leaseRenewalIntervalInSeconds (**心跳频率)加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为在服务器内部有一些计算,他们对续约做出假设。

5.2.2 服务节点剔除问题

默认情况下,由于Eureka Server剔除失效服务间隔时间为90s且存在自我保护的机制。所以不能有效而速的剔除失效节点,这对开发或测试会造成困扰。解决方案如下:

Eureka Server

配置关闭自我保护,设置剔除无效节点的时间间隔

eureka:
 instance:
   hostname: eureka1
 client:
   service-url:
     defaultZone: http://eureka2:8762/eureka
 server:
   enable-self-preservation: false  #关闭自我保护
   eviction-interval-timer-in-ms: 4000 #剔除时间间隔,单位:毫秒

Eureka Client

配置开启健康检查,并设置续约时间

eureka:
 client:
   healthcheck: true #开启健康检查(依赖spring-boot-actuator)
   serviceUrl:
     defaultZone: http://eureka1:8761/eureka/,http://eureka1:8761/eureka/
 instance:
   preferIpAddress: true
   lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给server端后,续约到期时间(默认90秒)
   lease-renewal-interval-in-seconds: 5 #发送心跳续约间隔
5.2.3 监控页面显示ip

在Eureka Server的管控台中,显示的服务实例名称默认情况下是微服务定义的名称和端口。为了更好

的对所有服务进行定位,微服务注册到Eureka Server的时候可以手动配置示例ID

eureka:
 instance:
   instance-id: ${spring.cloud.client.ip-address}:${server.port}

image-20210618184034767

6 Eureka源码分析 暂时不讨论

6.1 SpringBoot 中的自动装载

(1)ImportSelector

mportSelector接口是Spring导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)中起到了决定性的作用。当在@Configuration标注的Class上使用@Import引入了一个
ImportSelector实现类后,会把实现类中返回的Class名称都定义为bean

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

image-20210619160338642

6.1.1 利用ImportSelector 实现自定义的Enable功能

1)定义Bean对象


@Data
public class User {

    private String name;
    private Integer age;

}

2)定义配置类Configuration 这里并未使用spring中的注解

public class UserConfiguration {


    @Bean
    public User getUser(){
        User user = new User();
        user.setAge(11);
        user.setName("张三");
        return  user;
    }
}

3 ) 定义ImportSelector

public class UserImportSelector implements ImportSelector {
 @Override
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取配置类名称
 return new String[]{UserConfiguration.class.getName()};
 }
}

4) 定义EnableXXX注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(UserImportSelector.class)
public @interface EnableUserBean {
}

@Import 注解,会自动加载对应的类对象,然后执行里面的方法

5 ) 测试

/**
* 通过在类上声明@EnableUserBean,会自动的加载所有对象
*/
@EnableUserBean
public class TestApplication {
 public static void main(String[] args) {
 AnnotationConfigApplicationContext applicationContext = new
AnnotationConfigApplicationContext(TestApplication.class);
 User user = applicationContext.getBean(User.class);
 System.out.println(user);
 }
}

接口在哪里调用呢?我们可以来看一下ConfigurationClassParser这个类的processImports方法

image-20210619161214737

在这里我们可以看到ImportSelector接口的返回值会递归进行解析,把解析到的类全名按照@Configuration进行处理

6. 1.2 SpringBoot 自动配置的代码分析

在SpringBoot中最重要的一个注解SpringBootApplication

image-20210625111205732

在SpringBootApplication注解中声明了一@EnableAutoConfiguration

image-20210625111224337

在EnableAutoConfifiguration中通过Import引入了SpringBoot定义的

AutoConfigurationImportSelector,,我们只需看下最主要的逻辑代码即可

image-20210625111321513

从上面的逻辑可以看出,最终获取bean的渠道在SpringFactoriesLoader.loadFactoryNames

image-20210625111449301

image-20210625111516030

每个jar都可以定义自己的META-INF/spring.factories ,jar被加载的同时 spring.factories里面定义的bean就可以自动被加载

image-20210619163520860

7 Consul

在Euraka的GitHub上,宣布Eureka 2.x闭源。近这意味着如果开发者继续使用作为 2.x 分支上现有工作repo 一部分发布的代码库和工件,则将自负风险

7.1 consul 概述

Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服
务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较 为简单。

  • Consul 使用Go 语言编写因此具有天然可移植性(支持Linux、windows和Mac OS X);
  • 安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

Consul 的优势:

  • 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是Paxos, 而 etcd 使用的则是 Raft。
  • 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。
  • 支持健康检查。 etcd 不提供此功能
  • 支持 http 和 dns 协议接口。 zookeeper 的集成较为复杂, etcd 只支持 http 协议。
  • 官方提供 web 管理界面, etcd 无此功能。

特性:

  • 服务发现
  • 健康检查
  • Key/Value 存储
    多数据中心

7.2 Consul与Eureka 的区别

(1)一致性

Consul强一致性(CP)

  • 服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功
  • Leader挂掉时,重新选举期间整个consul不可用。保证了强一致性但牺牲了可用性。

Eureka保证高可用和最终一致性(AP)

  • 服务注册相对要快,因为不需要等注册信息replicate到其他节点,也不保证注册信息是否replicate成功
  • 当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。

(2)开发语言和使用

eureka就是个servlet程序,跑在servlet容器中

Consul则是go编写而成,安装启动即可

7.3 consul的下载和安装

Consul 不同于 Eureka 需要单独安装,访问Consul 官网下载 Consul 的最新版本,我这里是consul1.5x。根据不同的系统类型选择不同的安装包 https://www.consul.io/downloads

1 在linux虚拟中下载consul服务

## 从官网下载最新版本的Consul服务
wget https://releases.hashicorp.com/consul/1.5.3/consul_1.5.3_linux_amd64.zip
##使用unzip命令解压
unzip consul_1.5.3_linux_amd64.zip
##将解压好的consul可执行命令拷贝到/usr/local/bin目录下
cp consul /usr/local/bin
##测试一下
consul

启动consul服务

##已开发者模式快速启动,-client指定客户端可以访问的ip地址
[root@node01 ~]# consul agent -dev -client=0.0.0.0
==> Starting Consul agent...
           Version: 'v1.5.3'
           Node ID: '49ed9aa0-380b-3772-a0b6-b0c6ad561dc5'
         Node name: 'node01'
       Datacenter: 'dc1' (Segment: '<all>')
           Server: true (Bootstrap: false)
       Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
     Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, 
Auto-Encrypt-TLS: false

启动成功之后访问: http://IP:8500 ,可以看到 Consul 的管理界面

2 在Windows下载consul服务

1 https://www.consul.io/downloads 下载64位consul

2 cmd 运行

consul agent  -dev -client=0.0.0.0

-dev 表示开发者模式启动

-client= 运行访问服务的地址 0.0.0.0 表示允许所有ip访问

consul 端口默认为8500

3 访问http://localhost:8500/

image-20210625114519954

7.4 Consul 的基本使用

Consul 支持健康检查,并提供了 HTTP 和 DNS 调用的API接口完成服务注册,服务发现,以及K/V存储这些功能。接下来通过发送HTTP请求的形式来了解一下Consul

7.4.1 POSTMAN的使用

参考博客

7.4.2 服务注册与发现

(1)注册服务

通过postman发送put请求到http://127.0.0.1:8500/v1/catalog/register地址可以完成服务注册

{
 "Datacenter": "dc1", 
 "Node": "node01", 
 "Address": "192.168.74.102",
 "Service": {
 "ID":"mysql-01",
 "Service": "mysql", 
 "tags": ["master","v1"], 
 "Address": "192.168.74.102",
 "Port": 3306
 }
}

image-20210625115219105

(2)服务查询

通过postman送get请求到http://127.0.0.1:8500/v1/catalog/services查看所有的服务列表

image-20210625115342709

通过postman发送get请求到http://192.168.74.101:8500/v1/catalog/service/服务名查看具体的服务详情

(3)服务删除

通过postman发送put请求到http://192.168.74.101:8500/v1/catalog/deregister删除服务

7.4.3 Consul的KV存储

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQ2cOJ9A-1624612499121)(C:\Users\liujian\AppData\Roaming\Typora\typora-user-images\image-20210625115438125.png)]

https://www.consul.io/api/kv

7.5 基于consul的服务注册

**(1)创建一个工程 **spring_cloud_consul

配置Pom

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </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-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


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

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <packaging>pom</packaging>

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

(2 ) 创建商品模块 和订单模块 和前面一样

在商品模块 和订单模块 导入依赖

   <!--SpringCloud提供的基于Consul的服务发现-->
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
 <!--actuator用于心跳检查-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency

配置服务注册

修改每个微服务的application.yml配置文件,添加consul服务发现的相关配置信息

server:
  port: 9002 #端口
spring:
  application:
    name: service-order #服务名称
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true
  ###开始配置consul的服务注册
  cloud:
    consul:
      host: 127.0.0.1 #consul服务器的主机地址
      port: 8500 #consul服务器的ip地址
      discovery:
        #是否需要注册
        register: true
        #注册的实例ID (唯一标志)
        instance-id: ${spring.application.name}-1
        #服务的名称
        service-name: ${spring.application.name}
        #服务的请求端口
        port: ${server.port}
        #指定开启ip地址注册
        prefer-ip-address: true
        #当前服务的请求ip
        ip-address: ${spring.cloud.client.ip-address}


  • host:表示Consul的Server的请求地址

  • port:表示Consul的Server的端口

  • discovery:服务注册与发现的相关配置

  • instance-id : 实例的唯一id(推荐必填),spring cloud官网文档的推荐,为了保证生成一个唯一的id ,也可以换成 s p r i n g . a p p l i c a t i o n . n a m e : {spring.application.name}: spring.application.name:{spring.cloud.client.ipAddress}

prefer-ip-address:开启ip地址注册

ip-address:当前微服务的请求ip

启动Consul 和两个微服务

image-20210625122155653

7.6 Consul 高可用集群

image-20210625164206477

此图是官网提供的一个事例系统图,图中的Server是consul服务端高可用集群,Client是consul客户端。

  • Agent——agent是一直运行在Consul集群中每个成员上的守护进程。通过运行 consul agent来启动。

agent 有三种模式

  • dev 模式 开发者模式

  • client 模式

  • server模式

  • Client 是consul客户端。consul客户端不保存数据,客户端将接收到的请求转发RPC到Server端

    Client的数量没有现在,

  • Server是一个有一组扩展功能的代理,这些功能包括参与Raft选举,维护集群状态,响应RPC查询,与其他数据中心交互WANgossip和转发查询给leader或者远程数据中心。

​ Server的数量一般为3-5台

  • LAN Gossip——它包含所有位于同一个局域网或者数据中心的所有节点

为了保证各个节点数据的一致性,Consul有两种协议

Gossip协议

传统的监控,如ceilometer,由于每个节点都会向server报告状态,随着节点数量的增加server的压力随之增大。在所有的Agent之间(包括服务器模式和普通模式)运行着Gossip协议。服务器节点和普通

Agent都会加入这个Gossip集群,收发Gossip消息。每隔一段时间,每个节点都会随机选择几个节点发送Gossip消息,其他节点会再次随机选择其他几个节点接力发送消息。这样一段时间过后,整个集群都

能收到这条消息

所有的consul都会参与到gossip协议中(多节点中数据赋值)

Raft 算法

为了实现集群中多个ConsulServer中的数据保持一致性,consul使用了基于强一致性的RAFT算法。

在Raft中,任何时候一个服务器可以扮演下面角色之一:

  1. Leader: 处理所有客户端交互,日志复制等,一般一次只有一个Leader.
  2. Follower: 类似选民,完全被动
  3. Candidate(候选人): 可以被选为一个新的领导人。

Consule 基于Raft协议保证Server 数据为强一致性

关于RAFT一致性算法有一个经典的动画http://thesecretlivesofdata.com/raft/,其中详细介绍了选举,数据同步的步骤

Leader全权负责所有客户端的请求,以及将数据同步到Follower中(同一时刻系统中只存在一个Leader)。Follower被动响应请求RPC,从不主动发起请求RPC。Candidate由Follower向Leader转换

的中间状态

image-20210625165206760

集群的搭建

1 环境准备

image-20210625165641249

2 安装并启动Consul

打开三台LInux 虚拟机 作为Server 端

image-20210625165759092

在每个consul节点上安装consul服务,下载安装过程和单节点一致。

##从官网下载最新版本的Consul服务
wget https://releases.hashicorp.com/consul/1.5.3/consul_1.5.3_linux_amd64.zip
##使用unzip命令解压
unzip consul_1.5.3_linux_amd64.zip
##将解压好的consul可执行命令拷贝到/usr/local/bin目录下
cp consul /usr/local/bin
##测试一下
consul

启动每个consul server节点

##登录s1虚拟机,以server形式运行
consul agent -server -bootstrap-expect 3 -data-dir /etc/consul.d -node=server-1 
-bind=192.168.74.101 -ui -client 0.0.0.0 &
##登录s2 虚拟机,以server形式运行
consul agent -server -bootstrap-expect 2 -data-dir /etc/consul.d -node=server-2 
-bind=192.168.74.102 -ui -client 0.0.0.0 & 
##登录s3 虚拟机,以server形式运行
consul agent -server -bootstrap-expect 2 -data-dir /etc/consul.d -node=server-3 
-bind=192.168.74.103 -ui -client 0.0.0.0 &

-server: 以server身份启动。

-bootstrap-expect:集群要求的最少server数量,当低于这个数量,集群即失效。

-data-dir:data存放的目录,更多信息请参阅consul数据同步机制

-node:节点id,在同一集群不能重复。

bind:监听的ip地址。

-client:客户端的ip地址(0.0.0.0表示不限制)

& :在后台运行,此为linux脚本语法

至此三个Consul Server模式服务全部启动成功

##在本地电脑中使用client形式启动consul
consul agent -client=0.0.0.0 -bind=ip地址 -data-dir /etc/consul.d -node=client-1

(3) 每个节点加入集群

在s2,s3,s4 服务其上通过consul join 命令加入 s1中的consul集群中

##加入consul集群
consul join 192.168.74.101

注意包括Client ,都加入道s1 192.168.74.101 的集群中

(4) 测试

在任意一台服务器中输入 consul members查看集群中的所有节点信息

##查看consul集群节点信息
consul members

image-20210625170631875

image-20210625170616042

每个节点都有服务

image-20210625170852367

7.7 Consul 存在的问题

(1)节点和服务注销

当服务或者节点失效,Consul不会对注册的信息进行剔除处理,仅仅标记已状态进行标记(并且不可使

用)。如果担心失效节点和失效服务过多影响监控。可以通过调用HTTP API的形式进行处理

节点和服务的注销可以使用HTTP API:

注销任意节点和服务:/catalog/deregister

注销当前节点的服务:/agent/service/deregister/:service_id

如果某个节点不继续使用了,也可以在本机使用consul leave命令,或者在其它节点使用consul force leave 节点Id。

(2)健康检查与故障转移

在集群环境下,健康检查是由服务注册到的Agent来处理的,那么如果这个Agent挂掉了,那么此节点的健康检查就处于无人管理的状态。

从实际应用看,节点上的服务可能既要被发现,又要发现别的服务,如果节点挂掉了,仅提供被发现的
功能实际上服务还是不可用的。当然发现别的服务也可以不使用本机节点,可以通过访问一个Nginx实
现的若干Consul节点的负载均衡来实现

image-20210625171222991

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值