springcloud学习
笔记整理来源 B站UP主狂神说https://www.bilibili.com/video/BV1jJ411S7xr
参考:菜鸟小杰子 https://blog.csdn.net/lijie0213/article/details/107914619
参考:路飞 https://csp1999.blog.csdn.net/article/details/106255122?spm=1001.2014.3001.5502
1、概述
1.1、学习前提
- 熟练使用springboot开发框架
- 了解过Dubbo + Zookeeper 分布式基础
- 电脑配置内存不低于8G(我自己的是16G)
1.2、学习目的
- 熟练使用springcloud搭建微服务框架
- 了解微服务相关内容(面试题)
2、springcloud项目搭建
2.1、总体介绍
- 我们会使用一个Dept部门模块做一个微服务通用案例Consurmer消费者(Client)通过REST调用Provider提供者(Server)提供的服务
- 回忆Spring、SpringMVC、Mybatis等以往学习的知识
- Maven的分包模块架构复习
简单来说就是一个父工程项目包含了多个子工程项目
一个父工程带着多个Moudule子模块
MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)
microservicecloud-api 【封装的整体entity/接口/公共配置等】
microservicecloud-consumer-dept-80 【服务提供者】
microservicecloud-provider-dept-8001 【服务消费者】
2.2、springcloud版本选择
大版本说明
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) |
实际开发版本关系
spring-boot-starter-parent | spring-cloud-dependencles | ||
---|---|---|---|
版本号 | 发布日期 | 版本号 | 发布日期 |
1.5.2.RELEASE | 2017-03 | Dalston.RC1 | 2017-x |
1.5.9.RELEASE | 2017-11 | Edgware.RELEASE | 2017-11 |
1.5.16.RELEASE | 2018-04 | Edgware.SR5 | 2018-10 |
1.5.20.RELEASE | 2018-09 | Edgware.SR5 | 2018-10 |
2.0.2.RELEASE | 2018-05 | Fomchiey.BULD-SNAPSHOT | 2018-x |
2.0.6.RELEASE | 2018-10 | Fomchiey-SR2 | 2018-10 |
2.1.4.RELEASE | 2019-04 | Greenwich.SR1 | 2019-03 |
建议使用后两个开发版本
2.3、创建相关工程
1、新建父工程项目
新建一个maven项目,如下所示
- 新建好如下所示:
删除掉src文件,只保留一个pom文件(父工程不需要编写逻辑内容)
- pom文件内容:
<?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>
<!-- 打包方式 pom-->
<packaging>pom</packaging>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springCloud-api</module>
<module>springCloud-provider-dept-8001</module>
<module>springCloud-consumer-dept-80</module>
</modules>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<lombok.version>1.18.16</lombok.version>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<!-- 添加详细依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入springcloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</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>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
maven中type和scope在多继承中用法(复习):
pom文件设置
dependency为什么会有type为pom,默认的值是什么?
dependency中type默认为jar即引入一个特定的jar包。那么为什么还会有type为pom呢?当我们需要引入很多jar包的时候会导致pom.xml过大,我们可以想到的一种解决方案是定义一个父项目,但是父项目只有一个,也有可能导致父项目的pom.xml文件过大。这个时候我们引进来一个type为pom,意味着我们可以将所有的jar包打包成一个pom,然后我们依赖了pom,即可以下载下来所有依赖的jar包
scope作用域
- compile:默认作用域,表示再此作用域的依赖项,在编译,运行和测试都是有效的
- provided:测试和编译有效
- runtime:运行和测试有效
- test: 测试时有效
- system:表示该依赖项是由我们提供的,不需要maven到仓库中寻找。使用的时候需要和另一个标签
systemPath
配合使用,这个标签表示该依赖项在系统中的位置,使用的是绝对路径
例子:
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
- import 多继承时候使用
通过继承一个项目的依赖,可以在子项目中很好的申明需要使用父项目的dependencyMangement中定义的依赖项。但是每一个项目只能申明唯一的一个父项目,那么在某些时候就会限制我们项目的建立。
为此,Maven为我们提供了一种方法,那就是通过设置依赖项的socpe为import。注意,这种方式只在Maven2.0.9以上的版本才生效。
只能在dependencyManagement中使用
父项目的依赖:
<project>
...
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>B</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</dependencyManagement>
...
</project>
如果使用之前的继承机制,那么这个时候子项目artifactB的pom.xml文件是这样定义的:
<project>
...
<parent>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.0</version>
</parent>
<groupId>groupA</groupId>
<artifactId>artifactB</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>A</artifactId>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>D</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
...
</project>
import方式
<project>
...
<groupId>groupA</groupId>
<artifactId>artifactB</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>A</artifactId>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>D</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
...
</project>
2、创建实体 springcloud-api
- 封装服务涉及到的实体,公共配置等
- 创建dept部门实体类:
package com.lzh.springcloud.dept;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 部门信息
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:44:34
*/
@Data
@Accessors
@NoArgsConstructor
public class Dept {
/**主键*/
private long deptno;
/**部门名称*/
private String dname;
/**存储数据库名称*/
private String db_source;
}
- 脚本如下所示:
DROP TABLE IF EXISTS `dept_test`;
CREATE TABLE `dept_test` (
`deptno` bigint(20) NOT NULL COMMENT '主键',
`dname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '部门名称',
`db_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '存储数据库名称',
PRIMARY KEY (`deptno`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门数据' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of dept_test
-- ----------------------------
INSERT INTO `dept_test` VALUES (1, '开发部', 'mysql');
INSERT INTO `dept_test` VALUES (2, '研发部', 'mysql');
INSERT INTO `dept_test` VALUES (3, '事业部', 'mysql');
SET FOREIGN_KEY_CHECKS = 1;
3、创建服务的提供者
- pom文件如下所示:
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springCloud-provider-dept-8001</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入实体-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
- 配置文件 application.yml
server:
port: 8001
mybatis:
# 配置检索的实体的包的前缀
type-aliases-package: com.lzh.springcloud.dept
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
application:
# 微服务的唯一标识
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowMultiQueries=true
username: root
password: root
-
编写业务代码,单表的crud
-
DeptController:
package com.lzh.springcloud.dept.controller;
import com.lzh.springcloud.dept.Dept;
import com.lzh.springcloud.dept.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:51:49
*/
@RestController
@RequestMapping("/dept")
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/add")
public boolean addDept(Dept dept) {
return deptService.addDept(dept);
}
@RequestMapping("/get/{id}")
public Dept queryById(@PathVariable("id") Long id) {
return deptService.queryById(id);
}
@RequestMapping("/list")
public List<Dept> queryAll() {
return deptService.queryAll();
}
}
- DeptService:
package com.lzh.springcloud.dept.service;
import com.lzh.springcloud.dept.Dept;
import java.util.List;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:52:38
*/
public interface DeptService {
/**
* 添加部门
*
* @param dept
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
boolean addDept(Dept dept);
/**
* 根据id查出一个部门
*
* @param id
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
Dept queryById(Long id);
/**
* 查询所有部门信息
*
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
List<Dept> queryAll();
}
- DeptServiceImpl
package com.lzh.springcloud.dept.service.Impl;
import com.lzh.springcloud.dept.Dept;
import com.lzh.springcloud.dept.dao.DeptDao;
import com.lzh.springcloud.dept.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:53:09
*/
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptDao deptDao;
@Override
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptDao.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptDao.queryAll();
}
}
- DeptDao:
package com.lzh.springcloud.dept.dao;
import com.lzh.springcloud.dept.Dept;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:58:08
*/
@Mapper
public interface DeptDao {
/**
* 添加部门
*
* @param dept
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
boolean addDept(Dept dept);
/**
* 根据id查出一个部门
*
* @param id
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
Dept queryById(Long id);
/**
* 查询所有部门信息
*
* @return
* @author LZH
* @date 2023/4/7 17:34:21
* @version 1.0
*/
List<Dept> queryAll();
}
- DeptMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--核心配置文件-->
<mapper namespace="com.lzh.springcloud.dept.dao.DeptDao">
<!--增加一个部门-->
<insert id="addDept" parameterType="com.lzh.springcloud.dept.Dept">
insert into dept_test (dname,db_source) values (#{dname},DATABASE())
</insert>
<!--根据id查询部门信息-->
<select id="queryById" resultType="com.lzh.springcloud.dept.Dept">
select * from dept_test where deptno = #{depono};
</select>
<!--查询所有的部门信息-->
<select id="queryAll" resultType="com.lzh.springcloud.dept.Dept">
select * from dept_test;
</select>
</mapper>
- 启动类:
package com.lzh.springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/07 17:44:41
*/
@Slf4j
@SpringBootApplication
public class springcloud_8001 {
public static void main(String[] args) {
SpringApplication.run(springcloud_8001.class);
log.info("启动成功!!");
}
}
- 启动之后测试:
4、编写消费者:
- pom依赖
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springCloud-provider-dept-80</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入实体-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
- 配置文件:
server:
port: 80
mybatis:
# 配置检索的实体的包的前缀
type-aliases-package: com.lzh.springcloud.dept
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
application:
# 微服务的唯一标识
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowMultiQueries=true
username: root
password: root
- DeptConsumerController:
package com.lzh.springcloud.deptController;
import com.lzh.springcloud.dept.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* 消费者
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:06:35
*/
@RestController
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate;
// 理解:消费者,不该有service层
// RestTemplate.. 里面有方法供我们调用即可,注册到spring中
//(String url,实体, Class<T> responseType)
//提供多种便捷访问远程http服务的方法,简单的Restful服务模板
//声明提供者的localhost路径
private static final String rest_url_prefix = "http://localhost:8001";
//调用8001提供者的控制器=>根据id查询数据
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
// restTemplate.注意和提供者的getmapping或postmapping保持一致
return restTemplate.getForObject(rest_url_prefix+"/dept/get/"+id,Dept.class);
}
//调用8001提供者的控制器=>添加方法
@RequestMapping("/consumer/dept/add")
public Boolean add(Dept dept){
// restTemplate.注意和提供者的getmapping或postmapping保持一致
return restTemplate.postForObject(rest_url_prefix+"/dept/add",dept,Boolean.class);
}
//调用8001提供者的控制器=>查询所有
@RequestMapping("/consumer/dept/list")
public List<Dept> queryall(){
// restTemplate.注意和提供者的getmapping或postmapping保持一致
return restTemplate.getForObject(rest_url_prefix+"/dept/list",List.class);
}
}
- Config:
package com.lzh.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:08:30
*/
@Configuration
public class Config {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 启动类:
package com.lzh.springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:14:38
*/
@Slf4j
@SpringBootApplication
public class Springcloud_80 {
public static void main(String[] args) {
SpringApplication.run(Springcloud_80.class);
log.info("启动成功!!");
}
}
- 启动8001服务和80服务,使用80服务调用8001服务
3、Eureka服务注册中心
3.1 什么是Eureka
Netflix在涉及Eureka时,遵循的就是API原则.
Eureka是Netflix的有个子模块,也是核心模块之一。Eureka是基于REST的服务,用于定位服务,以实现云端中间件层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper.
3.2 原理理解
Eureka基本的架构
Springcloud 封装了Netflix公司开发的Eureka模块来实现服务注册与发现 (对比Zookeeper).
Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,他是服务注册中心.
而系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,Springcloud 的一些其他模块 (比如Zuul) 就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑.
和Dubbo架构对比.
Eureka 包含两个组件:Eureka Server 和 Eureka Client.
Eureka Server 提供服务注册,各个节点启动后,回在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会储存所有课用服务节点的信息,服务节点的信息可以在界面中直观的看到.
Eureka Client 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳 (默认周期为30秒) 。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (默认周期为90s).
三大角色
Eureka Server:提供服务的注册与发现
Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能狗找到
Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务
3.3、配置Eureka服务注册中心
1、pom文件中引入Eureka依赖
2、配置文件中配置Eureka相关配置
3、启动类上加上Eureka注解
1、启动类:
package com.lzh.springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/10 11:42:21
*/
@SpringBootApplication
//表示为Eureka的启动类,接受别人注册进来
@EnableEurekaServer
@Slf4j
public class Springcloud_7001 {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Springcloud_7001.class);
log.info("启动成功!!");
}
}
2、配置文件:
server:
port: 7001
# Eureka配置
eureka:
instance:
# eureka实例名称
hostname: 127.0.0.1
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
fetch-registry: false
# Eureka监控页面~
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3、pom文件:
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-eureka-7001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 引入父依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 引入eureka依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
4、访问如下所示:
Eureka服务消费,注册、信息配置,自我保护机制
5、在springCloud-provider-dept-8001中添加Eureka依赖
1、pom文件:
<!-- 添加Eureka依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
启动类上添加Eureka注解
package com.lzh.springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/07 17:44:41
*/
@Slf4j
@SpringBootApplication
//再服务启动后自动注册到Eureka注册中心去
@EnableEurekaClient
public class springcloud_8001 {
public static void main(String[] args) {
SpringApplication.run(springcloud_8001.class);
log.info("启动成功!!");
}
}
配置类上添加Eureka配置
eureka:
client:
service-url:
# 访问7001
defaultZone: http://localhost:7001/eureka/
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-provider-dept8001
启动8001服务和7001服务如下所示:
status中的描述就是在配置类中,配置的Eureka下的instance下的instance-id的内容
停掉8001服务,等待30s后会开启保护机制:
配置服务加载的监控信息:
不配置点开是404
pom文件中添加:
<!-- 添加status跳转出页面的详情-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置文件中添加:
info:
app.name: com.lzh.springclod
company.name: 苏州xx公司
点击status下的路由跳转
如下所示:
3.4、EureKa自我保护机制:好死不如赖活着
一句话总结就是:某时刻某一个微服务不可用,eureka不会立即清理,依旧会对该微服务的信息进行保存!
默认情况下,当eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除(默认是90秒),但是,如果短时间内丢失大量的实例心跳,便会触发eureka server的自我保护机制,比如在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),当一分钟内收到的心跳数大量减少时,会触发该保护机制。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.从警告中可以看到,eureka认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。
该保护机制的目的是避免网络连接故障,在发生网络故障时,微服务和注册中心之间无法正常通信,但服务本身是健康的,不应该注销该服务,如果eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到eureka server了,因为只有在微服务启动的时候才会发起注册请求,后面只会发送心跳和服务列表请求,这样的话,该实例虽然是运行着,但永远不会被其它服务所感知。所以,eureka server在短时间内丢失过多的客户端心跳时,会进入自我保护模式,该模式下,eureka会保护注册表中的信息,不在注销任何微服务,当网络故障恢复后,eureka会自动退出保护模式。自我保护模式可以让集群更加健壮。
但是我们在开发测试阶段,需要频繁地重启发布,如果触发了保护机制,则旧的服务实例没有被删除,这时请求有可能跑到旧的实例中,而该实例已经关闭了,这就导致请求错误,影响开发测试。所以,在开发测试阶段,我们可以把自我保护模式关闭,只需在eureka server配置文件中加上如下配置即可:eureka.server.enable-self-preservation=false【不推荐关闭自我保护机制】
3.5、获取注册进来的服务的一些信息
启动类加上获取客户端服务的注解
package com.lzh.springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/07 17:44:41
*/
@Slf4j
@SpringBootApplication
//开启Eureka客户端注解,再服务启动后自动注册到Eureka注册中心去
@EnableEurekaClient
//开启服务获取客户端注解,获取一些服务,得到一些信息
@EnableDiscoveryClient
public class springcloud_8001 {
public static void main(String[] args) {
SpringApplication.run(springcloud_8001.class);
log.info("启动成功!!");
}
}
接口中获取服务相关信息
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/get/discoveryInfo")
public Object discoveryInfo(){
List<String> services = discoveryClient.getServices();
List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-provider-dept");
instances.forEach(l->{
System.out.println("主机:"+l.getHost());
System.out.println("主机:"+l.getUri());
System.out.println("主机:"+l.getInstanceId());
System.out.println("主机:"+l.getServiceId());
System.out.println("主机:"+l.getScheme());
});
return services;
}
访问如下所示
3.6、Eureka集群搭建,暂时就简单看一下了
Eureka对比zookeeper
- 回顾CAP原则
RDBMS (MySQL\Oracle\sqlServer) ===> ACID
NoSQL (Redis\MongoDB) ===> CAP
-
ACID是什么?
A (Atomicity) 原子性
C (Consistency) 一致性
I (Isolation) 隔离性
D (Durability) 持久性 -
CAP是什么?
C (Consistency) 强一致性
A (Availability) 可用性
P (Partition tolerance) 分区容错性
CAP的三进二:CA、AP、CP -
CAP理论的核心
一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求
根据CAP原理,将NoSQL数据库分成了满足CA原则,满足CP原则和满足AP原则三大类
CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差
CP:满足一致性,分区容错的系统,通常性能不是特别高
AP:满足可用性,分区容错的系统,通常可能对一致性要求低一些 -
作为分布式服务注册中心,Eureka比Zookeeper好在哪里?
著名的CAP理论指出,一个分布式系统不可能同时满足C (一致性) 、A (可用性) 、P (容错性),由于分区容错性P再分布式系统中是必须要保证的,因此我们只能再A和C之间进行权衡。
Zookeeper 保证的是 CP —> 满足一致性,分区容错的系统,通常性能不是特别高
Eureka 保证的是 AP —> 满足可用性,分区容错的系统,通常可能对一致性要求低一些
Zookeeper保证的是CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接收服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30-120s,且选举期间整个zookeeper集群是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络问题使得zookeeper集群失去master节点是较大概率发生的事件,虽然服务最终能够恢复,但是,漫长的选举时间导致注册长期不可用,是不可容忍的。
Eureka保证的是AP
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有之中自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka不在从注册列表中移除因为长时间没收到心跳而应该过期的服务
Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上 (即保证当前节点依然可用)
当网络稳定时,当前实例新的注册信息会被同步到其他节点中
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪
4、 Ribbon:负载均衡(基于客户端)
4.1 负载均衡以及Ribbon
Ribbon是什么?
Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。
简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说,就是在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的及其,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器。我们也容易使用 Ribbon 实现自定义的负载均衡算法!
Ribbon能干嘛?
LB,即负载均衡 (LoadBalancer) ,在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高用)。
常见的负载均衡软件有 Nginx、Lvs 等等。
Dubbo、SpringCloud 中均给我们提供了负载均衡,SpringCloud 的负载均衡算法可以自定义。
负载均衡简单分类:
集中式LB
即在服务的提供方和消费方之间使用独立的LB设施,如Nginx(反向代理服务器),由该设施负责把访问请求通过某种策略转发至服务的提供方!
进程式 LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
Ribbon 就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!
4.2、集成ribbon到消费者客户端
pom文件中引入ribbon的依赖和Eureka的依赖
<!-- 引入Eureka,ribbon可以从Eureka中获取需要拿的服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
启动类上加上Eureka的注解
//加入Eureka服务
@EnableEurekaClient
配置文件中配置Eureka依赖
# 配置 Eureka
eureka:
client:
service-url:
# 访问路径
defaultZone: http://localhost:7001/eureka/
# 不需要向Eureka中注册自己
register-with-eureka: false
config配置类中添加ribbon的注解
package com.lzh.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:08:30
*/
@Configuration
public class Config {
@LoadBalanced //配置负载均衡实现RestTemplate
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
DeptConsumerController中修改提供者的访问路径:
private static final String rest_url_prefix = "http://SPRINGCLOUD-PROVIDER-DEPT";
仿照8001依次建立8002,8003项目,创建8002和8003对应的数据库
修改连接方式,修改配置文件中关于服务的描述,instance-id属性改成 springcloud-provider-dept8003和 8002 ,dept_test表中的数据源名称修改成db02和db03
依次启动项目
查看Eureka服务注册
访问list接口,查看ribbon是从哪一个服务下拿数据的
刷新如下所示:
使用ribbon实现负载均衡已经实现了使用
4.3、自定义ribbon负载均衡算法:
以前ribbon采用的是轮询算法,改成算法一个服务使用五次,在使用下一个服务
ribbon中的随机算法:RandomRule.class 文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.netflix.loadbalancer;
import com.netflix.client.config.IClientConfig;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class RandomRule extends AbstractLoadBalancerRule {
public RandomRule() {
}
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
在主目录外面创建如下文件夹
RuleConfig如下所示:
package com.lzh.myrule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/12 10:44:11
*/
@Configuration
public class RuleConfig {
@Bean
public IRule myRule(){
return new MyRibbonRule();
}
}
MyRibbonRule代码如下:
package com.lzh.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/12 10:47:08
*/
public class MyRibbonRule extends AbstractLoadBalancerRule {
int totalCount = 0;
int currentIndex = 0;
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
// 老的版本 RandomRule 采用的是轮询算法,改成新的算法,每个服务依次用5次,然后再调用其他服务
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
// 获取active的服务
List<Server> upList = lb.getReachableServers();
// 获取所有的服务
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// 新的算法
if (totalCount < 5){
// 每个服务用五次
totalCount++;
server = upList.get(currentIndex);
}else {
// totalCount值大于5次说明,已经进入下一个服务了
totalCount = 0;
currentIndex++;
if (currentIndex>upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);
}
// 老的算法
// int index = this.chooseRandomInt(serverCount);
// server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
// 获取当前线程的下一个随机整数
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
启动类上加上
package com.lzh.springcloud;
import com.lzh.myrule.MyRibbonRule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClientName;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:14:38
*/
@Slf4j
//加入Eureka服务
@EnableEurekaClient
//启用自己创建的ribbon算法规则
@SpringBootApplication
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRibbonRule.class)
public class Springcloud_80 {
public static void main(String[] args) {
SpringApplication.run(Springcloud_80.class);
log.info("启动成功!!");
}
}
启动项目:
刷新五次即可进入下一个服务
5.Feign:负载均衡(基于服务端)
5.1 Feign简介
Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供负载均衡的http客户端
只需要创建一个接口,然后添加注解即可~
Feign,主要是社区版,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法
微服务名字 【ribbon】
接口和注解 【feign】
Feign能干什么?
Feign旨在使编写Java Http客户端变得更容易
前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一个客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它 (类似以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。
Feign默认集成了Ribbon
利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
1、新建feign项目
2、启动类中引入feign依赖
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-consumer-dept-feign-80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入实体-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Eureka,ribbon可以从Eureka中获取需要拿的服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
</project>
3、配置类
server:
port: 80
mybatis:
# 配置检索的实体的包的前缀
type-aliases-package: com.lzh.springcloud.dept
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
application:
# 微服务的唯一标识
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowMultiQueries=true
username: root
password: root
# 配置Eureka
eureka:
client:
# 不注册自己
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/
4、消费服务调用
package com.lzh.springcloud.deptController;
import com.lzh.springcloud.dept.Dept;
import com.lzh.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* 消费者
*
* @author LZH
* @version 1.0
* @date 2023/04/07 18:06:35
*/
@RestController
public class DeptConsumerController {
@Autowired
private DeptService deptClientService;
/**
* 消费方添加部门信息
* @param dept
* @return
*/
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return deptClientService.addDept(dept);
}
/**
* 消费方根据id查询部门信息
* @param id
* @return
*/
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return deptClientService.queryById(id);
}
/**
* 消费方查询部门信息列表
* @return
*/
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return deptClientService.queryAll();
}
}
5、启动类
package com.lzh.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/15 13:17:21
*/
@SpringBootApplication
@EnableEurekaClient
// feign客户端注解,并指定要扫描的包以及配置接口DeptClientService
@EnableFeignClients(basePackages = {"com.lzh.springcloud"})
public class springcloud_feign {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(springcloud_feign.class);
System.out.println("feign启动成功!!");
}
}
6、在springCloud-api中引入feign以来,并编写服务接口
<!-- 引入feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
7、dept接口:
package com.lzh.springcloud.service;
import com.lzh.springcloud.dept.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
/**
* dept相关service
*
* @author LZH
* @version 1.0
* @date 2023/04/15 13:34:39
*/
// @FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptService {
@GetMapping("/dept/get/{id}")
Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
List<Dept> queryAll();
@PostMapping("/dept/add")
boolean addDept(Dept dept);
}
8、依次启动
9、成功获取:
6、 Hystrix:服务熔断
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免失败!
6.1 服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某个微服务的调用响应时间过长,或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几十秒内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以达到单个依赖关系的失败而不影响整个应用程序或系统运行。
我们需要,弃车保帅!
6.2 什么是Hystrix?
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
6.3 Hystrix能干嘛?
服务降级
服务熔断
服务限流
接近实时的监控
…
当一切正常时,请求流可以如下所示:
当许多后端系统中有一个潜在阻塞服务时,它可以阻止整个用户请求:
随着大容量通信量的增加,单个后端依赖项的潜在性会导致所有服务器上的所有资源在几秒钟内饱和。
应用程序中通过网络或客户端库可能导致网络请求的每个点都是潜在故障的来源。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,从而备份队列、线程和其他系统资源,从而导致更多跨系统的级联故障。
当使用Hystrix包装每个基础依赖项时,上面的图表中所示的体系结构会发生类似于以下关系图的变化。每个依赖项是相互隔离的,限制在延迟发生时它可以填充的资源中,并包含在回退逻辑中,该逻辑决定在依赖项中发生任何类型的故障时要做出什么样的响应:
官网资料:https://github.com/Netflix/Hystrix/wiki
6.4 服务熔断
什么是服务熔断?
熔断机制是赌赢雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是:@HystrixCommand。
6.5 服务熔断解决如下问题:
当所依赖的对象不稳定时,能够起到快速失败的目的;
快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。
1、复制springCloud-provider-dept-8001模块
2、pom文件中新增服务熔断服务
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-provider-dept-hystrix-8001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入实体-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加Eureka依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 添加服务熔断依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 添加status跳转出页面的详情-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>
</dependencies>
</project>
3、启动类上添加,服务熔断注解
package springcloud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/07 17:44:41
*/
@Slf4j
@SpringBootApplication
//开启Eureka客户端注解,再服务启动后自动注册到Eureka注册中心去
@EnableEurekaClient
//开启服务获取客户端注解,获取一些服务,得到一些信息
@EnableDiscoveryClient
//开启服务熔断注解
@EnableCircuitBreaker
public class springcloud_hystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(springcloud_hystrix_8001.class);
log.info("启动成功!!");
}
}
4、controller添加,服务熔断后相关内容
package springcloud.dept.controller;
import cn.hutool.core.util.ObjectUtil;
import com.lzh.springcloud.dept.Dept;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springcloud.dept.service.DeptService;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/07 16:51:49
*/
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@RequestMapping("/dept/get/{id}")
// 添加服务熔断注解
@HystrixCommand(fallbackMethod = "hystrixMethod")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (ObjectUtil.isEmpty(dept)){
throw new RuntimeException("此id无法查询到用户信息");
}
return dept;
}
/**
* 服务熔断
*
* @param id
* @return
* @author LZH
* @date 2023/4/17 15:17:48
* @version 1.0
*/
public Dept hystrixMethod(@PathVariable("id") Long id){
return new Dept().setDeptno(id).setDname("服务熔断备选部门名称").setDb_source("服务数据源");
}
}
5、启动项目
访问不存在数据:
抛出异常导致服务熔断了
因此,为了避免因某个微服务后台出现异常或错误而导致整个应用或网页报错,使用熔断是必要的
6.6、服务降级
1、什么是服务降级?
服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
降级的方式可以根据业务来,可以延迟服务,比如延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行 ;或者在粒度范围内关闭服务,比如关闭相关文章的推荐。
由上图可得,当某一时间内服务A的访问量暴增,而B和C的访问量较少,为了缓解A服务的压力,这时候需要B和C暂时关闭一些服务功能,去承担A的部分服务,从而为A分担压力,叫做服务降级。
服务降级需要考虑的问题
1)那些服务是核心服务,哪些服务是非核心服务
2)那些服务可以支持降级,那些服务不能支持降级,降级策略是什么
3)除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
自动降级分类
1)超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
2)失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
3)故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
4)限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
2、简单实现
springCloud-api 中service中添加实现类
package com.lzh.springcloud.service;
import com.lzh.springcloud.dept.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 服务熔断内容
*
* @author LZH
* @version 1.0
* @date 2023/04/18 13:49:53
*/
@Component
public class DeptCilentServiceFallBackFactory implements FallbackFactory {
@Override
public DeptService create(Throwable throwable) {
return new DeptService(){
@Override
public Dept queryById(Long id) {
return new Dept().setDeptno(id).setDname("降级了,没有数据了").setDb_source("降级啦");
}
@Override
public List<Dept> queryAll() {
return null;
}
@Override
public boolean addDept(Dept dept) {
return false;
}
};
}
}
DeptService中添加服务降级方法
package com.lzh.springcloud.service;
import com.lzh.springcloud.dept.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
/**
* dept相关service
*
* @author LZH
* @version 1.0
* @date 2023/04/15 13:34:39
*/
// @FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptCilentServiceFallBackFactory.class)
public interface DeptService {
@GetMapping("/dept/get/{id}")
Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
List<Dept> queryAll();
@PostMapping("/dept/add")
boolean addDept(Dept dept);
}
在springCloud-consumer-dept-feign-80消费端开启服务降级
server:
port: 80
mybatis:
# 配置检索的实体的包的前缀
type-aliases-package: com.lzh.springcloud.dept
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
application:
# 微服务的唯一标识
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowMultiQueries=true
username: root
password: root
# 配置Eureka
eureka:
client:
# 不注册自己
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/
#开启服务降级
feign:
hystrix:
enabled: true
6.7 服务熔断和降级的区别
服务熔断—>服务端:某个服务超时或异常,引起熔断~,类似于保险丝(自我熔断)
服务降级—>客户端:从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用,此时在客户端,我们可以准备一个 FallBackFactory ,返回一个默认的值(缺省值)。会导致整体的服务下降,但是好歹能用,比直接挂掉强。
触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
实现方式不太一样,服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
熔断,降级,限流:
限流:限制并发的请求访问量,超过阈值则拒绝;
降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;
熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复
6.8 Dashboard 流监控
新建springcloud-consumer-hystrix-dashboard模块
pom文件中添加dashborad依赖
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-consumer-dept-hystrix-dashbord</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--dashboard流监控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入实体-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Eureka,ribbon可以从Eureka中获取需要拿的服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
</project>
配置文件:
server:
port: 9001
启动类:
package com.lzh.springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2023/04/18 17:17:37
*/
@SpringBootApplication
//开启dashboard监控
@EnableHystrixDashboard
public class dept_hystrix_dashbord {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(dept_hystrix_dashbord.class);
System.out.println("启动成功!!");
}
}
springCloud-provider-dept-hystrix-8001 启动类添加
package springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/07 17:44:41
*/
@Slf4j
@SpringBootApplication
//开启Eureka客户端注解,再服务启动后自动注册到Eureka注册中心去
@EnableEurekaClient
//开启服务获取客户端注解,获取一些服务,得到一些信息
@EnableDiscoveryClient
//开启服务熔断注解
@EnableCircuitBreaker
public class springcloud_hystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(springcloud_hystrix_8001.class);
log.info("启动成功!!");
}
//增加一个 Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
//访问该页面就是监控页面
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
访问http://localhost:9001/hystrix,填写相关监控服务
调用服务,可以看到实时监控
7、Zuul路由网关
7.1、什么是Zuul
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合, 将Zuu自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka
提供:代理+路由+过滤 三大功能
7.2、Zuul能干什么
路由
过滤
官网文档=> https://github.com/Netflix/zuul
7.3、为什么要建立Zuul
Netfix. API流量的数量和多样性有时会导致生产问题迅速出现而没有警告。我们需要一个允许我们快
速改支行为以对这些情况做出反应的系统。
Zuul使用了各种不同类型的过滤器,这使我们能够快速灵活地将功能应用于边缘服务。这些过滤器帮
助我们执行以下功能 :
身份验证和安全性识别每个资源的身份验证要求,井拒绝不满足要求的请求。
见解和监控在边缘跟族有意义的数据和统计信息,以便为我们提供准确的生产规图。
动态路由根据需要将请求动态路由到不同的后端群集
压力测试逐渐增加到群集的流量以评估性能。
减载-小每种类型的清求分配容量,并丢弃超出限制的请求。
静态响应处理-直接在边缘构建一 些响应。而不是将其转发到内部集群
多区域弹性在AWS区域之间路由请求,以多样化我们的ELB使用并将我们的优势拉近我们的成员有关更多详细信息:我们如何在Netfio中使用Zuul
7.4、Zuul组件
zuul-core–zuul核心库,包含编译和执行过滤器的核心功能
zuul-simple-webapp–zuul Web应用程序示例,展示了如何使用zuul-core构建应用程序
zuul-netflix–lib包,将其他NetflixOSS组件添加到Zuul中,例如使用功能区进去路由请求处理
zuul-netflix-webapp–webapp,它将zuul-core和zuul-netflix封装成一个简易的webapp工程包
7.5、Zuul路由网关 项目搭建
创建maven项目 springcloud/springcloud-zuul-9527
导入 springcloud-consumer-hystrix-dashboard 项目所有依赖,并加上Zuul依赖
创建子项目:
配置文件
server:
port: 9527
spring:
application:
# 服务名称
name: springcloud-zuul
#Eureka注册
eureka:
client:
service-url:
# 访问7001
defaultZone: http://localhost:7001/eureka/
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-zuul
prefer-ip-address: true
info:
app.name: com.lzh.springclod
company.name: 苏州xx公司
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
#之前的查询链接地址 http://activate.navicat.com:9527/springcloud-provider-dept/dept/get/1
#现在的查询链接地址,配置后为 http://activate.navicat.com:9527/mydept/dept/get/1
#两者都皆可访问(原路径+现配路径)。配置自定义的前缀后 可不让客户端知道真实的ip地址
mydept.path: /mydept/**
#加上此配置后 原路径不能访问(springcloud-provider-dept/dept/get/6),只能通过自定义的规则路径访问。
#ignored-services: springcloud-provider-dept
#星号(*) 隐藏全部的项目真实名字
ignored-services: "*"
prefix: /lzh #设置公共的地址前缀 配置后链接为:activate.navicat.com:9527/li/mydept/dept/get/11
pom文件
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-zuul</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入zuul网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入Eureka注册中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入feign负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入Hystrix服务熔断-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入ribbon客户端的负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 引入实体类-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
启动类:
package com.lzh.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 网关启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/20 16:25:45
*/
@SpringBootApplication
@EnableZuulProxy // 开启Zuul
public class Springcloud_zuul {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Springcloud_zuul.class);
System.out.println("启动成功!!!");
}
}
开启服务进行测试
查看 C:\Windows\System32\drivers\etc\hosts 模拟zuul网关
127.0.0.1 activate.navicat.com
没有配置zuul之前访问如下所示:
添加zuul配置之后访问如下所示:
我们看到,微服务名称被替换并隐藏,换成了我们自定义的微服务名称mydept,同时加上了前缀haust,这样就做到了对路由fan访问的加密处理!
详情参考springcloud中文社区zuul组件 :https://www.springcloud.cc/spring-cloud-greenwich.html#_router_and_filter_zuul
8、Spring Cloud Config 分布式配置
Dalston.RELEASE
Spring Cloud Config为分布式系统中的外部配置提供服务器和客户端支持。使用Config Server,您可以在所有环境中管理应用程序的外部属性。客户端和服务器上的概念映射与Spring Environment和PropertySource抽象相同,因此它们与Spring应用程序非常契合,但可以与任何以任何语言运行的应用程序一起使用。随着应用程序通过从开发人员到测试和生产的部署流程,您可以管理这些环境之间的配置,并确定应用程序具有迁移时需要运行的一切。服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。很容易添加替代实现,并使用Spring配置将其插入。
概述
分布式系统面临的–配置文件问题
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的。spring cloud提供了configServer来解决这个问题,我们每一个微服务自己带着一个application.yml,那上百个的配置文件修改起来,令人头疼!
什么是SpringCloud config分布式配置中心?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pE2ByaTt-1683193762693)(\49.png)]
spring cloud config 为微服务架构中的微服务提供集中化的外部支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
spring cloud config 分为服务端和客户端两部分。
服务端也称为 分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理。并且可用通过git客户端工具来方便的管理和访问配置内容。
spring cloud config 分布式配置中心能干嘛?
集中式管理配置文件
不同环境,不同配置,动态化的配置更新,分环境部署,比如 /dev /test /prod /beta /release
运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
当配置发生变动时,服务不需要重启,即可感知到配置的变化,并应用新的配置
将配置信息以REST接口的形式暴露
spring cloud config 分布式配置中心与GitHub整合
由于spring cloud config 默认使用git来存储配置文件 (也有其他方式,比如自持SVN 和本地文件),但是最推荐的还是git ,而且使用的是 http / https 访问的形式。
springcloud config环境搭建配置
注册码云账号 https://gitee.com/。可qq 微信登录
新建仓库 springcloud-config,设置为公开,语言和模板都选择java。开源许可证GPL-3.0,仓库创建后复制当前仓库的SSH隧道地址
在电脑新建文件夹,用GitBash工具 执行 git clone springcloud-config复制的SSH地址,进行拉取仓库内容,拉去后选择yes,因为权限不存在,需要配置当前Git
Git大全 [https://gitee.com/all-about-git]
1、第一步在git上创建一个仓库
2、拉取仓库信息
常用命令
# 显示当前的Git配置
$ git config --list
# 编辑Git配置文件
$ git config -e [--global]
# 设置提交代码时的用户信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"
# 下载一个项目和它的整个代码历史
$ git clone [url]
拉取代码
此时已经拿到远程的代码
创建application.ynl文件,并push到仓库中
spring:
profiles:
active: dev
---
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
提交成功:
入门案例学习
新建子项目:
pom文件如下所示:
<?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>springCloudStudy</artifactId>-->
<!-- <groupId>org.example</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>springCloud-config-server-2233</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springCloudStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!-- 监控可不加-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
启动类,开启服务配置注解
package com.lzh.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
* 启动类
*
* @author LZH
* @version 1.0
* @date 2023/04/24 14:39:34
*/
@SpringBootApplication
//开启配置服务
@EnableConfigServer
public class springcloud_2233 {
public static void main(String[] args) {
SpringApplication.run(springcloud_2233.class);
System.out.println("启动成功!!");
}
}
配置文件
server:
port: 2233
spring:
application:
name: springcloud-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/lzh_springcloud.git
#不加报错 Authentication is required but no CredentialsProvider has been registered
username: xxx
password: xxx
私密连接需要加上用户名和密码
报错:
Authentication is required but no CredentialsProvider has been registered
访问如下所示
http://localhost:2233/application-dev.yml
http://localhost:2233/application-test.yml
在仓库中添加config-cilent.yml配置文件
内容如下:
#启动环境选择的配置
spring:
profiles:
active: dev
#springboot启动多环境的配置
---
server:
port: 8201
#spring的配置
spring:
profiles: dev
application:
name: springcloud-config-client-dev
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
---
server:
port: 8202
#spring的配置
spring:
profiles: test
application:
name: springcloud-config-client-test
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
将文件push到远程仓库
git命令如下:
GitBash打开命令工具
cd 至 springcloud-config
git add . 将文件添加到暂存区
git status 查看状态
git commit -m “一次提交” 本地提交,-m为提交时写的信息
git push origin master 提交到远程的当前路径分枝