SpringCloud一、为什么需要学习SpringCloud1.1、单体应用1.1.1、什么是单体应用?1.1.2、单体应用的缺点1.1.3、单体应用的优点1.1.4、集群(高并发,高可用)1.1.5、负载均衡(请求均匀分发)1.2、什么是分布式?1.3、什么是SOA?1.4、微服务架构1.4.1、什么是微服务架构?1.4.2、微服务的优点1.4.3、微服务的缺点1.5、如何技术选型二、什么是SpringCloud?(了解)2.1、基本概念2.2、常用组件2.3、SpringCloud版本2.4、服务通信2.5、Dubbo简介2.6、SpringCloud与Dubbo区别学习SpringCloud的原因三、SpringCloud入门3.1、Eureka介绍3.1.1、什么是Eureka3.1.2、Eureka的工作原理(重点掌握)3.2、EurekaServer实战(必须会自己搭建)3.2.1、多模块项目结构3.2.2、搭建EurekaServer3.2.3、Eureka自我保护机制3.3、EurekaClient实战-用户服务3.4、EurekaClient实战-订单服务3.5、使用IP地址进行注册&配置实例ID3.6、Eureka高可用集群3.6.1、搭建EurekaServer集群3.6.2、启动Eureka集群3.6.3、Eureka客户端注册到Eureka服务端集群3.6.4、DS Replicas问题3.6.5、注意问题3.7、Eureka参数调优3.7.1、客户端未正常下线3.7.2、Eureka Server剔除服务时间设置3.7.3、Eureka Client获取服务慢3.7.4、Eureka Client服务续约频率3.8、Eureka设置账号认证3.8.1、为什么要设置账号认证3.8.2、EurekaServer服务端设置认证账号3.8.3、EureakClient客户端注册地址设置认证账号3.9、使用RestTemplate实现服务通信3.9.1、如何实现服务同信3.9.2、服务通信实战3.9.3、小结3.10、客户端负载均衡Ribbon+RestTemplate3.10.1、为什么要Ribbon3.10.2、什么是Ribbon3.10.3、Ribbon的工作机制(重点)3.10.4、提供者-用户服务集群3.10.5、消费者-订单服务集成Ribbon3.10.6、Ribbon负载均衡算法3.10.7、Ribbon调优配置3.10.8、小结
课程介绍
为什么需要学习SpringCloud
SpringCloud概述
SpringCloud入门
应用架构的演变
简单来说我们以前传统的应用的就是单体架构,即所有的模块,组件等都在一个应用中应用最终打成一个(war,jar)包使用一个容器(Tomcat)进行部署,通常一个应用享用一个数据库。
在单体应用中我们通常把应用分为三个组成部分:持久层,业务层,表现层,这样的应用结构在项目初期业务较少的情况下没有任何问题,但是随着业务需求不断的增加,要求单体应用中的业务逻辑,业务组件等日益扩张,应用将会变得越来越臃肿,往后的开发和维护就会变得特别麻烦,再加上越来越大的访问量,并发越来越高,面对海量的用户无论从应用性能还是从数据库方面都有吃不消的时候。所以单体应用在数据量,并发量到一定程度的时候一定会遇到瓶颈。如下图:
对于高并发、大数据量 ,处理不占优势
开发时间越长,代码越多,项目越臃肿,杂乱无章
模块与模块,业务与业务耦合高 :比如一个模块挂了,其他模块也挂,一个模块升级,其他模块也要重启
技术选型单一,数据库选型单一
项目体积庞大的时候,会造成,编译慢,项目打包等也慢。
二次开发,维护难
新的程序员对项目需要花很长时间去熟悉,成本高。
不方便局部拓展,只能整体做集群
项目初期,项目的搭建,开发都比较快
技术成本低,对程序员的要求相对低 , 开发成本较低
项目的部署相对简单 - 就一个tomcat
在单体架构中,为了提升应用的并发能力和防止应用的单节点故障(一个Tomcat挂了,应用就挂了),我们通常会对应用做集群,这里的集群指的是把单个应用复制多个,分别部署到不同机器上,一起工作来提高作业能力,多个应用做的是相同的事情。
当应用做了集群之后作业能力得到提升,能够处理更高的并发请求,同时产生了一个新的问题,就是客户端的请求应该如何相对平均的分发到集群中的多个应用呢?这就需要负载均衡器了
在我们的应用程序中也是这样,当我们的应用做了集群,那么就会存在多个应用节点,多个应用将会暴露多个访问地址(ip:port),那客户端是不知道该访问哪个应用节点的,这个时候我们就需要有一个请求分发的功能的组件(负载均衡器)将客户端的请求相对平均的分发多个应用节点上,这就是负载均衡,这个做请求分发的组件就是负载均衡器,如下图:
这里的Nginx就是一个负载均衡器,它可以按照某种算法(轮询,ip_hash等等)将请求路由到不同的后端服务器上,同类型的负载均衡器还有如“HAproxy”,“LVS”等,这里不展开讨论。
负债均衡算法(Nginx)
轮询(round robin) : 依次将请求分配到各个后台服务器中,默认的负载均衡方式
权重(weight) : 根据权重值将请求分配到后台服务器中,权重值越大,分配比例越高
IP_HASH:按照ip地址进行hash运算,同一ip的请求会被分配到相同的机器上
url_hash : 根据请求的url的hash值将请求分到不同的机器中。
fair:根据服务器响应时间来分发请求,时间越短分发的请求越多
分布式就是将应用按照业务功能拆分成多个子系统,多个子系统部署在不同的服务器中,多个子系统组成一个完整的系统,所有的子系统一起工作,相互通信、相互协调才能完成一个完整的业务流程,缺一不可。
注意,集群和分布式的区别:
1、集群:是将同一份代码部署到多个服务器中
2、分布式:将一个系统按照业务功能进行拆分成多个子系统,然后分别部署到不同服务器中
SOA是面向服务的架构。它的思想是每个子应用可以通过网络通信协议向其他子应用提供服务或者消费服务,SOA也是分布式架构,我们可以简单的理解为SOA把分布式架构划分成表示层和服务层,服务层中包含了业务逻辑和相关流程,只需要对外暴露服务即可,表现层负责处理和页面的交互。这样的划分好处在于系统之间调用的方便性,如用户子系统只需要调用订单子系统的服务层即可完成应用之间的通信。这样的结构划分提高了应用的重用性,业务逻辑也变得可组合。
如图:
SOA架构中有重要的两个角色,服务消费者(Consumer)和服务提供者(Provider)即服务调用者和服务被调用者,这样的架构优点有:
模块拆分,使用API通信,降低模块之间的耦合度
项目拆分多个子应用,每个子应用业务简单,代码简单,方便维护开发
不同技术人员可以负责不同的子应用
提高服务之间的重用性,业务逻辑可组合
其缺点在于:
服务之间的API接口开发增加了工作量
SOA服务之间的网络通信调用对性能有一定的影响(尽管很小)
相对于单体应用来说,技术,人力成本较高
部署和运维相对麻烦
微服务是一种架构风格,它把一个单一的应用,按照业务/模块/api等方式进行拆分成多个微小的服务(应用),这些小的服务相对独立,有自己的进程,自己的Tomcat,甚至可以有自己的数据库, 微服务之间使用网络通信协议进行数据交互(通常是基于HTTP的RESTful API)。,最终组成一个完整的系统,缺一不可。如下图:
服务之间相对解耦合:用户的重启(宕机),不影响其他服务
单看某个微服务,业务简单,方便开发维护
轻量级HTTP通信机制,使得的不同的服务可以采用不同的编程语言,数据库选型多样化
扩展性强:方便局部扩展,可以针对某个微服务做扩容(集群),或缩容
一个程序员(一个组)只需要关注一个微服务即可 ,简单。特别是新员工上手快。
方便持续集成,持续交互,敏捷开发。
技术要求高,对程序员要求比较高
项目的构建相对复杂,开发成本高,资金成本高
部署麻烦,需要借助一些自动化部署工具
微服务之间的通讯会有延迟,一定程度上来说影响性能
虽然微服务有诸多好处,但是不是任何系统都适合用微服务架构来开发,在项目规模不大的情况下单体应用性能表现良好,当项目规模较大,用户体量,并发较大的时候,微服务总体性能占优势,所以我们应该根据项目类型以及项目规模来决定应用架构的选型,如大型电商,物流,售票等系统我们可以选择使用微服务架构,对于中小型的企业级应用我们依然可以选择单体架构。
SpringCloud是一个基于SpringBoot实现的服务治理工具包,用于微服务架构中管理和协调服务的。Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务注册发现、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。有了SpringCloud之后,让微服务架构的落地变得更简单。
简单理解:
SpringBoot:是用来快速开发一个一个的微服务
SpringCloud:是用来治理这些微服务的
当我们的项目采用微服务架构之后就会引发一些列的难题需要去解决,如众多微服务的通信地址应该如何管理,微服务之间应该使用何种方式发起调用,微服务故障该如何处理,众多微服务的配置文件如何集中管理等等,SpringCloud为这一系列的难题提供了相应的组件来解决,下面我们简单来理解一下SpringCloud最核心的几大组件如如下:
1、Netflix Eureka 当我们的微服务过多的时候,管理服务的通信地址是一个非常麻烦的事情,Eureka就是用来管理微服务的通信地址清单的,有了Eureka之后我们通过服务的名字就能实现服务的调用。
2、Netflix Ribbon\Feign : 客户端负载均衡 Ribbon和Feign都是客户端负载均衡器,它的作用是在服务发生调用的时候帮我们将请求按照某种规则分发到多个目标服务器上,简单理解就是用来解决微服务之间的通信问题。
3、Netflix Hystrix :断路器 微服务的调用是非常复杂的,有的时候一个请求需要很多的微服务共同完成,那么一旦某个服务发生故障,导致整个调用链上的微服务全都出现异常,甚至导致整个微服务架构瘫痪。Hystrix就是用来解决微服务故障,保护微服务安全的组件。
4、Netflix Zuul : 服务网关 zuul作为服务网关,我们可以把它看作是微服务的大门,所有的请求都需要经过zuul之后才能到达目标服务,根据这一特性,我们可以把微服务公共的是事情交给zuul统一处理,如:用户鉴权,请求监控等。
5、Spring Cloud Config :分布式配置 微服务架构中的服务实例非常的多,服务的配置文件分散在每个服务中,每次修改服务的配置文件和重新服务实例都是一个很麻烦的工作,Spring Cloud Config作为分布式配置管理中心就是用来统一的管理服务的配置文件。
6、Spring Cloud Bus : 消息总线 消息总线是在微服务中给各个微服务广播消息的一个组件,我们使用消息总线构建一个消息中心,其他微服务来接入到消息中心,当消息总线发起消息,接入的微服务都可以收到消息从而进行消费。
7、Spring Cloud sleuth :微服务链路追踪 当我们的应用采用微服务架构之后,后台可能有几十个甚至几百个服务在支撑,一个请求请求可能需要多次的服务调用最后才能完成,链路追踪的作用就是来监控维护他们之间的调用关系,让程序员方便直观的感受到一个请求经历了哪些微服务,以及服务的请求时间,是否有异常等。
SpringCloud是基于SpringBoot的,所以两者的jar包都需要导入,需要注意的是SprinbCloud的版本需要和SpringBoot的版本兼容。下面是版本兼容图:
RPC: Remote Procedure Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型
Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。 现在热门的Rest风格,就可以通过http协议来实现, SpringCloud基于Http。
如何选择:
其实我们并不需要刻意去选择该使用RPC或者是Http,如果架构师选择Dubbo作为微服务开发框架,通信方式自然就是RPC,当架构师选择了SpringCloud作为微服务开发框架,那么通信方式自然就是基于Http协议,当然,如果服务的通信方式成为了微服务的性能瓶颈,那么架构师自然需要去考虑更换微服务的通信协议。
Dubbo最早是由阿里巴巴提供的一个服务治理和服务调用框架,现在已经成为Apache的顶级项目,Dubbo跟SpringCloud最显著的区别是Dubbo的定位只是一个RPC框架,相比SpringCloud来说它缺少很多功能模块,如:网关,链路追踪等,所以往往在使用Dubbo作为微服务开发框架的时候,还需要去配合其他的框架一起使用,如:加入zookeeper作为注册中心。
定位不一样:SpringCloud为了微服务的落地提供了一套完整的组件,而Dubbo只是一个RPC框架,缺失的功能很多
通信协议不一样:Dubbo的通信方式是RPC,基于原生的tcp,性能较好,而SpringCloud的通信方式基于Http协议,虽然底层基于tcp,但是Http的封装过于臃肿,但是使用Http好处在于互相通信的两个服务可以使用不同的编程语言去编写,只要他们都支持Http通信即可互相调用,而Dubbo只支持Java,当然Dubbo交给Apache维护之后做了升级,Dubbo在以后不仅仅支持Java
背景都很强大:Dubbo背靠阿里和Apache;SpringCloud是Spring家族的成员;
开发风格不一样:Dubbo官方推荐倾向于使用Spring xml配置方式,SpringCloud是基于SpringBoot的开发风格,即采用注解的方式进行配置,从开发速度上来说,SpringCloud具有更高的开发和部署速度
下面是网上比较流行的Dubbo和Spring Cloud功能对比图
SpringCloud 是微服务架构的最佳落地方案
非常优雅的解决了微服务架构中存在的许多问题(服务注册发现、负载均衡、配置中心、路断器、链路追踪、消息总线、容错保护等)
SpringCloud天然支持 Spring Boot,更加便于业务落地
SpringCloud来源于 Spring,质量、稳定性、持续性都可以得到保证
SpringCloud 是 Java 领域最适合做微服务的框架。相比于其它框架,SpringCloud 对微服务周边环境的支持力度最大。对于中小企业来讲,使用门槛较低
微服务的其中一个特点是服务之间需要进行网络通信,服务器之间发起调用时调用服务得知道被调用服务的通信地址,试问当微服务数量成百上千之多,程序员该如何管理众多的服务通信地址,对于随时新增加的微服务和下线的微服务,又应该如何去动态添加和删除这些微服务的通信地址呢?所以手工管理服务的通信地址是一件遥不可及的事情,我们需要借助一个强大的工具帮我们实现这一功能 - Eureka,同类型的组件还有 zookeeper,consul,nacos等。
下面我们用一张图来介绍Eureka的工作流程:
服务注册
Eureka是一个服务注册与发现组件,简单说就是用来统一管理微服务的通信地址的组件,它包含了EurekaServer 服务端(也叫注册中心)和EurekaClient客户端两部分组成,EurekaServer是独立的服务,而EurekaClient需要集成到每个微服务中。
微服务(EurekaClient)在启动的时候会向EurekaServer提交自己的服务信息(通信地址如:服务名,ip,端口等),在 EurekaServer会形成一个微服务的通信地址列表存储起来。 — 这叫服务注册
服务发现
微服务(EurekaClient)会定期(RegistryFetchIntervalSeconds:默认30s)的从EurekaServer拉取一份微服务通信地址列表缓存到本地。当一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址,然后基于HTTP协议向目标服务发起请求。—这叫服务发现
服务续约
另外,微服务(EurekaClient)采用定时(LeaseRenewalIntervalInSeconds:默认30s)发送“心跳”请求向EurekaServer发请求进行服务续约,其实就是定时向 EurekaServer发请求报告自己的健康状况,告诉EurekaServer自己还活着,不要把自己从服务地址清单中剔除掉,那么当微服务(EurekaClient)宕机未向EurekaServer续约,或者续约请求超时,注册中心机会从服务地址清单中剔除该续约失败的服务。
服务下线
微服务(EurekaClient)关闭服务前向注册中心发送下线请求,注册中心(EurekaServer)接受到下线请求负责将该服务实例从注册列表剔除
这里我们采用“用户”、“订单”等业务来演示整个SpringCloud的各个组件
工程目录结构:
1、搭建项目结构
欲善其事,比利其器,为了方便演示SpringCloud的各个组件,我这里采用多模块的方式搭建项目,所有的jar包交给父工程来管理,搭建好的项目结构如下:
springcloud-parent //父项目
pom.xml //父项目的pom
springcloud-eureka-server-1010 //注册中心EurekaServer
springcloud-user-server-1020 //用户服务EurekaClient,提供者
springcloud-order-server-1030 //订单服务EurekaClient,消费者
2、父项目管理依赖
springcloud-parent父工程负责管理SpringBoot和SpringCloud的jar包 ,pom.xml如下:
<?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.itsource</groupId>
<artifactId>springcloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-eureka-server-1010</module>
<module>springcloud-user-server-1020</module>
<module>springcloud-order-server-1030</module>
</modules>
<!-- 打包方式:pom 代表这个项目只做pom管理,不写代码 -->
<packaging>pom</packaging>
<!--公共的一些配置-->
<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.version>Finchley.SR1</spring-cloud.version>
</properties>
<!--1.管理 SpringBoot的jar包-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<!--2.管理 SpringCloud的jar包-->
<!--注意:dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖-->
<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>
</project>
在springcloud-parent父工程下面搭建好子工程springcloud-eureka-server-1010,然后我们来集成EurekaServer
1、导入依赖
<?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>springcloud-parent</artifactId>
<groupId>cn.itsource</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-eureka-server-1010</artifactId>
<dependencies>
<!--EurekaServer端的基础依赖:spring-cloud-starter-netflix-eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
提示:spring-cloud-starter-netflix-eureka-server作为EurekaServer端的基础依赖,但同时这个包也把EurekaClient端的依赖也导入进来了,spring-boot-starter-web作为web服务的基础依赖是不可缺少的。
2、主配置类
/**
* @description: 注册中心启动类
*/
@SpringBootApplication
//@EnableEurekaServer : 开启EurekaServer服务端
@EnableEurekaServer
public class EurekaServerApplication1010 {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication1010.class);
}
}
提示:在主配置类上通过 @EnableEurekaServer 注解开启了EurekaServer端的功能
3、application.yml配置文件
server:
port: 1010 #端口
eureka:
instance:
hostname: localhost #主机IP
client: #客户端配置
registerWithEureka: false #EurekaServer自己不要注册到EurekaServer自己 ,只有EurekaClient才注册
fetchRegistry: false #EurekaServer不要拉取服务的通信地址列表 ,只有EurekaClient才拉取地址列表
serviceUrl: #注册中心的注册地址
defaultZone: http://localhost:1010/eureka/
提示:这里配置了EurekaServer的端口为 1010 ,主机名 localhost ,需要特别说明的是我们再引入EurekaServer的基础依赖spring-cloud-starter-netflix-eureka-server时,这个依赖即引入了EurekaServer所需要的包,也引入了EurekaClient的包,换句话说,现在的springcloud-eureka-server-1010工程既是一个 EurekaServer,也是一个EurekaClient。
我们这里暂时把EurekaClient的功能屏蔽掉 ,即关闭它的服务注册和发现功能,让他做好EurekaServer该做的事情即可。
serviceUrl是服务注册地址,EurekaClient需要注册到EurekaServer就得跟上该地址
registerWithEureka=false :禁止自己向自己注册
fetchRegistry=false : 禁止拉取服务注册列表
4、启动测试
启动springcloud-eureka-server-1010工程,浏览器访问 http://localhost:1010 ,出现如下界面代码EurekaServer集成成功:
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
官方的定义:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,测试环境可以关闭自我保护,但是生产环境时最好开启自我保护。
关闭Eureka自我保护后的配置如下:
server:
port: 1010 #端口
eureka:
instance:
hostname: localhost #主机
client: #客户端配置
registerWithEureka: false #EurekaServer自己不要注册到EurekaServer自己
fetchRegistry: false #不要拉取服务的通信地址列表
serviceUrl: #注册中心的注册地址
defaultZone: http://localhost:1010/eureka/ #http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false #关闭自我保护
根据上一章节我们的Eureka的理解,Eureka分为服务端和客户端,服务端已经搭建成功,我们来搭建客户端
1、导入依赖
<?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>springcloud-parent</artifactId>
<groupId>cn.itsource.springboot</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-user-server-1020</artifactId>
<name>springcloud-user-server-1020</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
2、主配置类
我们可以在主配置通过注解@EnableEurekaClient标记服务作为Eureka客户端
/**
* @description: 用户服务的启动类
*/
@SpringBootApplication
//@EnableEurekaClient:标记该应用是Eureka客户端(可不写,因为导入spring-cloud-starter-netflix-eureka-client 依赖后,默认就开启了EurekaClient)
@EnableEurekaClient
public class UserServerApplication1020 {
public static void main(String[] args) {
SpringApplication.run(UserServerApplication1020.class);
}
}
提示:主配置类通过打@EnableEurekaClient注解开启EurekaClient客户端功能,当然如果不打这个标签也能实现功能,因为导入spring-cloud-starter-netflix-eureka-client 依赖后,默认就开启了EurekaClient
3、application.yml配置
在配置文件中,我们需要通过eureka.client.serviceUrl配置EurekaServer的地址,EurekaClient根据该地址把自己注册给服务端
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1010/eureka/
spring:
application:
name: user-server
server:
port: 1020
提示:serviceUrl是EurekaServer注册中的地址,主机和端口都应该指向springcloud-eureka-server-1010工程,这里额外指定了服务的名字,和端口,这些信息都会被注册到EurekaServer
注意:此时,启动用户服务时,会发现启动失败,并有报错信息:
2021-08-16 10:27:45.625 WARN 8324 --- [ Thread-11] .s.c.a.CommonAnnotationBeanPostProcessor : Invocation of destroy method failed on bean with name 'scopedTarget.eurekaClient': org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaInstanceConfigBean': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
Process finished with exit code 0
此时需要在用户服务的pom.xml中加入web的基础依赖 spring-boot-starter-web,因为需要Tomcat的支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
再次启动就成功了
4、测试EurekaClient
启动springcloud-eureka-server-1010 , 启动springcloud-user-server-1020 , 浏览器再次访问http://localhost:1010,那你应该可以看到我们的user-server服务已经被注册到EurekaServer。如下:
订单服务和用户服务的做法一样,只是yml配置文件中有些稍微的不同。
1、导入依赖
<?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>springcloud-parent</artifactId>
<groupId>cn.itsource</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-order-server-1030</artifactId>
<dependencies>
<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>
</dependencies>
</project>
2、主配置类
/**
* @description: 订单服务的启动类
*/
@SpringBootApplication
//@EnableEurekaClient:标记该应用是 Eureka客户端
@EnableEurekaClient
public class OrderServerApplication1030 {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication1030.class);
}
}
3、application.yml配置
订单服务除了服务名,端口和用户服务不一样,其他的都一样,如下:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1010/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
instance-id: order-server:1030 #实例ID
spring:
application:
name: order-server
server:
port: 1030
4、测试
启动订单服务,访问Eureka Server的监控界面:http://localhost:1010,可以看到订单服务也注册进去了,如下图所示:
prefer-ip-address: true #使用ip地址进行注册
instance-id: order-server:1030 #配置实例ID
在客户端添加上面配制后,效果就变成下图所示样子了:
这就OK了,显示我们自己配置的实例ID了,将鼠标移动过去,左下角就会显示IP和端口了。
如果只有一个EurekaSever,如果EurekaSever挂了那么整个微服务都不可用,解决方案:EurekaServer高可用集群。
准备三个EurekaServer 相互注册,也就是说每个EurekaServer都需要向所有的EureakServer注册,包括自己 ,每个EurekaServer即充当了服务端,也充当了客户端。咱们的其他微服务(order,user)只需要把注册地址指向所有的EurekaServer就可以了。
创建本地域名
路径:C:\Windows\System32\drivers\etc,修改hosts文件,如下图所示:
这样修改后,在本地可以模拟多个域名的效果(实际上都是指向本机)
修改注册中心eureka-server-1010实现高可用配置
注意配置规则:
采用SpringBoot单配置文件多环境配置
因为三个EurekaServer要相互注册 ,那么三个EurekaServer的 serviceUrl都是一样的,我们就把serviceUrl抽取到最上面,其中包括 使用ip地址注册 (prefer-ip-address),EureakServer自我保护机制(enable-self-preservation) ,以及服务名(application.name)都是相同点的,都可以抽到最上面。
每个EurekaServer不一样的配置:端口(port) ,环境名字(profiles),主机名(hostname),实例id(instance-id:),具体配置如下:
#相同配置抽取到上面统一配置
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
instance:
prefer-ip-address: true #启用IP注册
server:
enable-self-preservation: false #关闭自我保护警告
spring:
profiles:
active: peer3
application:
name: eureka-server
---
#peer1第1个Eureka
spring:
profiles: peer1
server:
port: 1010 #端口
eureka:
instance:
hostname: peer1 #主机
instance-id: eureka-peer1:1010
---
#peer2第2个Eureka
spring:
profiles: peer2
server:
port: 1011 #端口
eureka:
instance:
hostname: peer2 #主机
instance-id: eureka-peer2:1011
---
#peer3第3个Eureka
spring:
profiles: peer3
server:
port: 1012 #端口
eureka:
instance:
hostname: peer3 #主机
instance-id: eureka-peer3:1012
这里相当于是做了三个Eureak服务端的配置,端口分别是:1010,1011,1012
需要指定IDEA的多实例启动配置
第一步:
第二步:
我这里的IDEA版本是:IntelliJ IDEA 2020.3 (Ultimate Edition) 所以这里是勾上这个勾,其他版本可能是下面这样操作:
上图中的文字写错了,这里的勾应该去掉不勾,就可以多次启动了
启动EurekaServer集群
需要启动三次EurekaServerApplication1010,需要注意的是,每次启动的时候需要修改spring.profiles.active的值激活不同的环境
测试集群
分别访问http://localhost:1010 ;http://localhost:1011;http://localhost:1012 ,在三个Eureak Server的监控界面都呈现如下效果:
这样就大功告成了,三个EurekaServer服务端就集群成功了。
修改客户端注册中心地址
用户和订单的微服务的配置中,注册中心地址修改为所有的EurekaServer注册地址即可,如下图所示:
原来指向一个Eureka服务端地址,现在指向三个即可
测试集群
分别启动三个Eureka Server注册中心服务 ,然后再启动用户和订单服务, 分别访问:http://localhost:1010 ;http://localhost:1011;http://localhost:1012 , 可以看到订单服务和用户服务在三个Eurea Server监控界面都可以看到,如下图所示:
Replicas:复制品的意思
集群后,页面上面有个DS Replicas下面应该不会出现自己的,但是如果你在服务端配置了:
prefer-ip-address: true #用IP注册
那就会出现自己了。最好不要配置该项
在IDEA测试集群时,千万不要采用热部署,不要引入下面的包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
如果引入上面的包了,就会出现一种现象:peer1启动成功,然后启动peer2时,peer1会被关闭掉,然后以peer2的端口启动,最终只会启动一个,无法启动三台模拟集群场景。
当Eureka Client客户端意外宕机没及时通知Eureka Server时,需要及时移除该服务信息,该种情况可以通过 eureka.server.eviction-interval-timer-in-ms=5000 来指定Eureka Server定时清除过期数据的频率(默认是60秒),尽量早的清除掉过时的数据。
如下图所示(在服务端配置):
Eureaka Server缓存
Eureka Server缓存可能会导致服务信息残留问题,可以通过 eureka.server.use-read-only-response-cache=false关闭缓存
在服务端配置
eureka server在收到eureka client的心跳续约请求之后,默认会等90s如果没有收到第二次续约请求就会把服务从服务注册列表剔除掉,可以通过 eureka.instance.lease-expiration-duration-in-seconds=90修改服务该时间。
如下图所示:(在服务端配置)
Eureka Client默认从Eureka Server获取服务注册列表的间隔时间为 30秒,在开发阶段我们可以适当调整该参数让客户端可以更快的发现服务,如:eureka.client.registry-fetch-interval-seconds=10将定时获取服务注册列表时间调整为 10s一次。
如下图所示:(在客户端配置)
Eureka Client默认每隔30s发送一个心跳请求到Eureka Server进行服务续约,表明它仍然活着,默认为30 秒,如果达到3次续约失败(leaseExpirationDurationInSeconds默认90s),Eurea Server将会把续约失败的服务剔除掉,可以通过 eureka.instance.lease-renewal-interval-in-seconds=10来修改心跳频率时间。 如下图所示:(在客户端配置)
在之前的案例中,我们可以直接通过浏览器访问Eureka的控制台界面看到注册的服务列表,这是一个很危险的操作,在生产环境中我们需要有相关的认证机制,即需要通过输入用户名和密码认证后才能访问Eureak的控制台界面
我们需要改造Eureka服务端的pom文件,添加security安全依赖,如下图所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
导入该依赖后,我们需要添加一个security的配置,这个配置目的是忽略跨域伪造请求检查,因为EurekaServer集成了Security后,默认是要求向EureakServer发起的请求中带有CSRF令牌,而EurekaClient的请求不可能有这个令牌,所以我们要禁用该行为,增加如下配置代码:
package cn.itsource.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//忽略伪造跨站检查
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
接着我们需要在yml中配置账号和密码, 同时需要修改注册中心的注册地址,也需要加上账号和密码,如下配置即可:
spring:
security:
user:
name: admin #账号,自己定义
password: 123456 #密码,自己定义
同时,serviceUrl.defaultZone的注册地址要修改,格式:http://账号:密码@主机:端口/eureka/
如下图所示:
然后再分别启动三个服务端,浏览器访问http://localhost:1010/,就需要使用账号密码进行验证了,如下图所示:
输入admin/123456登录后显示如下图所示页面:
三个服务端依然成功集群了,到此大功告成。
接下来将用户服务和订单服务中的注册中心地址也需要加入账号认证,如下图所示:
用户服务和订单服务的yml配置文件中的defaultZone值,都改成上图所示即可,启动客户端服务,再刷新服务端管理页面:
可以看到,两个客户端成功注册到服务端了,大功告成。
1、需求说明
目前除了Eureka Server以外我们的微服务有订单服务springcloud-order-server-1030,和用户服务springcloud-user-server-1020 , 我们就用这两个服务来演示微服务的通信,他们的调用关系应该是:浏览器 -> 订单服务 -> 用户服务,如下图:
这里订单服务通过RestTemplate向用户服务发起调用,目的是要获取到用户服务返回的User对象,最终是需要浏览器获取到User。
2、解决方案
用户服务需要提供User对象,我们需要为其编写Controller接口,编写相关方法返回User,订单服务需要从用户服务获取到User对象,而浏览器需要访问订单服务获取到User对象,所以订单服务也需要编写Controller接口供浏览器来调用。
我们发现不管是用户服务还是订单服务都需要用到User对象,那么是不是在用户服务和订单服务都需要创建User的模型?当然没必要,公共的东西就是要抽取,所以我们会把User对象封装在一个公共的模块 springcloud-user-common中然后让用户服务和订单服务都去依赖这个模块。
3、RestTemplate介绍
微服务的通信协议主流的有RPC,Http,SpringCloud是基于Http Restful 风格 ,在Java中发起一个Http请求的方式很多,比如 Apache的HttpClient , OKHttp等等 。Spring为我们封装了一个基于Restful的使用非常简单的Http客户端工具 RestTemplate ,我们就用它来实订单服务和用户服务的通信。需要注意的是,RestTmplate本身不具备服务发现和负载均衡器的功能,我们本章的案例只是演示在订单服务中使用RestTemplate基于ip和端口的方式向用户服务发起调用,即:不走注册中心,不使用服务发现方式。
1、搭建公共User模块
创建工程模块 springcloud-user-common ,效果如下:
springcloud-parent
springcloud-eureka-server-1010
springcloud-order-server-1030
springcloud-user-common //公共User模块
springcloud-user-server-1020
在springcloud-user-common中创建User对象如下:
public class User {
private Long id;
private String username;
private String desc;
public User() {
}
public User(Long id, String username, String desc) {
this.id = id;
this.username = username;
this.desc = desc;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
2、用户服务和订单服务依赖User模块
修改工程springcloud-order-server-1030 , springcloud-user-server-1020在他们的pom.xml都导入springcloud-user-common 模块
<dependency>
<groupId>cn.itsource</groupId>
<artifactId>springcloud-user-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
用户服务返回User对象
修改 springcloud-user-server-1020 工程,编写controller,返回User对象
//用户服务:暴露接口给订单访问
@RestController
public class UserController {
//订单服务来调用这个方法 http://localhost:1020/user/10
// @GetMapping(value = "/user/{id}" )
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id")Long id){
//根据id去数据库查询User
return new User(id, "张三", "大家好,我是张三");
}
}
订单服务获取User
在订单服务中需要使用RestTemplate调用用户服务,我们需要把RestTmplate配置成Bean方便使用(当然也可以不创建Bean,用的时候直接new对象也可以) ,修改工程springcloud-order-server-1030在主配置类配置RestTemplate如下:
/**
* 订单的启动类
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServerApplication1030
{
//配置一个RestTemplate ,Spring封装的一个基于Restful风格的http客户端工具
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(OrderServerApplication1030.class);
}
}
创建controller,通过RestTemplate调用用户服务,代码如下:
//订单服务
@RestController
public class OrderController {
//需要配置成Bean
@Autowired
private RestTemplate restTemplate ;
//浏览器调用该方法
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id")Long id){
//发送http请求调用 user的服务,获取user对象 : RestTemplate
//目标资源路径:user的ip,user的端口,user的Controller路径
String url = "http://localhost:1020/user/"+id;
//发送http请求
return restTemplate.getForObject(url, User.class);
}
}
这里的url是用户服务获取user资源的路径,通过RestTmplate发起Http调用,拿到User对象后返回给浏览器。
测试服务通信
依次启动Eureka Server注册中心(不启动也行) , 用户服务 ,订单服务 , 浏览器访问订单服务:http://localhost:1030/order/11 , 页面显示结果:
虽然这里我们基于RestTemplate实现了订单服务和用户服务的通信,但是我们在请求用户服务的时候是使用ip地址和端口的方式,在之前的Eureka章节我们说到,Eureka的目的就是用来管理微服务的通信地址,服务之间可以使用服务名发起调用,很显然我们还没有满足这一情况。在下一章节我们将使用Ribbon客户端负载均衡配合RestTmplate使用服务发现的方式实现服务的通信。
我们知道,为了防止应用出现单节点故障问题,同时为了提高应用的作业能力,我们需要对应用做集群 ,如果我们对user-server(用户服务)做了集群 ,那么这个时候回衍生出一些问题:现在有两个user-server(用户服务)就意味着有两个user-server(用户服务)的通信地址,我的order-server(订单服务)在向user-server(用户服务)发起调用的时候该访问哪个?如何访问?这个时候就需要有一个组件帮我们做请求的分发,即:负载均衡器,而Ribbon就是一个客户端负载均衡器。
Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,Ribbon可以按照负载均衡算法(如简单轮询,随机连接等)向多个服务发起调用(正好可以解决上面的问题),我们也很容易使用Ribbon实现自定义的负载均衡算法。
如下图,我们将user-server(用户服务)做集群处理,增加到2个节点(注意:两个user-server(用户服务)的服务名要一样,ip和端口不一样),在注册中心的服务通信地址清单中user-server(用户服务)这个服务下面会挂载两个通信地址 。 order-server(订单服务)会定时把服务通信地址清单拉取到本地进行缓存, 那么当order-server(订单服务)在向user-server(用户服务)发起调用时,需要指定服务名为 user-server(用户服务);那么这个时候,ribbon会根据user-server(用户服务)这个服务名找到两个user-server的通信地址 , 然后ribbon会按照负载均衡算法(默认轮询)选择其中的某一个通信地址,发起http请求实现服务的调用,如下图:
在理解了Ribbon工作机制之后,我们就来编码实战上图描述的场景
服务集群方案
使用SpringBoot多环境配置方式集群,一个配置文件配置多个user-server环境 ,需要注意的是集群中的多个服务名(spring.application.name)应该一样,我们把相同的东西提取到最上面,不同的东西配置在各自的环境中。
用户服务集群配置
修改 application.yml 如下:
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
#使用ip地址进行注册
instance:
prefer-ip-address: true
spring:
application:
name: user-server #服务名都叫user-server
profiles:
active: user-server1
---
server:
port: 1020
eureka:
instance:
instance-id: user-server:1020
spring:
profiles: user-server1
---
server:
port: 1021
eureka:
instance:
instance-id: user-server:1021
spring:
profiles: user-server2
用户服务集群配置成功,启动方式同注册中心集群一样。需要配置服务运行多实例启动,启动时需要注意修改spring.profiles.active的值来切换不同的实例。
修改用户服务的Controller
为了后续测试的时候方便区分不同的用户服务实例,这里我们在Controller中读取应用的端口随User返回,在启动不同的用户服务实例的时候端口是不同的,修改后的代码如下:
/**
* @description: 用户服务API
*/
@RestController
public class UserController {
//加载端口
@Value("${server.port}")
private int port;
@GetMapping(value = "/user/{id}")
public User getUserById(@PathVariable(value = "id") Long id){
//查询数据库,根据入参ID查询出对应的用户对象
//http://localhost:1020/user/11
return new User(id, "张三", "大家好,我是张三,端口=" + port);
}
}
1、导入Ribbon依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2、开启负载均衡
修改RestTemplate的Bean的定义方法,加上Ribbon的负载均衡注解@LoadBalanced赋予RestTemplate有负载均衡的能力。
3、修改调用用户服务的方式
在之前的案例中,我们调用用户服务是通过用户服务的主机加上端口“localhost:1020”的调用方式,现在我们把 “localhost:1020” 修改为 用户服务的服务名 。底层会通过服务发现的方式使用Ribbin进行负载均衡调用。
4、测试Ribbon
分别启动EurekaServer注册中心 ,启动两个UserServer用户服务,启动OrderServer订单消费者服务,浏览器访问订单服务:http://localhost:1030/order/11,发送多次请求。
观察响应的结果中的端口变化 - 端口会交替出现1020,,1021,我们可以推断出Ribbon默认使用的是轮询策略。
1、内置算法
Ribbon内置7种负载均衡算法。
2、配置负载均衡算法
Ribbon可以进行全局负载均衡算法配置,也可以针对具体的服务做不同的算法配置。同时可以使用注解方式和yml配置方式来实现上面两种情况。
注解全局配置
随机算法的效果最好演示,我们把负载均衡算法修改成随机算法,只需要RandomRule配置成Bean即可,修改主配置类如下:
测试:
重启订单服务,访问http://localhost:1030/order/1 ,发送多次请求应该可以看到结果中的端口随机变动。
配置具体服务的负载均衡
如果要针对某一服务配置负载均衡算法可以通过@RibbonClient注解来实现。 先来定义一个注解,用来排除配置类不被扫描到(后面有解释):
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoScan {
}
然后定义一个配置类,在配置类中定义随机算法策略:
@Configuration
@NoScan //这是一个空标签,是用来防止@SpringBootApplication 扫描到该类
public class RibbonConfig {
@Bean
public IRule randomRule(){
return new RandomRule();
}
}
提示:需要注意的是这个类我们是要交给RibbonClient作为配置,而不应该被主配置类上的@SpringBootApplication(这个标签里面有个@ComponentScan)所扫描到,不然会不起作用(官方文档上有明确的解释),这里有两种方法可以不让@SpringBootApplication扫描到,1是调整 RibbonConfig 所在包的位置 (@MapperScan扫描当前包及其子包), 2是通过@ComponentScan排除注解的方式,所以我们在配置类上贴了一个注解@NoScan ,后面我们需要根据这个注解排除配置不被扫描。
主配置类通过@RibbonClient指定服务的负载均衡配置,同时排除@NoScan 注解不被扫描:
/**
* 订单的启动类
*/
@SpringBootApplication
//排除 NoScan标签所在的类
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = NoScan.class))
@EnableEurekaClient
@RibbonClient(value = "user-server",configuration = RibbonConfig.class)
public class OrderServerApplication1030
{
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
//@LoadBalanced :让RestTemplate有负载均衡的功能
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(OrderServerApplication1030.class);
}
}
除了 @RibbonClient 注解以外还可以通过@RibbonClients来对多个服务进行策略配置:
@RibbonClients({
@RibbonClient(value = "服务名1",configuration = RibbonConfig1.class),
@RibbonClient(value = "服务名2",configuration = RibbonConfig2.class)
})
yml方式配置负载均衡算法
配置全局Ribbon算法
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
配置某个服务的Ribbon算法
user-server:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
pay-server:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
1、超时配置
使用Ribbon进行服务通信时为了防止网络波动造成服务调用超时,我们可以针对Ribbon配置超时时间以及重试机制
ribbon:
ReadTimeout: 3000 #读取超时时间
ConnectTimeout: 3000 #链接超时时间
MaxAutoRetries: 1 #重试机制:同一台实例最大重试次数
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
OkToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
当然也可以针对具体的服务进行超时配置:如"<服务名>.ribbon…"
user-server:
ribbon:
ReadTimeout: 3000 #读取超时时间
ConnectTimeout: 3000 #链接超时时间
MaxAutoRetries: 1 #重试机制:同一台实例最大重试次数
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
OkToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
2、饥饿加载
我们在启动服务使用Ribbon发起服务调用的时候往往会出现找不到目标服务的情况,这是因为Ribbon在进行客户端负载均衡的时候并不是启动时就创建好的,而是在实际请求的时候才会去创建,所以往往我们在发起第一次调用的时候会出现超时导致服务调用失败,我们可以通过设置Ribbon的饥饿加载来改善此情况,即在服务启动时就把Ribbon相关内容创建好。
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: user-server #针对于哪些服务需要饥饿加载
Ribbon的使用相对比较简单,配合RestTemplate使用注解@LoadBalanced即可完成负载均衡配置,但是在我们的订单服务Controller中向用户服务发起请求的代码就显得不简单了。我们需要手动去拼接目标服务的URL,以及参数,可能参数比较简单的时候你没什么感觉,当地址比较复杂,参数比较多的时候,拼接URL就会得特别麻烦,而且显得好傻,在下一章节我们会学习另外一个客户端负载均衡Feign,它在Ribbon的基础上进行了封装,让服务的调用方式显得更简答和高级。