springcloud初级,对于springcloud的基础组件以及概念

Springcloud概念

说在前面

​ springcloud初级知识适合刚刚接触springcloud的人阅读和学习,如果您已经了解了不少springcloud知识,请移步springcloud中级\springcloud中级

为什么要学习springcloud?

我们从很多地方听说或者了解到了springcloud,分布式,微服务,等等等等当下互联网最火热的名词,但是为什么要学习springcloud呢?

什么是微服务架构

微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就成为微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信。

这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

说人话就是,微服务架构把一个很大的东西细分为很小很小的东西,就和一辆车需要很多零件和螺丝一样!但是汽车的目的是为了方便四级在现实世界出行,而微服务是为了方便程序员在架构中实现需求!

微服务微服务微服务,分布式分布式分布式,重要的事情说三遍,你学习springcloud就是为了分布式,springcloud是集成者而不是开创者。

什么是springcloud

  • Spring Cloud是一系列Java框架的有序集合。

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

  • Spring Cloud是服务框架的组合

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

综上所述,springcloud即为服务框架的整合,通过springboot风格进行再封装屏蔽掉了复杂的配置和实现原理

SpringCloud的优缺点

优点

  • 1.耦合度比较低,不影响其他模块的开发
  • 2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。
  • 3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。
  • 4.微服务跨平台的,可以用任何一种语言开发。
  • 5.每个微服务可以有自己的独立的数据库也有用公共的数据库。
  • 6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。

缺点

  • 1.部署比较麻烦,给运维工程师带来一定的麻烦。
  • 2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
  • 3.系统集成测试比较麻烦
  • 4.性能的监控比较麻烦。【最好开发一个大屏监控系统】

总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。

SpringBoot和SpringCloud的区别

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

Spring Cloud和SpringBoot版本对应关系

Spring Cloud Version SpringBoot Version
2020.0.x aka Ilford 2.4.x
Hoxton 2.2.x, 2.3.x (Starting with SR5)
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

SpringCloud由什么组成

image-20210404093515477

  • Spring Cloud Eureka(现在闭源了):服务注册与发现

  • Spring Cloud Zuul(gateway):服务网关

  • Spring Cloud Ribbon:客户端负载均衡

  • Spring Cloud Feign:声明性的Web服务客户端

  • Spring Cloud Hystrix:断路器

  • Spring Cloud Config:分布式统一配置管理

  • 等20几个框架,开源一直在更新

使用 Spring Boot 开发分布式微服务时,我们面临什么问题

  • 1.与分布式系统相关的复杂性-这种开销包括网络问题延迟开销带宽问题安全问题
  • 2.服务发现-服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。
  • 3.冗余-分布式系统中的冗余问题。
  • 4.负载平衡 --负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。
  • 5.性能-问题 由于各种运营开销导致的性能问题。

服务注册和发现是什么意思?Spring Cloud 如何实现?

当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。

有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka 服务注册和发现可以在这种情况下提供帮助。由于所有服务都在 Eureka 服务器上注册并通过调用 Eureka 服务器完成查找,因此无需处理服务地点的任何更改和处理。

Eureka

Eureka的基础概念

什么是Eureka?

Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过Eureka Service来监控各个微服务是否运行正常。

Eureka服务注册与发现

什么是服务治理?

SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理

在传统的RPC远程调用框架中,管理每个服务与服务之间的依赖关系比较复杂,管理比较麻烦,所以需要使用服务治理,管理服务于服务之间的依赖关系,可以实现服务调用负载均衡容错等,实现服务发现与注册。

什么是服务注册与发现

Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,他是服务注册中心。而系统中的其他微服务,使用Eureka 的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息:比如 服务地址通讯地址等以别名的方式注册到注册中心上。另一方面(消费者|服务提供者),会以该别名的方式去注册中心上获取到实际的服务服务通讯地址,然后再实现本低RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信息(如:接口地址))

image-20210404094749028

Eureka的使用规范

Eureka两个组件

  • 1.Eureka Server 提供服务注册 服务

各个微服务节点通过配置启动之后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

  • 2.Eureka Client通过注册中心进行访问

本质是一个java客户端,用于简化Eureka Server 的交互,客户端同时也具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30s)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从注册服务表中把这个服务节点移除(默认90s)。

Eureka怎么实现高可用(搭建集群)

Eureka 的集群搭建方法很简单:每一台 Eureka 只需要在配置中指定另外多个 Eureka 的地址就可以实现一个集群的搭建了。

什么是Eureka的自我保护模式

默认情况下,如果Eureka Service在一定时间内没有接收到某个微服务的心跳,Eureka Service会进入自我保护模式,在该模式下Eureka Service会保护服务注册表中的信息,不在删除注册表中的数据,当网络故障恢复后,Eureka Servic 节点会自动退出自我保护模式(好死不如赖活着)

在测试环境中不建议开启这个参数,生产环境中建议开启

测试用例

我是跟着尚硅谷周阳老师学习的,这里附上视频地址

具体架构如下:

image-20210404101103847

配置Eureka只需要实现一个springboot的主启动类就可以了,我写了两个是因为需要搭建集群,你也可以写三个或者更多。

eureka-server-7001服务模块

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com   # eureka服务端的实例名称
  client:
    register-with-eureka: false    # false表示不向注册中心注册自己
    fetch-registry: false          # false表示自己端就是注册中心,职责就是维护实例,并不需要去检索服务
    service-url:
      #  设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7002.com:7002/eureka/
  server:
    enable-self-preservation: true # eureka默认的保护机制,默认开启true  在此保护机制下,eureka不会因为微服务的突然停止而终止服务
    eviction-interval-timer-in-ms: 0  # eureka默认的保护机制踢除时间
@SpringBootApplication
@EnableEurekaServer// 开启Eureka注册服务中心
public class EurekaMain7001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaMain7001.class,args);
    }
}

cloud-provider-payment-8001生产者模块

server:
  port: 8001
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #    defaultZone: http://localhost:7001/eureka    单机版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka   # 集群版
  instance:
    instance-id: payment8001
    prefer-ip-address: true  # 访问路径可以显示IP地址
    # eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认30s)
    lease-renewal-interval-in-seconds: 1
    # eureka服务端在收到最后一次心跳后的等待时间上限,单位为秒(默认90s),超时将剔除服务
    lease-expiration-duration-in-seconds: 2

#配置mybatis
mybatis:
  #设置别名
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.simple.springcloud.entities
  configuration:
    map-underscore-to-camel-case: true #开启这个的作用是可以让数据库中的p_Addr与pojo中的pAddr对应

@SpringBootApplication
@EnableEurekaClient // 通过注册中心进行访问
@EnableDiscoveryClient 
@MapperScan("com.simple.springcloud.dao")
public class PaymentMain8001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

Zookeeper

Zookeeper的基础概念

什么是Zookeeper?

ZooKeeper是一个经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调服务。
分布式应用程序可以基于ZooKeeper实现数据发布与订阅、负载均衡、命名服务、分布式协调与通知、集群管理、Leader选举、分布式锁、分布式队列等功能。

总结:zookeeper是一个分布式协调工具,可以实现注册中心功能

Zookeeper目标

ZooKeeper致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调服务

  • 高性能

ZooKeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,尤其适用于以读为主的应用场景

  • 高可用

ZooKeeper一般以集群的方式对外提供服务,一般3 ~ 5台机器就可以组成一个可用的Zookeeper集群了,每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都相互保持着通信。只要集群中超过一般的机器都能够正常工作,那么整个集群就能够正常对外服务

  • 严格顺序访问

对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序

ZooKeeper五大特性

  • 顺序一致性

从同一个客户端发起的请求,最终将会严格按照其发送顺序进入ZooKeeper中

  • 原子性

所有请求的响应结果在整个分布式集群环境中具备原子性,即要么整个集群中所有机器都成功的处理了某个请求,要么就都没有处理,绝对不会出现集群中一部分机器处理了某一个请求,而另一部分机器却没有处理的情况

  • 单一性

无论客户端连接到ZooKeeper集群中哪个服务器,每个客户端所看到的服务端模型都是一致的,不可能出现两种不同的数据状态,因为ZooKeeper集群中每台服务器之间会进行数据同步

  • 可靠性

一旦服务端数据的状态发送了变化,就会立即存储起来,除非此时有另一个请求对其进行了变更,否则数据一定是可靠的

  • 实时性

当某个请求被成功处理后,ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能从服务端上读取到最新的数据状态,即ZooKeeper保证数据的最终一致性

Zookeeper集群角色

在分布式系统中,集群中每台机器都有自己的角色,ZooKeeper没有沿用传统的Master/Slave模式(主备模式),而是引入了Leader、Follower和Observer三种角色

  • Leader

集群通过一个Leader选举过程从所有的机器中选举一台机器作为”Leader”,Leader能为客户端提供读和写服务
Leader服务器是整个集群工作机制的核心,主要工作:

  1. 事务请求的唯一调度者和处理者,保证集群事务处理的顺序性
  2. 集群内部各服务器的调度者
  • Follower

顾名思义,Follower是追随者,主要工作:

  1. 参与Leader选举投票
  2. 处理客户端非事务请求 - 即读服务
  3. 转发事务请求给Leader服务器
  4. 参与事务请求Proposal的投票
  • Observer

Observer是ZooKeeper自3.3.0版本开始引入的一个全新的服务器角色,充当一个观察者角色,工作原理和Follower基本是一致的,和Follower唯一的区别是Observer不参与任何形式的投票

处理客户端非事务请求 - 即读服务
转发事务请求给Leader服务器
不参与Leader选举投票
参与事务请求Proposal的投票
所以Observer可以在不影响写性能的情况下提升集群的读性能

测试用例

cloud-provider-payment-8004

server:
port: 8004

spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 192.168.136.140:2181
@SpringBootApplication
@EnableDiscoveryClient // 服务发现组件
public class PaymentMain8004 {
    
    
public static void main(String[] args) {
    
    
SpringApplication.run(PaymentMain8004.class,args);
    }
}
@RestController
@Slf4j
public class PaymentController {
    
    

@Value("${server.port}")
private String serverPort;

@GetMapping(value = "/payment/zk")
public String paymentzk(){
    
    
return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

此时的服务节点为临时节点

Eureka和ZooKeeper的区别

Eureka和ZooKeeper都可以提供服务注册与发现的功能,那么区别是什么呢

Dubbo作为服务框架的,一般注册中心会选择Zookeeper
Spring Cloud作为服务框架的,一般服务注册中心会选择Eureka

DiscoveryClient的作用

  • 可以从注册中心中根据服务别名获取注册的服务器信息。

服务注册发现的原理

Eureka的集群架构

Eureka,peer-to-peer,部署一个集群,但是集群里每个机器的地位是对等的,各个服务可以向任何一个Eureka实例服务注册和服务发现,集群里任何一个Euerka实例接收到写请求之后,会自动同步给其他所有的Eureka实例

Zookeeper的集群架构

ZooKeeper,服务注册和发现的原理,Leader + Follower两种角色,只有Leader可以负责写也就是服务注册,他可以把数据同步给Follower,读的时候leader/follower都可以读

Eureka和Zookeeper的区别

  1. ZooKeeper中的节点服务挂了就要选举
    在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的,
    选举就是改微服务做了集群,必须有一台主其他的都是从
  2. Eureka各个节点是平等关系,服务器挂了没关系,只要有一台Eureka就可以保证服务可用,数据都是最新的。
    如果查询到的数据并不是最新的,就是因为Eureka的自我保护模式导致的
  3. Eureka本质上是一个工程,而ZooKeeper只是一个进程
  4. Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper 一样使得整个注册系统瘫痪
  5. ZooKeeper保证的是CP,Eureka保证的是AP

服务的一致性保障(CAP)

CAP理论关注粒度是数据,而不是整体系统设计的策略

AP架构(Eureka)

当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性

结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

img

CP架构(Zookeeper/Consul)

当网络分区出现后,为了保证一致性,就必须拒绝请求,否则无法保证一致性

结论:违背了可用性A的要求,只满足一致性和分区容错,即CP

img

CAP理论的核心是:一个分布式系统不可能很好的满足一致性,可用性和分区容错性这三个需求,因此,根据CAP原理将NoSQL分成了满足CA原则,满足CP原则,和满足AP原则三大类

CA:单点集群,满足一致性,可用性的系统,通常在可拓展性上不太强大

CP:满足一致性,分区容忍性的分析,通常性能不是很好

AP:满足可用性,分区容忍性的系统,通常可能对一致性要求低一些

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A3at4vSn-1617551613997)(…/…/AppData/Local/Temp/mindmaster/9252b142206/001/24828D79-251B-488C-A71A-4332FD60E754.png)]

C:一致性,Consistency;
取舍:(强一致性、单调一致性、会话一致性、最终一致性、弱一致性)

A:可用性,Availability;

P:分区容错性,Partition tolerance;

ZooKeeper是有一个leader节点会接收数据, 然后同步写其他节点,一旦leader挂了,要重新选举leader,这个过程里为了保证C,就牺牲了A,不可用一段时间,但是一个leader选举好了,那么就可以继续写数据了,保证一致性

Eureka是peer模式,可能还没同步数据过去,结果自己就死了,此时还是可以继续从别的机器上拉取注册表,但是看到的就不是最新的数据了,但是保证了可用性,强一致,最终一致性

服务注册发现的时效性

Zookeeper,时效性更好,注册或者是挂了,一般秒级就能感知到

Eureka,默认配置非常糟糕,服务发现感知要到几十秒,甚至分钟级别,上线一个新的服务实例,到其他人可以发现他,极端情况下,可能要1分钟的时间,ribbon去获取每个服务上缓存的eureka的注册表进行负载均衡

服务故障,隔60秒才去检查心跳,发现这个服务上一次心跳是在60秒之前,隔60秒去检查心跳,超过90秒没有心跳,才会认为他死了,2分钟都过去
30秒,才会更新缓存,30秒,其他服务才会来拉取最新的注册表

三分钟都过去了,如果你的服务实例挂掉了,此时别人感知到,可能要两三分钟的时间,一两分钟的时间,很漫长

容量

ZooKeeper,不适合大规模的服务实例,因为服务上下线的时候,需要瞬间推送数据通知到所有的其他服务实例,所以一旦服务规模太大,到了几千个服务实例的时候,会导致网络带宽被大量占用

Eureka,也很难支撑大规模的服务实例,因为每个Eureka实例都要接受所有的请求,实例多了压力太大,扛不住,也很难到几千服务实例

之前dubbo技术体系都是用Zookeeper当注册中心,spring cloud技术体系都是用Eureka当注册中心这两种是运用最广泛的,但是现在很多中小型公司以spring cloud居多,所以后面基于Eureka说一下服务注册中心的生产优化

服务过慢的优化方法

Zookeeper,一般来说还好,服务注册和发现,都是很快的

Eureka,必须优化参数

  • 服务器到注册中心心跳时间设置
  • 注册中心定时检测心跳时间设置
  • 心跳失效时间设置
  • readWrite缓存定更新到readOnly时间设置
  • 客户端定时拉取readWrite缓存时间设置

Consul

Consul的基础概念

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

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

什么是Consul

Consul是一个基于CP的轻量级分布式高可用的系统,提供服务发现、健康检查、K-V存储、多数据中心等功能,不需要再依赖其他组件(Zookeeper、Eureka、Etcd等)。

  • 服务发现:提供HTTP和DNS两种发现方式

    Consul可以提供一个服务,比如api或者MySQL之类的,其他客户端可以使用Consul发现一个指定的服务提供者,并通过DNS和HTTP应用程序可以很容易的找到所依赖的服务。

  • 健康检查:支持多种协议,HTTP、TCP、Docker、Shell脚本定制化

    Consul客户端提供相应的健康检查接口,Consul服务端通过调用健康检查接口检测客户端是否正常

  • K-V存储:

    客户端可以使用Consul层级的Key/Value存储,比如动态配置,功能标记,协调,领袖选举等等

  • 多数据中心:

    Consul支持开箱即用的多数据中心

Consul的使用场景

docker 实例的注册与配置共享
coreos 实例的注册与配置共享
vitess 集群
SaaS 应用的配置共享
与 confd 服务集成,动态生成 nginx 和 haproxy 配置文件

Consul的作用

client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群.server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个.

由于Spring Cloud Consul项目的实现,我们可以轻松的将基于Spring Boot的微服务应用注册到Consul上,并通过此实现微服务架构中的服务治理。

Consul常用命令

命令 解释 示例
agent 运行一个consul agent consul agent -dev
join 将agent加入到consul集群 consul join IP
members 列出consul cluster的members consul members
leave 将节点移除所在集群 consul leave

Consul agent命令的常用选项

-data-dir

作用:指定agent储存状态的数据目录
这是所有agent都必须的
对于server尤其重要,因为他们必须持久化集群的状态
-config-dir

作用:指定service的配置文件和检查定义所在的位置
通常会指定为”某一个路径/consul.d”(通常情况下,.d表示一系列配置文件存放的目录)
-config-file

作用:指定一个要装载的配置文件
该选项可以配置多次,进而配置多个配置文件(后边的会合并前边的,相同的值覆盖)
-dev

作用:创建一个开发环境下的server节点
该参数配置下,不会有任何持久化操作,即不会有任何数据写入到磁盘
这种模式不能用于生产环境(因为第二条)
-bootstrap-expect
- 作用:该命令通知consul server我们现在准备加入的server节点个数,该参数是为了延迟日志复制的启动直到我们指定数量的server节点成功的加入后启动。

-node

作用:指定节点在集群中的名称
该名称在集群中必须是唯一的(默认采用机器的host)
推荐:直接采用机器的IP
-bind

作用:指明节点的IP地址
有时候不指定绑定IP,会报Failed to get advertise address: Multiple private IPs found. Please configure one. 的异常
-server

作用:指定节点为server
每个数据中心(DC)的server数推荐至少为1,至多为5
所有的server都采用raft一致性算法来确保事务的一致性和线性化,事务修改了集群的状态,且集群的状态保存在每一台server上保证可用性
server也是与其他DC交互的门面(gateway)
-client

作用:指定节点为client,指定客户端接口的绑定地址,包括:HTTP、DNS、RPC
默认是127.0.0.1,只允许回环接口访问
若不指定为-server,其实就是-client
-join

作用:将节点加入到集群
-datacenter(老版本叫-dc,-dc已经失效)

作用:指定机器加入到哪一个数据中心中

下载与启动

直接去官方网站下载,下载完成之后只有一个consul文件

在consul的相应路径下启动cmd

使用开发模式启动: consul agent -dev

测试用例

cloud-providerconsul-payment-8006

server:
  port: 8006

spring:
  application:
    name: consul-provider-payment

###consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${
    
    spring.application.name}
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentMain8006.class,args);
    }
}
@RestController
@Slf4j
public class PaymentController {
    
    

    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/consul")
    public String paymentConsul(){
    
    
        //后面这个是流水号,会一直变的
        return "springcloud with consul:"+serverPort + "\t"+ UUID.randomUUID().toString();
    }
}

Ribbon负载均衡

Ribbon的基础概念

Ribbon是什么

  • Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法
  • Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单的说,就是在配置文件中列出后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。(有点类似Nginx)

LB负载均衡的分类

集中式LB

即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,F5,也可以是软件,nginx)由该设施负责把访问请求通过某种策略转发至服务的提供方

进程内LB

将LB逻辑集成到消费方,消费方从服务之策中心获知有那些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

Ribbon就属于进程内LB,他只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

LB是什么?

简单的说法就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。

常见的负载均衡有软件nginx,LVS,硬件F5等。

Nginx与Ribbon的区别

Nginx服务器负载均衡,客户端的所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端处理。

Nginx是反向代理同时可以实现负载均衡,nginx拦截客户端请求采用负载均衡策略根据upstream配置进行转发,相当于请求通过nginx服务器进行转发。

Ribbon客户端负载均衡,又称本地负载均衡,在调用微服务接口时,会在注册中心上获取注册信息服务列表之后缓存到JVM本地。

从注册中心读取目标服务器信息,然后客户端采用轮询策略对服务直接访问,全程在客户端操作。

总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

Ribbon的工作步骤

Ribbon在工作时分成两步

  • 1.先选择Eureka Server ,他优先选择在同一个区域内负载较少的server
  • 2.再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址,其中Ribbon提供了多种策略,比如轮询,随机和根据响应时间加权。

使用@LoadBalanced注解开启客户端负载均衡

RestTemplate的使用

RestTemplate的官网

RestTemplate的简介

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。接下来我们就来看看这些操作方法的使用。

GET请求

在 RestTemplate 中,和 GET 请求相关的方法有如下几个:

图片描述

这里的方法一共有两类,getForEntity 和 getForObject,每一类有三个重载方法,下面我们分别予以介绍。

getForEntity

既然 RestTemplate 发送的是 HTTP 请求,那么在响应的数据中必然也有响应头,如果开发者需要获取响应头的话,那么就需要使用 getForEntity 来发送 HTTP 请求,此时返回的对象是一个 ResponseEntity 的实例。这个实例中包含了响应数据以及响应头。例如,在 provider 中提供一个 HelloController 接口,HelloController 接口中定义一个 sayHello 的方法,如下:

@RestController
public class UseHelloController {
    
    
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/hello")
    public String hello(String name) {
    
    
        List<ServiceInstance> list = discoveryClient.getInstances("provider");
        ServiceInstance instance = list.get(0);
        String host = instance.getHost();
        int port = instance.getPort();
        String url = "http://" + host + ":" + port + "/hello?name={1}";
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, name);
        StringBuffer sb = new StringBuffer();
        HttpStatus statusCode = responseEntity.getStatusCode();
        String body = responseEntity.getBody();
        sb.append("statusCode:")
                .append(statusCode)
                .append("</br>")
                .append("body:")
                .append(body)
                .append("</br>");
        HttpHeaders headers = responseEntity.getHeaders();
        Set<String> keySet = headers.keySet();
        for (String s : keySet) {
    
    
            sb.append(s)
                    .append(":")
                    .append(headers.get(s))
                    .append("</br>");
        }
        return sb.toString();
    }
}

getForObject

getForObject 方法和 getForEntity 方法类似,getForObject 方法也有三个重载的方法,参数和 getForEntity 一样,因此这里我就不重复介绍参数了,这里主要说下 getForObject 和 getForEntity 的差异,这两个的差异主要体现在返回值的差异上, getForObject 的返回值就是服务提供者返回的数据,使用 getForObject 无法获取到响应头。例如,还是上面的请求,利用 getForObject 来发送 HTTP 请求,结果如下:

String url = "http://" + host + ":" + port + "/hello?name=" + URLEncoder.encode(name, "UTF-8");
URI uri = URI.create(url);
String s = restTemplate.getForObject(uri, String.class);

POST请求

和 GET 请求相比,RestTemplate 中的 POST 请求多了一个类型的方法,如下:

图片描述

可以看到,post 请求的方法类型除了 postForEntity 和 postForObject 之外,还有一个 postForLocation。这里的方法类型虽然有三种,但是这三种方法重载的参数基本是一样的,因此这里我还是以 postForEntity 方法为例,来剖析三个重载方法的用法,最后再重点说下 postForLocation 方法。

postForEntity

在 POST 请求中,参数的传递可以是 key/value 的形式,也可以是 JSON 数据,分别来看:

  1. 传递 key/value 形式的参数

首先在 provider 的 HelloController 类中再添加一个 POST 请求的接口,如下:

@GetMapping("/hello5")
public String hello5(String name) {
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ServiceInstance instance = list.get(0);
    String host = instance.getHost();
    int port = instance.getPort();
    String url = "http://" + host + ":" + port + "/hello2";
    MultiValueMap map = new LinkedMultiValueMap();
    map.add("name", name);
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, map, String.class);
    return responseEntity.getBody();
}

在这里, postForEntity 方法第一个参数是请求地址,第二个参数 map 对象中存放着请求参数 key/value,第三个参数则是返回的数据类型。当然这里的第一个参数 url 地址也可以换成一个 Uri 对象,效果是一样的。这种方式传递的参数是以 key/value 形式传递的,在 post 请求中,也可以按照 get 请求的方式去传递 key/value 形式的参数,传递方式和 get 请求的传参方式基本一致,例如下面这样:

@GetMapping("/hello6")
public String hello6(String name) {
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ServiceInstance instance = list.get(0);
    String host = instance.getHost();
    int port = instance.getPort();
    String url = "http://" + host + ":" + port + "/hello2?name={1}";
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, null, String.class,name);
    return responseEntity.getBody();
}

postForObject

postForObject 和 postForEntity 基本一致,就是返回类型不同而已,这里不再赘述。

Object

返回对象为响应体中数据转化成的对象,基本上可以理解为JSON

Entity

返回对象为ResponseEntity对象,抱涵了响应中一些重要信息,比如响应头,响应状态码,响应体等

Ribbon负载均衡的算法

img

原理!

如果想要自己替换负载均衡算法!需要注意规则

官方文档明确给出了警告:

这个自定义配置类不能放在@ComponentScan所扫描的当前包下,否则我们自定义的这个配置类就会被所有Ribbon客户端所共享,就达不到特殊化定制的目的了!

在这里插入图片描述

@Configuration
public class MySelfRule {
    
    

    @Bean
    public IRule myRule(){
    
    
        return new RandomRule();//定义为随机
    }
}

最后在主启动类加入一个注解

@RibbonClient(name="",configuration = .class)

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
public class OrderMain80 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderMain80.class,args);
    }
}

手写负载均衡算法原理

@Component
public class MyLB implements LoadBalancer {
    
    

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    //坐标
    private final int getAndIncrement(){
    
    
        int current;
        int next;
        do {
    
    
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));  //第一个参数是期望值,第二个参数是修改值是
        System.out.println("*******第几次访问,次数next: "+next);
        return next;
    }
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
    
      //得到机器的列表
       int index = getAndIncrement() % serviceInstances.size(); //得到服务器的下标位置
        return serviceInstances.get(index);
    }
}

@GetMapping(value = "/consumer/payment/lb")
     public String getPaymentLB(){
    
    
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0){
    
    
            return null;
        }
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }

OpenFeign

OpenFeign的基础概念

Feign是什么

feign是netflix开发的声明式,模板化的HTTP客户端,Feign可以帮助我们更加便捷,优雅的调用HTTP API。

Feign原理

在配置类上,加上@EnableFeginClients,那么该注解是基于@Import注解,注册有关Fegin的解析注册类,这个类是实现 ImportBeanDefinitionRegistrar 这个接口,重写registryBeanDefinition 方法。

他会扫描所有加了@FeginClient 的接口,然后针对这个注解的接口生成动态代理,然后你针对fegin的动态代理去调用他方法的时候,此时会在底层生成http协议格式的请求。

Feign能干什么

Feign旨在使编写Java Http客户端变得更加容易。

前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,行程了一套模板化的调用方法,但是实际开发中对于服务依赖的调用可能不止一处。往往一个接口会被多次调用,所以通常都会针对每个微服务自行封装一些客户端来包装这些依赖服务的调用。

在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置他(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上标注一个Feign注解即可)即可完成对服务提供方的接口绑定,简化了使用springcloud时,自动封装调用客户端的开发量

Feign集成了Ribbon

利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。但是与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法。

在这里插入图片描述

测试用例

cloud-consumer-feign-order-80

这里feign集成了我们的ribbon,eureka也集成了ribbon,前提都是新版本

server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
@SpringBootApplication
@EnableFeignClients
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE")
public class OrderFeignMain80 {
    
    
    public static void main(String[] args) {
    
    
       SpringApplication.run(OrderFeignMain80.class,args);
    }
}
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")//这里就是微服务名称
public interface PaymentFeignService {
    
    
    //客户端需要向浏览器返回一个状态值,所以需要包起来~
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}
@RestController
@Slf4j
public class OrderFeignController {
    
    
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id){
    
    
        return paymentFeignService.getPaymentById(id);
    }
}

在这里插入图片描述

往简单了想,feign就是把ribbon集成了,把restTemplate方式代替成结构的方式,更加体现面向接口编程的思想,搞完收工~

OpenFeign的控制以及日志功能

OpenFeign的超时控制

因为feign里面集成了ribbon,所以这里可以这样写,feign默认的超时时间是1s,如果超过了时间就会报错,所以我们需要手动的在yml里面设置超时时间:

ribbon:
  //这里指的是建立连接所用的时间
  ReadTimeout:  5000
  //这里指的是建立连接之后读取到资源所用的时间
  ConnectTimeout: 5000

OpenFeign日志打印功能

Feign提供了日志打印功能,我们可以调用配置来调整日志级别,从而了解Feign中Http请求的细节。

说白了就是对Feign接口的调用情况进行监控和输出

NONE:默认的,不显示任何日志;

BASIC:仅记录请求方法,URL,响应状态码及执行时间;

HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;

FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及数据源

配置config:

@Configuration
public class FeignConfig {
    
    
    //开启详细日志
    @Bean
    Logger.Level feignLoggerLevel(){
    
    
        return Logger.Level.FULL;
    }
}

YML文件里需要开启日志的Feign客户端

logging:
  level:
    #配置feign日志以什么级别,监控哪个接口
    com.yan.springcloud.service.PaymentFeignService: debug

SpringCloud有几种调用接口方式

  • Feign

  • RestTemplate

Ribbon和Feign调用服务的区别

  • 调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
  • 而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

Spring Cloud Netflix(重点,这些组件用的最多)

Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。

  • Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;

  • Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;

  • Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;

  • Feign:基于Ribbon和Hystrix的声明式服务调用组件;

  • Zuul:API网关组件,对请求提供路由及过滤功能。

我觉得SpringCloud的福音是Netflix,他把人家的组件都搬来进行封装了,使开发者能快速简单安全的使用

:仅记录请求方法,URL,响应状态码及执行时间;**

HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;

FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及数据源

配置config:

@Configuration
public class FeignConfig {
    
    
    //开启详细日志
    @Bean
    Logger.Level feignLoggerLevel(){
    
    
        return Logger.Level.FULL;
    }
}

YML文件里需要开启日志的Feign客户端

logging:
  level:
    #配置feign日志以什么级别,监控哪个接口
    com.yan.springcloud.service.PaymentFeignService: debug

SpringCloud有几种调用接口方式

  • Feign

  • RestTemplate

Ribbon和Feign调用服务的区别

  • 调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
  • 而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

Spring Cloud Netflix(重点,这些组件用的最多)

Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。

  • Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;

  • Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;

  • Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;

  • Feign:基于Ribbon和Hystrix的声明式服务调用组件;

  • Zuul:API网关组件,对请求提供路由及过滤功能。

我觉得SpringCloud的福音是Netflix,他把人家的组件都搬来进行封装了,使开发者能快速简单安全的使用

猜你喜欢

转载自blog.csdn.net/qq_43454016/article/details/115435493