springcloud 学习项目
- 概述
- 一、创建一个简单的springcloud项目
- 1_1、创建数据库和一个maven项目
- 1_2、创建springcloud-api模块
- 1_3 创建一个微服务提供者(springcloud-provider-dept-8001):
- 1_4创建服务消费者springcloud-consumer-dept-80
- 1_5创建eureka服务器(springcloud-eureka-7001)(已过时)
- 1_6 通过actuator完善eureka的监控信息
- 1_7搭建eureka集群环境
- 1_8在客户端实现负载均衡
- 1_9 使用feign替代RestTemplate(其内部集成了RestTemplate)
- 1_10分布式系统的延迟和容错处理方式
- 1_11路由网关Zuul(已过时)
- 1_12config
- -----------------------springcloud新技术-----------------------
- 一、Nacos:服务中心+配置中心
- GateWay网关
- 【附加】springcloud原理和思想
概述
1.springcloud是什么,为什么要学springcloud,不学springcloud又怎样呢。
一、创建一个简单的springcloud项目
本程序的结构图如下
1_1、创建数据库和一个maven项目
1_1_1 创建数据库db01,并创建表格dept,数据如下
1_1_2 创建maven项目并删掉src文件夹,然后添加依赖
点击查看dependencyManagement和dependencys的区别
1、关于打包方式:
pom:(父模块)用在父级工程或聚合工程中,用来做jar包的版本控制,必须指明这个聚合工程的打包方式为pom
war:(子模块)将会打包成war,发布在服务器上,如网站或服务。用户可以通过浏览器直接访问,或者是通过发布服务被别的工程调用
2、这里使用了propertieyManagement
来管理依赖的版本:
dependencies
即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)
dependencyManagement
里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。实际上只要在最大的项目内使用dependencyManagement之后(可以不在子模块中写同样的代码重复控制版本),那么子子模块、子子子模块都会默认使用dependencyManagement规定的版本。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
<junit.version>4.12</junit.version>
<lombok.version>1.16.18</lombok.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<!-- 1.:packaging使用pom的打包方式-->
<packaging>pom</packaging>
<!-- **************2.依赖管理:仅管理依赖,但不导入依赖***********-->
<dependencyManagement>
<dependencies>
<!-- springcloud的依赖(与其他依赖相比是pom类型)-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- springBoot 启动器(内涵Mybatis)-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- **********以下是与日志测试相关的********-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--配置打包插件build-->
<!-- <build>-->
<!-- <resources></resources> -->
<!-- </build>-->
1_2、创建springcloud-api模块
1_2_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_01</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 当前module中自己需要的依赖,如果父依赖中已经配制了版本,这里就不用写了-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
1_2_2 创建实体类pojo
@Data
@NoArgsConstructor
//使用链式写法(该类中的所有setter和getter方法的返回值都是this哦)
//@Accessors(prefix = "xxx")是忽略以xxx开头的属性,其余属性使用链式写法
@Accessors(chain = true)
public class Dept implements Serializable {//Dept实体列,orm对象关系映射
private Long deptno;//主键
private String dname;
//这个数据是存在哪个数据库的字段~微服务,一个服务对应一个数据库,同一个信息可能存在不同的数据库
private String db_source;
public Dept(String name){
this.dname=dname;
}
}
1_3 创建一个微服务提供者(springcloud-provider-dept-8001):
1_3_1导入依赖
热部署工具非常好用,是指我们在程序中修改java代码之后能够生效。
使用方法很简单:1、添加依赖 2、修改服务器设置
参考文章
<dependencies>
<!-- 1、添加Pojo实体类-->
<dependency>
<groupId>org.junjun</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 2、springboot和测试类-->
<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>
<scope>com.junjun.test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>com.junjun.test</scope>
</dependency>
<!-- 3、数据库相关-->
<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>
<!-- 4、热部署工具:就是我们改动java代码后不用手动重启项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_3_2 配置application.yml
server:
port: 8001
# mybatis的配置
mybatis:
type-aliases-package: com.junjun.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring服务器和数据库的配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
username: root
password: 3333
1_3_3 配置dao层,service层,controller层
1_3_3_1 dao/DeptDao:实体类的的增删改查方法的实现
使用@Mapper就能被对应的xml文档用namespace=""给找到(而且有提示)。
@Mapper
@Repository
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
—DeptDao在sources中的对应的mapper.xml
虽然会爆红报错(xml中找不到Dept),但是可以正常运行
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间是对应的接口-->
<mapper namespace="com.junjun.springcloud.dao.DeptDao">
<insert id="addDept" parameterType="Dept">
insert into dept(dname,db_source)
value (#{dname},DATABASE())
</insert>
<!-- 只有一个参数,因此#{}中可以随便写-->
<select id="queryById" resultType="Dept" parameterType="Long">
select * from dept where deptno=#{deptno};
</select>
<select id="queryAll" resultType="Dept">
select * from dept;
</select>
</mapper>
1_3_3_2 service层
--------service/DeptService
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
--------service/DeptServiceImpl
@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() ;
}
}
1_3_3_4 controller层
-------controller/DeptController
//提供Restful服务!
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
// 带占位符的url是springMVC的重要功能,请求中的{xxx}将会被传送到@PathVariable("xxx")中去
@GetMapping("/dept/get/{id}")
public Dept getDept(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List<Dept> getAll(){
return deptService.queryAll();
}
}
1_3_4配置开启springboot服务器的类
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
1_3_5对controller进行测试
1_4创建服务消费者springcloud-consumer-dept-80
1_4_1 导入依赖
<dependencies>
<!-- 1、添加Pojo实体类-->
<dependency>
<groupId>org.junjun</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 2、springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1_4_2 在applicaction.yml中修改端口号
server:
port: 80
1_4_3 将RestTemplate注册到bean中
相当于在xml中用< bean/>注册,这种方式常用于框架中已经存在的类,自己定义的类通常会使用@Component在定义时就一步注册
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
1_4_4 配置controller层(通过restTemplate调用服务器的url)
1.消费者不应该有service层,而是调用服务提供者的service层,我们可以通过RestTemplate来申请调用服务提供者的url。
2.RestTemplate是Spring提供的一个访问Http服务的客户端类。服务消费者通过restTamplate给服务提供者发送HTTP请求并接受响应。。简单来说就是restTamplate可以用来调用请求并得到该服务器运行后的返回结果。
@RestController
public class DeptConsumerController {
@Autowire
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX="http://localhost:8001";
//getForObject的三个参数是:url,返回值类型,占位符传参
@RequestMapping("/consumer/dept/add")
public boolean add(@RequestBody Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
//实现消费者和访问者完全解耦(用户访问需要经过一次转发)
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list", List.class);
}
}
1_4_5 配置开启springboot服务器的类的类名
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class);
}
}
1_4_5测试
1_5创建eureka服务器(springcloud-eureka-7001)(已过时)
eureka会提供服务注册和发现
1_5_1导入依赖
只需要eureka服务器,这样就能开启服务器了。当然,eureka-server只是具有服务器最基本的功能(@SpringBootApplication),但是却没有springboot的各种注解,如@Component,@Autowired等。
<dependencies>
<!-- eureka服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
1_5_2application.yml配置
- eureka: client: register-with-eureka
表示是否想eureka注册中心注册自己,默认是true。
1,如果true表示要注册的话得在defaultZone提供一个注册中心地址,说明在这个注册中心注册
2.如果false表示不注册。一般eureka服务器不会在注册中心注册,只有服务提供者和消费者才要注册。- eureka: client: fetch-registry
表示是否从Eureka Server获取注册中心的服务信息,默认值是true
1.eureka服务器没有必要获取注册中心的注册信息,所以用false
2.eureka客户才需要获取注册信息来决定给哪一个服务器发送信息eureka: client: service-url: defaultZone:
的作用?????:
service-url: defaultZone:是默认服务注册中心地址,默认是http://localhost:端口/eureka ;多个地址可使用 , 分隔。在eureka服务器和客户开启时都向这个地址进行注册,实际上,eureka服务器集群原理就是注册中心相互注册,但是不开启检索服务的能力(fetch-registry: false)。
如果服务器或客户开启了register-with-eureka但是填写的defaultZone是无效地址 >就会报错Eureka was unable to refresh its cache! status = Cannot execute request on any known server
如果服务器不开启register-with-eureka但是填写的defaultZone是非http:地址(不合语法)>就会无法显示查询注册者的信息。如下
server:
port: 7001
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,默认值都是true
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# http://${eureka.instance.hostname}:${server.port}/eureka/就是http://localhost:7001/eureka/,使用了赋值操作,实际上$(eureka.instance.hostname)就是localhost,${server.port}就是7001,这都在上面定义过了。
1_5_3 配置springboot启动类
@SpringBootApplication
@EnableEurekaServer//EnableEurekaServer是开启服务器的启动类,他能够接受别人注册进来~
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class);
}
}
1_5_4 测试
即使能显示出如下界面也不代表没有配置错误,application.yml仍然有可能错误通过http://localhost:7001/来访问测试Eureka服务器是否搭建是否成功。
1_5_5 将8001和80配置为eureka服务器的的Client
1_5_5_1在原有依赖上加上eureka依赖
spring-cloud-starter-eureka已经可以作为服务器开启了,但是没有springboot的核心功能。
<dependencies>
<!-- 1、添加Pojo实体类-->
<dependency>
<groupId>org.junjun</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 2、springboot,都可以不用了,可以用eureka启动服务器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 3、数据库相关-->
<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>
<!-- 4、热部署工具:就是我们改动java代码后不用手动重启项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- 5、eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
1_5_5_2修改application.yml
server:
port: 8001
# mybatis的配置
mybatis:
type-aliases-package: com.junjun.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
username: root
password: 3333
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
1_5_5_3该启动类添加@EnableEurekaClient注解
@SpringBootApplication
@EnableEurekaClient//在程序启动之后会自动注册到Eureka服务端中!通过http://localhost:7001/能进入Eureka界面能看见注册的Eureka
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
1_5_5_4测试
先启动7001,进入http://localhost:7001/,再启动8001,然后刷新http://localhost:7001/
1.若没有出现也由是springcloud-eureka-7001的application.yml出现了问题。
1_6 通过actuator完善eureka的监控信息
1_6_1 导入actuator依赖
<dependencies>
<!-- 1、添加Pojo实体类-->
<dependency>
<groupId>org.junjun</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 2、springboot,都可以不用了,可以用eureka启动服务器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 3、数据库相关-->
<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>
<!-- 4、热部署工具:就是我们改动java代码后不用手动重启项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- 5、eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 6、actuator完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
1_6_2 在eureka界面查看客户详细信息:在application.yml中配置info
server:
port: 8001
# mybatis的配置
mybatis:
type-aliases-package: com.junjun.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #本应用在Eureka中的Application名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
username: root
password: 3333
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改eureka中的默认描述信息
#info配置:点击Status:UP就会得到eureka客户详细信息:{"app":{"name":"junjun-springcloud"},"company":{"name":"blog.junjun.com"}}
info:
app.name: junjun-springcloud
company.name: blog.junjun.com
再先后开启7001和8001,然后进入http://localhost:7001/网页。
1_6_2通过controller(即http请求)查看客户详细信息:使用discovery
1_6_2_1配置访问信息的controller控制器
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@Autowired//用来获取一些配置的信息,得到具体的微服务
private DiscoveryClient client;//import org.springframework.cloud.client.discovery.DiscoveryClient;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
// 带占位符的url是springMVC的重要功能,请求中的{xxx}将会被传送到@PathVariable("xxx")中去
@GetMapping("/dept/get/{id}")
public Dept getDept(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List<Dept> getAll(){
return deptService.queryAll();
}
//注册进来的微服务~获取一些消息
@GetMapping("/dept/discovery")
public Object discovery(){//获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("discovery=>services"+services);
//通过eureka中的application(即"SPRINGCLOUD-PROVIDER-DEPT")来得到一个具体的微服务的信息
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for(ServiceInstance instance:instances){
System.out.println(
instance.getHost()+"\t"+
instance.getPort()+"\t"+
instance.getUri()+"\t"+
instance.getServiceId()
);
}
return this.client;
}
}
1_6_2_2在启动类中添加@EnableDiscoveryClient注解
@SpringBootApplication
@EnableEurekaClient//在程序启动之后会自动注册到Eureka服务端中!通过http://localhost:7001/能进入Eureka界面能看见注册的Eureka
@EnableDiscoveryClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
1_6_2_3进入http://localhost:8001/dept/discovery查看效果
实际上我们完全可以通过status链接跳转到discorery界面或者是swagger界面等其他链接,只需要修改
eureka.instance.status-page-url
地址就行。
server:
port: 8001
#springboot和数据库相关配置
spring:
application:
name: server-provider-8001 #本应用在Eureka中的Application名称
datasource:
username: root
password: 3333
url: jdbc:mysql://localhost:3306/db01?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:/mapper/DeptMapper.xml
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
#Eureka Client注册到Eureka Server要用上这个地址
instance:
instance-id: provider_dept8001 #修改eureka中status的up的默认描述信息
status-page-url: http://localhost:8001/dept/discovery #默认地址:http://desktop-8aqan37:8001/actuator/info
##info配置:点击UP就会得到信息:{"app":{"name":"junjun-springcloud"},"company":{"name":"blog.junjun.com"}}
#info:
# app.name: junjun-springcloud
# company.name: blog.junjun.com
1_7搭建eureka集群环境
1_7_1复制eureka7001,生成7002,7003
1_7_2修改各个eureka服务器的主机名hostname,使得主机名唯一
1.把hostname和defaultZone修改一下,每个和其他所有eureka服务器相互注册。
2.要想搭配成集群环境,必须满足以下两个条件
(1)每台eureka服务器主机名唯一eureka.instance.hostname:
(2)每两个服务器service-url.defaultZone相互关联
实际上,eureka服务器集群原理就是注册中心相互注册,但是不开启检索服务的能力(fetch-registry: false)。
如果注册中心a的defaultZone为空或指向自己(不向b注册),注册中心b向a进行注册,那么注册信息是单向传递的(b会共享给a,a不会共享给b)参考文章。
3.测试结果:集群中所有服务器共享注册信息。即在一台eureka服务器上注册,另外两条也能显示注册信息。
--------7001的application.yml
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://localhost:7002/eureka/,http://localhost:7003/eureka/
--------7002的application.yml
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://localhost:7001/eureka/,http://localhost:7003/eureka/
--------7003的application.yml
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
1_7_3模拟多台独立服务器(域名)组成集群的效果:修改hosts中的域名映射关系
其实只要每个eureka服务器主机名不一样就能搭配成集群环境了,但是因为所有eureka服务器都部署在本地显得DS Replicas显示域名都是localhost。为了把eureka服务器做出区分,我们在hosts中修改域名映射关系来模拟在多台独立服务器(域名)组成集群的效果。要想让这两个url生效得在
C:\Windows\System32\drivers\etc\hosts
文档中添加域名映射关系
1.修改hosts域名关系映射表
2.修改defaultZone:注册中心地址。
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #eureka7002.com等这三个会在hosts文档中添加这种映射关系
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己,1,如果要注册的话得在defaultZone提供注册中心地址
fetch-registry: false #表示是否从Eureka Server获取注册中心的服务信息,eureka服务端没有必要获取注册中心的注册信息,默认值都是true
service-url:
# 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7001.com:7001/eureka/
#这三个会在hosts文档中添加这种映射关系
1_7_4 测试:使用服务器提供者8001来进行测试
1、先开启7001,7002,7003,http://localhost:7001/
再开启8001,注意8001的service-url:defaultZone: http://localhost:7001/eureka/
即仅在一台eureka服务器上注册,但是在eureka集群环境eureka服务器会进行注册信息同步.因此刷新http://localhost:7001/
、http://localhost:7002/
、http://localhost:7003/
后都能找到8001,那集群环境就算配置成功!
1_7_5将服务提供者向所以eureka服务器连通
2、然后我们会将服务提供者进行配置:
defaultZone:http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
,使得服务提供者可以在任意一台eureka服务器上进行注册。
server:
port: 8001
# mybatis的配置
mybatis:
type-aliases-package: com.junjun.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring和数据库的配置
spring:
application:
name: springcloud-provider-dept #本应用在Eureka中的名称(会变成大写)
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
username: root
password: 3333
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #本机的7001,7002,7003端口可以访问Eureka
instance:
instance-id: springcloud-provider-dept8001,springcloud-provider-dept8002,springcloud-provider-dept8003 #修改eureka中的默认描述信息
#info配置:点击UP就会得到信息:{"app":{"name":"junjun-springcloud"},"company":{"name":"blog.junjun.com"}}
info:
app.name: junjun-springcloud
company.name: blog.junjun.com
1_7_5配置服务端集群
(1)复制数据库生成完全一样的db02和db03,并修改datasource属性为当前数据库。
CREATE TABLE db02.dept LIKE db01.dept;
INSERT INTO db02.dept SELECT * FROM db01.dept;
CREATE TABLE db03.dept LIKE db01.dept;
INSERT INTO db03.dept SELECT * FROM db01.dept;
(2)创建多台服务提供者:复制springcloud-provider-dept-8001模块生成8002和8003
1.记得要修改每个服务提供者端口号
2.修改每个服务提供者所使用的数据库spring.datasource.url
3.修改每个服务提供者Eureka的配置,
修改defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/,使得服务提供者能够在任意一台eureka服务器上进行注册
修改instance-id,使得每台服务提供者的id都不同。
4.修改每个服务提供者自定义启动类的名称
【注意】
要保证三个服务器的spring.application.name是一样的哦,只有三台服务器的spring.application.name是一样的才能形成服务器集群,不然就不是同一个应用的服务器了。
1_8在客户端实现负载均衡
负载均衡的实现方式有两种,分别是
服务端负载均衡(nginx)
和客户端负载均衡(ribbon):
服务端负载均衡
:当浏览器向后台发出请求的时候,会首先向反向代理服务器发送请求,反向代理服务器会根据客户端部署的ip:port映射表以及负载均衡策略,来决定向哪台服务器发送请求,一般会使用到nginx反向代理技术。
客户端负载均衡
:当浏览器向后台发出请求的时候,客户端会向服务注册器(例如:Eureka Server),拉取注册到服务器的可用服务信息,然后根据负载均衡策略,直接命中哪台服务器发送请求。这整个过程都是在客户端完成的,并不需要一个独立的Ribbon服务器(反向代理服务器)的参与。
1_8_1 使用ribbon自带负载均衡算法:@LoadBalanced
在RestTemplate上使用@LoadBalanced,就能使得RestTemplate具有Ribbon的负载均衡的效果
-----------------------config/configBean
@Configuration
public class ConfigBean {
//RestTemplate是一套接口,服务消费者都会通过RestTemplate来调用服务提供者的方法。
@Bean
@LoadBalanced//配置负载均衡,实现Ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
测试是否成功配置负载均衡
http://localhost/consumer/dept/list
【注意】我们不需要额外添加ribbon依赖,是因为spring-cloud-starter-eureka依赖带有spring-cloud-starter-netflix-eureka-client依赖,spring-cloud-starter-netflix-eureka-client自带了spring-cloud-starter-netflix-ribbon依赖。
1_8_2 使用ribbon实现自定义负载均衡算法
(1)自己编写一个Rule类继承AbstractLoadBalancerRule,并添加choose(ILoadBalancer lb, Object key) 方法。
---------------------com.myrule.junRandomRule.java(注意:没有和启动类在同一级目录)
package com.junjun.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;
@Component
public class junRandomRule extends AbstractLoadBalancerRule {
private int total=0;
private int currentIndex=0;
public junRandomRule() {
}
//@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();//serverCount是所有服务的数量
if (serverCount == 0) {
return null;
}
// int index = this.chooseRandomInt(serverCount);//随机获取一个序号
// server = (Server)upList.get(index);//从活着的服务中获取对应序号的服务
//
//*******************这里是我改写的算法**************************************
if(total<5){
server = upList.get(currentIndex);
total++;
}else{
total=0;
currentIndex++;
if(currentIndex>=upList.size()){
currentIndex=0;
}
server=upList.get(currentIndex);
}
//*******************我的部分结束**************************************
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) {
}
}
(2)将该策略添加到项目中(在启动类上使用@RibbonClient)
如果把rule类放在和DeptConsumer_80同一目录下,那么该负载均衡算法是能够被本服务器扫描到的,因此@RibbonClient都不用写了(当然,@Component注册bean还是要写的)。
@SpringBootApplication
@EnableEurekaClient
//1、当客户端存在自定义Ribbon和原有Ribbon时,自定义会覆盖原有Ribbon
// 2、MyRule不应该在@ComponentScan中(所以不应该在application同级目录下),否则会被所有RibbonClients共享,所有RibbonClients都会采取这个策略
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = junRandomRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class);
}
}
(3)测试
本负载均衡的效果是每个服务器都能5次,5次后就换一个服务器
1_8_3 使用nginx实现负载均衡思路图
1_8_4 使用openFeign实现负载均衡
1_9 使用feign替代RestTemplate(其内部集成了RestTemplate)
1.feign不是做负载均衡的,feign只是集成了ribbon(负载均衡还是feign内置的ribbon做)。feign的作用的替代RestTemplate,这会导致性能降低,但是可以使代码可读性很强。
2.狂神的视频是把service层发在springcloud-api中,我觉得不是很懂,就自己放在了消费者里面了,并精简了config文件夹,不知道我这样做有没有什么问题,后期了解了原理再探讨这个问题吧。
3.若把service放在springcloud-api中,需要我们把依赖放在springcloud-api中,而springcloud-consumer-dept-feign
就不需要添加这个依赖了。
1_9_1 复制然后创建一个新的服务消费者model用来测试feign
复制
springcloud-consumer-dept-80
,创建springcloud-consumer-dept-feign
1_9_2 在原来依赖的基础上添加feign依赖
<dependencies>
<!-- 添加feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</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>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_9_3 修改启动类名为FeignDeptConsumer_80,并添加注解@EnableFeignClients扫描FeignClient
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.junjun.springcloud"})//这里是扫描springcloud_api模块中的DeptClientService作为Feign的接口类
public class FeignDeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeignDeptConsumer_80.class);
}
}
1_9_4在Fiegn所在的服务消费者中添加service层(使用@FeignClient)
----------------service/DeptClientService
@Component
@FeignClient(value= "SPRINGCLOUD-PROVIDER-DEPT")//这个接口将会被动态代理实现,参考内容是服务提供者("SPRINGCLOUD-PROVIDER-DEPT")中的mapper.xml???
public interface DeptClientService {
@GetMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
}
1_9_5在controller不使用RestTemplate,而是使用DeptClientService来调用服务提供者。
@RestController
public class DeptConsumerController {
@Qualifier("com.junjun.springcloud.service.DeptClientService")//有多个DeptClientService的bean,所以要用@Qualifier
@Autowired
private DeptClientService service;
@RequestMapping("/consumer/dept/add")
public boolean add(@RequestBody Dept dept){
return this.service.addDept(dept);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return this.service.queryById(id);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return this.service.queryAll();
}
}
1_9_6测试
进入
http://localhost/consumer/dept/list
,多次刷新查看负载均衡的策略(默认是轮询)。
1_10分布式系统的延迟和容错处理方式
1.服务熔断:某个服务超时或者异常,会引起服务熔断(在服务端实现)
2.服务降级:从服务端整体考虑,关掉某些服务(在客户端实现)
3.服务雪崩:
Hytrix实现服务熔断(已过时)
1_10_1复制springcloud-provider-dept-8001生成一个新的服务提供者:springcloud-provider-dept-hystrix-8001
1_10_2添加Hystrix依赖到原有依赖中
<dependencies>
<!-- 添加hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- actuator完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Eureka依赖添加-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 我们需要拿到api模块中的实体类,所以需要配置api module-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 2.junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- logback是log4j的日志组件-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<!-- Mybatis依赖:通过Mybatis管理mysql数据库-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- springboot的web项目的必备依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot-test:@SpringBootTest注解的所在的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!-- jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署工具:devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_10_3在application.yml中修改instance-id,方便区分
server:
port: 8001
# mybatis的配置
mybatis:
type-aliases-package: com.junjun.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #本应用在Eureka中的名称(会变成大写)
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezeno=GMT%2B8
username: root
password: 3333
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
instance:
instance-id: springcloud-provider-hystrix-dept8001 #修改eureka中的默认描述信息
#info配置:点击UP就会得到信息:{"app":{"name":"junjun-springcloud"},"company":{"name":"blog.junjun.com"}}
info:
app.name: junjun-springcloud
company.name: blog.junjun.com
1_10_4修改启动类名为:DeptProviderHystrix_8001.java,并添加@EnableCircuitBreaker
@EnableCircuitBreaker是将@HystrixCommand注册到bean中去
@SpringBootApplication
@EnableEurekaClient//在程序启动之后会自动注册到Eureka服务端中!通过http://localhost:7001/能进入Eureka界面能看见注册的Eureka
@EnableDiscoveryClient
@EnableCircuitBreaker//添加对熔断注解的支持(即将@HystrixCommand注册到bean中去)。
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
1_10_5修改controller层(添加处理错误的功能)
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")//失败就会调用hystrixGet方法
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if(dept==null){
throw new RuntimeException("id=>"+id+"没有对应的信息");
}
return dept;
}
//备选方案
public Dept hystrixGet(@PathVariable("id") Long id){
System.out.println("*****************失败了*************************");
return new Dept()
.setDeptno(id)
.setDname("id=>"+id+"没有对应的信息-@Hystrix")
.setDb_source("no this database in MySQL");
}
}
1_10_6测试http://localhost/consumer/dept/get/6
1.注意我们只写了add函数,没写list函数,所以不要用list来测试
2.可能会出现服务器崩坏的现象,这时候一个一个重启服务器就行 。
feign实现服务降级
1_10_7在feign实现负载均衡的基础上添加一个类
package com.junjun.springcloud.service;
import com.junjun.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
//降级
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDname("id=>"+id+"没有对应的信息,客户端提供了降级的信息,这个服务现在已经被关闭了")
.setDb_source("没有数据");
}
@Override
public List<Dept> queryAll() {
return null;
}
};
}
}
1_10_8给servicec层的接口添加服务降级画面显示(fallbackFactory)
提供服务降级提示的服务器是哪个呢??
//通过Feign来指定熔断降级的操作
@Component
@FeignClient(value= "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)//这个接口将会被动态代理实现,参考内容是服务提供者("SPRINGCLOUD-PROVIDER-DEPT")中的mapper.xml???
public interface DeptClientService {
@GetMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
}
1_10_9测试
熔断监控DashBoard监控请求(流量)
1_10_10新建一个模块:springcloud-consumer-hystrix-dashboard
,并往dashboard模块中添加依赖
<dependencies>
<!-- 导入Hystrix和他的监控页面这两个依赖
1.服务端必须要有actuator才能监控得到
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</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依赖添加-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_10_11设置dashboard模块的端口
server:
port: 9001
1_10_12配置启动类
@SpringBootApplication
@EnableHystrixDashboard//开启DashBoard
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class);
}
}
1_10_13在springcloud-provider-dept-hystrix-8001
的启动类上添加servlet
使用
springcloud-provider-dept-8001
就不行,无论是添加依赖还是添加@EnableCircuitBreaker、servlet都不行。
@SpringBootApplication
@EnableEurekaClient//在程序启动之后会自动注册到Eureka服务端中!通过http://localhost:7001/能进入Eureka界面能看见注册的Eureka
@EnableDiscoveryClient
@EnableCircuitBreaker//添加对熔断注解的支持(即将@HystrixCommand注册到bean中去)。
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
@Bean
//增加一个servlet
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
1_10_14测试
进入
http://localhost:9001/hystrix
。如果能实现上面界面,就可以进入dashboard界面了。
1_11路由网关Zuul(已过时)
1_11_1添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 导入Hystrix和他的监控页面这两个依赖
1.服务端必须要有actuator才能监控得到
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</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依赖添加-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_11_2 配置application.yml
server:
port: 9527
spring:
application:
name: springcloud-zuul
#Eureka的配置
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
instance:
instance-id: zuul9527.com #修改eureka中的默认描述信息
prefer-ip-address: true
info:
app.name: junjun-springcloud
company.name: blog.junjunstudy.com
1_11_3修改域名映射关系:C:\Windows\System32\drivers\etc\hosts
1_11_4配置启动类ZuulApplication_9527
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class);
}
}
1_11_5测试
访问
http://www.junjunstudy.com:9527/springcloud-provider-dept/dept/get/2
1_11_5实现域名伪装:修改9527一下application.yml的zuul配置,并测试
server:
port: 9527
spring:
application:
name: springcloud-zuul
#Eureka的配置
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
instance:
instance-id: zuul9527.com #修改eureka中的默认描述信息
prefer-ip-address: true
info:
app.name: junjun-springcloud
company.name: blog.junjunstudy.com
zuul:
#实现域名伪装:用mydept代替springcloud-provider-dept
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
#使得原来的服务器名称不可访问服务器
ignored-services: springcloud-provider-dept
#ignored-services: "*" #忽略所有,只有通过mydept才能访问得到。
用代理名称代替服务提供者的名称
完整版如下:
server:
port: 9527
spring:
application:
name: springcloud-zuul
#Eureka的配置
#Eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
instance:
instance-id: zuul9527.com #修改eureka中的默认描述信息
prefer-ip-address: true
info:
app.name: junjun-springcloud
company.name: blog.junjunstudy.com
zuul:
#实现域名伪装:用mydept代替springcloud-provider-dept
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
#使得原来的服务器名称不可访问服务器
ignored-services: "*" #忽略所有,只有通过mydept才能访问得到。
prefix: /junjun #设置统一的访问前缀`
访问
http://www.junjunstudy.com:9527/junjun/mydept/dept/get/2
1_12config
Git环境配置
1_12_1配置Git项目环境
【码云环境配置】
包括:1.官网下载Git工具 2.更改Git姓名和邮箱 3.在本机上配置公钥
1_12_2在git码云上创建一个项目,并获取到本地。然后新建一个application.yml文档,并添加上以下内容。
spring:
profiles:
active: dev
---
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
1_12_3上传到git远程服务器。
配置config服务端并连接git的远程仓库
1_12_4导入依赖
<dependencies>
<!-- springcloud-config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!-- Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1_12_5配置application.yml
server:
port: 3344
spring:
application:
name: springcloud-config-server
#连接远程仓库
cloud:
config:
server:
git:
uri: https://gitee.com/junjunlllll/spring-config.git #这是https的地址,不是SSH
1_12_6 配置启动类
@SpringBootApplication
@EnableConfigServer //
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class);
}
}
1_12_7测试是否联通
通过git进行远程资源 配置 本地项目。
1_12_8在git项目目录下添加一个yml文档。
在git上部署多套环境,而我们只要修改spring.profiles就能配置git上对应配置(如名字,端口号)。
spring:
profiles:
active: dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: springcoud-provider-dept
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: springcoud-provider-dept
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
1_12_9 push到远程服务器
1_12_10 创建springcloud-config-client-3355模块,并配置依赖
<dependencies>
<!-- springcloud-config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1_12_11 配置配置文件
bootstrap系统级别的配置,而application是用户级别的配置
--------------bootstrap.yml
#bootstrap系统级别的配置,而application是用户级别的配置
spring:
cloud:
config:
profile: test #激活了dev,所以这个client的端口号是8201,如果激活test该模块的端口号就是8202
name: config-client #从git上读取的资源名称(我们在git上有一个config-client.yml),不需要后缀
label: master
uri: http://localhost:3344
------------application.yml
spring:
application:
name: springcloud-config-client-3355
1_12_12启动类和controller层
-----------ConfigClient_3355启动类
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class);
}
}
----------------controller层
@RestController
public class ConfigClientControler {
@Value("${spring.application.name}")
private String application;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig(){
return "application:"+application
+"eurekaServer:"+eurekaServer
+"port:"+port;
}
}
1_12_12访问http://localhost:8201/config
测试
远程配置资源项目(这里以7001服务器为例)
前提是配置服务器3344成功,我们将通过3344获取资源文件并配置资源
1_12_13 配置并上传远程资源
---------代码如下
spring:
profiles:
active: dev
---
server:
port: 7001
spring:
profiles: dev
application:
name: springcloud-config-eureka
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己
fetch-registry: false #false表示自己为注册中心(开集群需要注册自己)
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #eureka7002.com等这三个会在hosts文档中添加这种映射关系
---
server:
port: 7001
spring:
profiles: test
application:
name: springcloud-config-eureka
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否想eureka注册中心注册自己
fetch-registry: false #false表示自己为注册中心(开集群需要注册自己)
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #eureka7002.com等这三个会在hosts文档中添加这种映射关系
1_12_14 创建一个7001模块,并导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!-- 导入Eureka包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
1_12_15创建bootstrap.yml和application.yml(核心)
--------------bootstrapyml
spring:
cloud:
config:
name: config-eureka #这是远程资源的名称
label: master
profile: dev
uri: http://localhost:3344
spring:
application:
name: springcloud-config-eureka-7001
1_12_16 配置启动类
@SpringBootApplication
@EnableEurekaServer//EnableEurekaServer是开启服务器的启动类,他能够接受别人注册进来~
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class);
}
}
1_12_17测试
-----------------------springcloud新技术-----------------------
在上述中很多技术都已经过时不再更新了,所以我们要用到新的技术
一、Nacos:服务中心+配置中心
1、Nacos作为服务中心
Nacos(Naming configuration service)是一个服务发现、配置管理、服务管理平台。所以Nacos=Eureka+Config+Bus。
1.1 下载、启动nacos
**********************1.下载
下载地址
**********************2.启动nacos,并登入
1、在bin中双击startup.cmd就行。
2、然后访问http://localhost:8848/nacos/index.html
3、通过账号密码:nacos/nacos登录。
4、右上角切换中文
1.2 nacos替代eureka作为服务注册中心
在服务的yml中spring☁️nacos:discovery:server-addr:localhost:8848。来在nacos中注册。
(1) 在nacos客户端(如服务提供者、服务消费者)上添加依赖
<dependencies>
<!-- springboot版本为2.3.9.RELEASE-->
<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>
<scope>test</scope>
</dependency>
<!-- nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
</dependencies>
【注意】
springboot版本低于nacos版本会出现异常;
(2) 对eureka客户端进行配置
server:
port: 9001
spring:
application:
name: nacos-test #本应用在nacos的名称
cloud:
nacos:
discovery:
server-addr: localhost:8848
(3)配置启动类
@EnableDiscoveryClient
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
(4)在nacos中发现该客户端成功
这可比Eureka方便多了,不需要自己手动搭建Eureka服务端。
2.1.3 nacos替代Config作为服务配置中心
2.1.4 nacos集群和持久化配置
2、Nacos作为配置中心
GateWay网关
2.1 GateWay+Eureka
网关有两种常用的类型:zool和GateWay。使用一种简单有效的方法来对API进行路由、过滤、熔断、限流、重试。
Springcloud GateWay是基于WebFlux框架实现的,而WebFlux框架底层是使用了高性能的Reactor模式的框架Netty。
2.1.1 GateWay实现路由效果
(1) 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Eureka依赖添加-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
【注意】
不能要spring-boot依赖不然会报错
(2)添加配置文件
------------------------------方法一:在application.yml中进行配置
server:
port: 9527
#我们往往不想暴露我们的服务器ip和端口,因此用GateWay的9527端口来掩饰
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #第一个路由,路由id要保证唯一
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates: #断言,与uri路径进行匹配
- Path=/dept/**
- id: pay #第二个路由,路由id要保证唯一
uri: http://localhost:8001
predicates:
- Path=/aaa/**
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
------------------------------方法二:在config/Gatewaay中使用配置bean的方式进行路由设置
@Configuration
class GatewayConfig {
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_route1",r->r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
//访问地址http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
return routes.build();
}
}
(3)配置启动类
@EnableEurekaClient
@SpringBootApplication
public class GateWay9527 {
public static void main(String[] args) {
SpringApplication.run(GateWay9527.class,args);
}
}
(4)测试:实现路由功能
http://localhost:9527/dept/list
发现即使是使用http://localhost:8001/dept/list也能成功访问。
2.1.2 Gateway配置动态路由,实现负载均衡
1、默认情况下,Gateway会根据注册中心的服务列表,以服务中心(eureka,nacos)上的服务名为路径创建动态路由进行转发,从而实现动态路由功能,实现服务器的负载均衡(默认轮询策略)。
(1)在2.1.1基础上更改配置文件
server:
port: 9527
#我们往往不想暴露我们的服务器ip和端口,因此用GateWay的9527端口来掩饰
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启Gateway从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #第一个路由,路由id要保证唯一
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://springcloud-provider-dept #1、lb是指loaderBalance的意思 2、springcloud-provider-dept是服务提供者集群的spring:application:name
predicates: #断言,与uri路径进行匹配
- Path=/dept/**
- id: pay #第二个路由,路由id要保证唯一
# uri: http://localhost:8001
uri: lb://springcloud-provider-dept #1、lb是指loaderBalance的意思 2、springcloud-provider-dept是服务提供者集群的spring:application:name
predicates:
- Path=/aaa/**
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
(2) 测试负载均衡效果
2.1.2 Gateway如何自定义负载均衡策略呢
2.1.3 Gateway的Predicate
Spring Cloud Gateway有很多内置工厂Route Predicate Factory
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启Gateway从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #第一个路由,路由id要保证唯一
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://springcloud-provider-dept #1、lb是指loaderBalance的意思 2、springcloud-provider-dept是服务提供者集群的spring:application:name
predicates: #断言,满足以下条件才会成功通过gateway把请求发送给服务器。
- Path=/dept/** #地址匹配要正确
- After=2020-10-12T16:51:37.485+08:00[Asia/Shanghai] #时间满足在2020-10-12T16:51:37之后
- Cookie=username,zzyy #Cookie里面有个键值对,第一个参数是key:username,第二个参数是value:zzyy
- Header=X-Request-Id,\d+ #对请求头进行匹配,value:\d+是为整数的正则表达式
- id: pay
# uri: http://localhost:8001
uri: lb://springcloud-provider-dept
predicates:
- Path=/aaa/**
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ #本机的7001,7002,7003端口可以访问Eureka
测试效果
2.1.4 GateWay的filter
Gateway自带的filter
使用自定义filter(全局GlobalFilter)
************filter/MyLoGateWayFilter
@Component
@Slf4j//这是lombok中的依赖. import lombok.extern.slf4j.Slf4j;
public class MyLoGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("*************************com in MyLogGateWayFilter:"+new Date());
String username = exchange.getRequest().getQueryParams().getFirst("username");
if(null==username){
log.info("***************不合法,用户名为null");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {//日志顺序
return 0;
}
}
测试
2.2 Nacos+Eureka
2.2.1
【附加】springcloud原理和思想
CAP理论
C:
Consistency
一致性:读写(在同一个集群中的不同服务器上)内容保存一致。不存在在server1写完后,在server2读取的却是旧数据>A:Availability
可用性:只要我对服务器,发送请求,服务器必须对我进行相应,保证服务器一直是可用的。
P:Partition tolerance
,分区容错::分布式的服务器之间的数据不能同步。
为什么CAP只能达到 CP 或者 AP?
不懂!eureka是基于ap的。zookeeper是基于cp的。所有,eureka各个节点完全平等,当有些节点坏掉时,剩余节点还是可以提供注册和查询服务,因此eureka一定能得到数据,但是不一定是最新的;而zookeeper得到的数据一定是最新的,但是不一定能成功得到数据:当某些节点坏掉时,我们就不能访问了。
ribbon:客户端负载均衡的工具
ribbon是为客户端提供简单的负载均衡(LB,LoadBalancer)的算法。常见的负载均衡算法:随机 轮询 最少响应比。
常见的负载均衡软件:Nginx
,Lvs
,Apache+Tomcat
负载均衡:为请求分配集群中的服务器
负载均衡的类别:1、集中式 (如Nginx反向代理服务器)2、进程式(将LB集成到消费方如上面的7001)