springcloud搭建集群,负载均衡 服务熔断 服务降级

it2025-11-15  2

springcloud

创建maven项目,添加依赖 <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> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.9.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <finalName>springcloudtest01</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <delimiters> <delimit>$</delimit> </delimiters> </configuration> </plugin> </plugins> </build> 把src文件删除,创建module子工程,例如,cloud-api

在此项目中添加配置例如 lombok,也可不加

将api项目打包成jar,步骤如下:

创建项目cloud-provider-dept-8001,同上一样,module

父工程查看,多了一个子工程:

8001中的pom文件添加:

<dependencies> <!-- 引入自己定义的api通用包,可以使用Dept部门Entity --> <dependency> <groupId>com.dw</groupId> <artifactId>cloud-api</artifactId> <version>${project.version}</version> </dependency> <!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</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-test</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>

8001项目中编写yml文件:

server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml type-aliases-package: com.dw.entity mapper-locations: - classpath:mybatis/mapper/*.xml spring: application: name: cloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/clouddb01?useUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8 username: root password: root dbcp2: min-idle: 5 initial-size: 5 max-total: 5 max-wait-millis: 200

resources包下创建mybatis包,该包下创建mybatis.cfg.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true" /><!-- 二级缓存开启 --> </settings> </configuration>

创建数据库:

drop database if exists clouddb01; create DATABASE clouddb01 CHARACTER SET utf8; use clouddb01; create TABLE dept( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());

src/main/java下创建包com.dw.dao,接口DeptDao,service,impl,controller层 写好以及mybaits文件下创建mapper DeptDao.xml映射文件:

创建主启动类,注意位置

运行项目,输入 localhost:8001/dept/list


目前为止,微服务的生产者已经创建成功,接下来需要创建微服务中的消费者

创建module,cloud-consumer-dept-80,pom添加配置信息

<dependencies> <dependency><!-- 自己定义的api --> <groupId>com.dw</groupId> <artifactId>cloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- Ribbon相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>

端口80 设置:

在src/main/java下创建包com.dw.cfgbeans,并创建ConfigBean

创建controller,并创建主启动:

启动8001 项目和80项目,输入 http://localhost/consumer/dept/get/1

运行成功,说明生成者和消费者都可以正常运行。


Eureaka 构建步骤

什么是Eureaka?

Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于rest的服务,用于定位服务,以实现云端中间层服务发现的故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如zookeeper.

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

Eureka Server提供服务注册服务

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

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

Eureka Server 提供服务注册和发现Service Provider 服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到Service Consumer 服务消费方,从Eureka获取注册的服务列表,从而能够消费服务

创建项目module,cloud-eureka-7001,配置pom:

<dependencies> <!--eureka-server服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>

配置当前项目application.yml:

server: port: 7001 eureka: instance: hostname: localhost #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

创建主启动类,并加上@EnableEurekaServer注解,表示当前项目是服务端:

测试,输入 localhost:7001/

出现此页面,7001项目正常运行。


目前,eureka已经创建完成

接下来需要把服务的提供者,注册到Eureka

修改8001 项目,pom文件中添加,看是否有

<!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>

修改8001 中的yml文件,添加:

eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka

启动类加入EnableEurekaClient注解:

测试,先启动7001,然后启动8001,输入http://localhost:7001/

application中已经有一个应用了,说明8001项目已经注册到了eureka中心。

服务名称修改:

8001项目中的application.yml加上配置:

instance: instance-id: microservicecloud-dept prefer-ip-address: true #访问路径可用显示ip

那么在eureka注册中心可见如下信息:

这样相当于给8001服务起了个别名

那么 加上 ip显示提示 true时,鼠标放在服务名字上,左下角显示当前服务所在ip地址:

继续修改8001项目中的application.yml文件,添加如下:

info: app.name: dw-microservicecloud company.name: www.dw.com build.artifactId: $project.artifactId$ build.version: $project.version$

那么,点击eureka中的服务链接,显示内容如下:

目前位置,服务已经注册到了eureka中。

8001 yml配置如下:

server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml type-aliases-package: com.dw.entity mapper-locations: - classpath:mybatis/mapper/*.xml spring: application: name: cloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/clouddb01?useUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8 username: root password: root dbcp2: min-idle: 5 initial-size: 5 max-total: 5 max-wait-millis: 200 eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: microservicecloud-dept prefer-ip-address: true #访问路径可用显示ip info: app.name: dw-microservicecloud company.name: www.dw.com build.artifactId: $project.artifactId$ build.version: $project.version$
服务已经注册到了Eureka中,那么80项目应该从Eureka中发现服务

​ 在8001项目中添加服务发现接口,在controller包的DeptController中添加代码:

​ 这里注意导包:import org.springframework.cloud.client.discovery.DiscoveryClient;

@Resource DiscoveryClient client; /* * 测试 用来查看eureka中所有可见的服务 开发中不需要写这个方法 * * getInstances("") 其中引用 application的名字 例如 我这里时CLOUD-DEPT */ @RequestMapping(value = "/dept/discovery",method = RequestMethod.GET) public Object discovery() { List<String> list =client.getServices(); System.out.println("====="+list); List<ServiceInstance> serList = client.getInstances("CLOUD-DEPT"); for(ServiceInstance element:serList) { System.out.println(element.getServiceId()+"\t"+element.getHost()); } return this.client; }

在 8001 主启动类 中添加注解 @EnableDiscoveryCilent 允许被发现

启动7001 项目 和 8001项目,输入http://localhost:8001/dept/discovery

这里显示的时已经注册到eureka中的服务,同时,控制台也有打印信息:

这里时输出已经注册到eureka中的服务


以上结束8001已经注册服务,那么80项目怎么才可用发现8001服务,接着继续

修改80 工程

找到controller中DeptController_Consumer

添加:

@RequestMapping(value = "/consumer/dept/discovery") public Object discovery() { return restTemplate.getForObject(REST_URL_PREFIX+"/dept/discovery", Object.class); }

启动 80 项目,此时 7001 8001 80 都启动,输入http://localhost/consumer/dept/discovery

实现:

浏览器----->80项目----->restTemplate远程调用----->8001----->从eureka中获取所有已注册的项目信息


Eureka集群搭建

新建工程7002 和7003

把7001 项目中内容 copy到 7002 和7003

修改7002 和 7003 主启动类

修改映射配置信息

- 修改7001 7002 7003 yml

启动 7001 7002 7003 8001 输入http://eureka7001.com:7001/

输入http://eureka7002.com:7002/

输入http://eureka7003.com:7003/

现在,集群搭建成功。


Ribbon 负载均衡

Ribbon是什么?

spring cloud Ribbon 是基于Netflix Ribbon实现的一套客户端 负载均衡工具。 简单的说,Ribbon是NetFlix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善额配置项 如连接超时,重试等。就是在配置文件中列出Load Balancer(简称LB)后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

重点是客户端的 负载均衡

以上,只有80项目是客户端(消费者),所以要修改80项目,加上负载均衡

修改80的pom文件,添加Ribbon相关的依赖

修改80的yml文件,没有经过eureka,现在要经过eureka访问8001:

server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

这样客户端就知道注册中心的位置了。

修改80项目的ConfigBean,添加注解即可:@LoadBalanced

这样 客户端就可以通过负载均衡来访问服务了,但是还没有配置完。

80 项目的主启动类加上注解:@EnableEurekaClient

修改80 项目的controller:要与注册中心的服务名相同

这样,客户端就通过注册中心找到服务端进行功能的调用。

启动7001 7002 7003,启动8001 ,启动客户端80:

输入http://localhost/consumer/dept/get/1

80项目(客户端)通过eureka访问 8001(服务端)提供的功能

浏览器输入url----->80客户端-----> 根据 eureka的 CLOUD-DEPT 找到服务,8001是根据此名 注册到了eureka标志

80在eureka中根据它找到8001,调用8001服务。


Ribbon负载均衡

Ribbon在工作时分成两步: 第一步先选择EurekaServer,它优先选择在同一个区域内负责较少的server 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址

上图提供了三个服务实例,目前我们只有一个服务实例 8001,还不算真正意义上的客户端的负载均衡。

例如 在卖煎饼果子,现在只有张三一个服务人员在卖,客户没有办法选择去哪排队。

所以,现在要建服务端的集群,即8002 8003,加上8001,一个三个实例。

步骤:

新建8002和8003

8001pom yml 包 各种全部 copy到 8002 8003,并修改其启动类名字,yml各自对应各自的端口号

以及创建数据库,修改各自连接数据库的 表(这里是为了到时候区分,负载均衡访问效果做了此操作,往下看)

创建数据库:2 3

DROP database if EXISTS clouddb02; CREATE DATABASE clouddb02 character set utf8; USE clouddb02; CREATE TABLE dept ( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT into dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT into dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT into dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT into dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT into dept(dname,db_source) VALUES('运维部',DATABASE()); DROP database if EXISTS clouddb03; CREATE DATABASE clouddb03 character set utf8; USE clouddb03; CREATE TABLE dept ( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT into dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT into dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT into dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT into dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT into dept(dname,db_source) VALUES('运维部',DATABASE());

测试:

先启动eureka 集群 3个,7001 7002 7003

然后启动 8001 8002 8003,可先输入http://localhost:8001/dept/list,http://localhost:8002/dept/list ,http://localhost:8003/dept/list ,查看能否查出数据,说明三个服务端正常访问

启动80,输入http://localhost/consumer/dept/list,显示结果:

第一次访问:

第二次刷新:

第三次刷新:

那么,这样就达到了效果,默认是轮询规则。


Ribbon算法:

默认是轮询,改变算法,例如使用随机算法:

打开80项目中的配置类,ConfigBean,添加如下:

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

达到随机访问。


Fegin

Feign就是代替RestTemplate

什么是Feign?

Feign 是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring cloud 对Feign进行了封装,使其支持了Spring mvc标准注解 和HttpMessageConverters.Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

步骤如下:这里文字描述,不截图了

此时可在80 项目中改,也可新建一个 做区分,掌握其中关键点即可

例如,80项目所有都copy到新的fegin项目目录下

此fegin 的pom中添加依赖

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>

修改api,api工程新建DeptClientService接口,并新增注解 @FeignClient

例如:@FeignClient(value = " CLOUD-DEPT")

然后写上接口方法,同8001

然后api 依照之前方式打包成jar

回到 feign工程,修改controller,注入DeptClientService ,该方法调用即可,之前是restTemplate

// 调用api项目中的service @Autowired DeptClientService deptClientService; @RequestMapping(value = "/consumer/dept/list") public List<Dept> list() { return this.deptClientService.list(); }

在 fegin项目启动类加上注解:

@EnableFeignClients(basePackages = {“com.dw”}) @ComponentScan(“com.dw”)

测试 :

运行7001-7003,8001-8003,最后启动regin,输入http://localhost/consumer/dept/list

能正常访问,因为已封装了Ribbon,认采用轮询。


Hystrix断路器

服务雪崩

多个微服务之间相互调用的时候,假设微服务A调用微服务B,微服务B调用微服务C.C又调用其他的服务,这就是“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的服务器资源,进而引起系统崩溃,这就是“雪崩效应”。


熔断机制是应对服务雪崩的一种微服务链路保护机制。当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在springCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的情况。当失败的调用到一定阈值,默认是5秒内20此调用失败会启动熔断机制。

步骤如下:参考主要

之前的8001举例,

添加pom依赖

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>

修改controller,这里为了模拟出错 然后返回信息效果写了如下代码:

@RequestMapping(value = "/dept/get/{deptno}",method = RequestMethod.GET) @HystrixCommand(fallbackMethod = "processHystrix_Get") public Dept getById(@PathVariable("deptno")Long deptno) { Dept dept = deptService.findById(deptno); // 故意出错 if(null == dept) { throw new RuntimeException("该id没有对应的信息"); } return dept; } public Dept processHystrix_Get(@PathVariable("deptno")Long deptno) { Dept dept = new Dept(); dept.setDeptno(deptno); dept.setDname("没有对应的信息"); dept.setDb_source("no this db"); return dept; }

然后再hystrix主启动类添加 注解**@EnableCircuitBreaker**

测试,启动三个eureka,启动该hystrix项目,再启动80

输入http://localhost/consumer/dept/get/112,一个不存在的id

显示:

说明熔断机制起了作用。

最新回复(0)