仅为学习记录,方便回顾复习,如有侵权请联系删除!
Spring Cloud
目录
文章目录
1、常见面试题
1.1 什么是微服务?
1.2 微服务之间如何独立通讯?
1.3 SpringCloud和Dubbo有哪些区别?
1.4 SpringBoot和SpringCloud,谈谈对它们的理解。
1.5 什么是服务熔断?什么是服务降级?
1.6 微服务的优缺点分别是什么?说一下在项目上遇到的坑?
1.7 你所知道的微服务的技术栈有哪些?列举
1.8 eureka和zookeeper都可以提供服务注册与发现的功能,说说这两个的区别?
2、微服务概述
2.1 什么是微服务
2.2 微服务与微服务架构
微服务
强调的是服务的大小,关注的是某一个点,具体解决某一个问题/提供一个落地的服务实现,可以狭义地理解为IDEA中的一个个微服务工程,或者Module
- IDEA 工具里使用Maven开发的一个个独立的小Module,具体是用springboot开发的一个小模块,专业的事交给专业的模块来做,一个模块就做一件事情。
- 强调的是一个个的个体,每个个体完成一个具体的任务或者功能。
微服务架构
2.3 微服务的优缺点
优点
缺点
2.4 微服务技术栈有哪些
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot、Spring、SpringMVC |
服务配置与管理 | Netflix公司的Achaius、阿里的Diamond等 |
服务注册于发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpenStack、Kubernetes等 |
数据流操作开发包 | SpringCloud Stream(封装与Redis, Rabbit, Kafka等发送接收消息) |
事件消息总栈 | SpringCloud Bus |
2.5 为什么要选SpringCloud作为微服务架构
1、选型依据**
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
2、当前各大IT公司用的微服务架构有哪些?
- 阿里:dubbo+HFS
- 京东:JSF
- 新浪:Motan
- 当当网 DubboX
- …
3、各服务框架对比
3、SpringCloud入门概述
3.1 SpringCloud是什么?
3.2 SpringCloud和SpringBoot关系
3.3 Dubbo和SpringCloud技术选型
1、分布式+服务治理Dubbo
目前成熟的互联网结构:应用服务化拆分 + 消息中间件
2、Dubbo 和 SpringCloud 对比
结果:
Dubbo | Spring | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总栈 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
3.4 SpringCloud能干什么
3.5 SpringCloud下载
官网:http://projects.spring.io/spring-cloud/
版本号解释
- Spring Cloud 是一个由众多独立子项目组成的大型综合项目,每个子项目有不同的发行节奏,都维护着自己的发布版本号。Spring Cloud 通过一个资源清单BOM (Bill of Materials) 来管理每个版本的子项目清单。为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。
- 这些版本名称的命名方式采用了伦敦地铁站的名称,同时根据字母的顺序来对应版本时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Finchley版本。
参考书:
- https://springcloud.cc/spring-cloud-netflix.html
- 中文API文档:https://springcloud.cc/spring-cloud-dalston.html
- SpringCloud中国社区 http://springcloud.cn/
- SpringCloud中文网 https://springcloud.cc
4、Rest 学习环境搭建 : 服务提供者
4.1 介绍
4.2 SpringCloud版本选择
4.3 创建父工程 springcloud
新建普通maven工程,不需要根据模板建立。
springcloud的就是整个父工程,src文件夹可以删掉。
下面编辑springcloud工程的pom.xml文件
\springcloud\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chw</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式 pom-->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<junit.version>4.13.2</junit.version>
<lombok.version>1.18.12</lombok.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<logback-core.version>1.2.3</logback-core.version>
<logback-classic.version>1.2.3</logback-classic.version>
</properties>
<dependencyManagement>
<dependencies>
<!--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.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--SpringBoot启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version><!--一般留空后续再写-->
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--logback日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback-core.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
创建数据库及表格
创建表格插入数据
创建子模块:springcloud-api
下面new一个普通module命名为springcloud-api,它的父工程是springcloud。
编辑pom.xml文件
\springcloud\springcloud-api\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.chw</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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
创建实体类
注意:网络通信要求所有的实体类必须实现序列化。
com/chw/springcloud/pojo/Dept.java
package com.chw.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //开启链式写法
public class Dept implements Serializable { //Dept 实体类,orm对象关系映射,类表关系映射
private Long deptno;//主键
private String dname;
//这个数据属于哪个数据库。一个服务对应一个数据库,同一个信息可能存在不同的数据库
private String db_source;
//构造器只需要一个参数,因为主键自增,db_source是通过函数得到的
public Dept(String dname) {
this.dname = dname;
}
/*
链式写法:
Dept dept = new Dept();
dept.setDeptno(11).setDname("xiaoming").setDb_source("001")
*/
}
到这里子模块springcloud-api就写完了,这个模块拆分出来的目的就是只管pojo。
创建子模块 : 服务提供者
模块名称:springcloud-provider-dept-8001
把端口号加上,以后项目越来越多时方便管理。
1、编写pom.xml文件
引入依赖
\springcloud\springcloud-provider-dept-8001\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.chw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-8001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--需要拿到实体类,所以要配置api模块,引入刚刚写的模块-->
<dependency>
<groupId>com.chw</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!--mybaits springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!--web相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--tomcat,上面的jetty可以换成tomcat-->
<!--<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>
</project>
2、写配置文件 *.yml 、*.xml
依赖写完下一步写配置
resources/application.yml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: 9527
把mybatis配置补齐
resources/mybatis/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
3、写接口及映射文件(DAO层)
com/chw/springcloud/mapper/DeptMapper.java
package com.chw.springcloud.mapper;
import com.chw.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptMapper {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
resources/mybatis/mapper/DeptMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chw.springcloud.mapper.DeptMapper">
<!--public boolean addDept(Dept dept);-->
<insert id="addDept" parameterType="Dept">
insert into dept (dname, db_source) values (#{dname},DATABASE())
</insert>
<!--public Dept queryById(Long id);-->
<select id="queryById" resultType="Dept" parameterType="Long">
select * from dept where deptno = #{id}
</select>
<!--public List<Dept> queryAll();-->
<select id="queryAll" resultType="Dept">
select * from dept
</select>
</mapper>
4、写service层
com/chw/springcloud/service/DeptService.java
package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
com/chw/springcloud/service/DeptServiceImpl.java
package com.chw.springcloud.service;
import com.chw.springcloud.mapper.DeptMapper;
import com.chw.springcloud.pojo.Dept;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Resource
private DeptMapper deptMapper;
@Override
public boolean addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptMapper.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptMapper.queryAll();
}
}
5、写controller层 (提供RESTful服务)
com/chw/springcloud/controller/DeptController.java
package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
//提供RESTful服务
@RestController
public class DeptController {
@Resource
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(@RequestBody Dept dept) {
return deptService.addDept(dept);
} //如果前端传来的是json值,那么就要通过@RequestBody来将请求体转换为对象
//由于后面是用消费端转发请求过来调用,转来的请求体是json格式,所以这里加了@RequestBody
//如果单独启动服务模块单独调用这个控制方法,则不能加@RequestBody
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List<Dept> queryAll() {
return deptService.queryAll();
}
}
6、写主启动类
com/chw/springcloud/DeptProvider_8001.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//启动类
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
启动主程序,用postman测试。
创建子模块:服务消费者
模块名称:springcloud-consumer-dept-80
消费方一般放在80端口,只要用了80端口,写请求的时候就不用带上端口号。
1、写pom.xml文件(导包)
消费者不需要连数据库,只需要实体类和web
springcloud\springcloud-consumer-dept-80\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.chw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--实体类——web-->
<dependency>
<groupId>com.chw</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>
</project>
2、写配置文件 *.yml
resources/application.yml
server:
port: 80
3、写消费者相关类
配置类
把 RestTemplate 注册成bean让spring管理
com/chw/springcloud/config/ConfigBean.java
package com.chw.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
//@Configuration --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate() { return new RestTemplate(); }
}
Controller层
com/chw/springcloud/controller/DeptConsumerController.java
package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@Controller
public class DeptConsumerController {
// 理解:消费者模块是不应该有service层的,怎么拿到service层呢?怎么取到url
// springboot支持RESTful风格。RestTemplate ... 可供我们直接调用,需要把它注册到spring中。
// RestTempate的重要参数 ( url , 实体 Map保存 , Class<T> responseType返回类型 )
@Autowired
private RestTemplate restTemplate; //提供多种便捷访问远程http服务的方法,简单的RESTful服务模板
//远程服务的地址一般是固定的,所以可以写一个常量
private static final String REST_URL_PREFIX = "http://localhost:8001";
//调用远程的服务 http://localhost:8001/dept/get/{id}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
//服务端的请求方式是get,约束了这里的restTemplate.getForObject。请求方式是post,则restTemplate.postForObject
return restTemplate.getForObject(REST_URL_PREFIX+"dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
下面写主启动类测试
com/chw/springcloud/DeptConsumer_80.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
先启动提供方的启动类(启动完先自测一下本地能不能访问到服务)
再启动消费方的启动类(http://localhost/consumer/dept/list)
5、Eureka服务注册与发现
5.1 什么是Eureka
5.2 原理讲解
下面开始写Eureka的注册中心
创建子模块-Eureka注册中心
命名为springcloud-eureka-7001
第一步:导入依赖
springcloud\springcloud-eureka-7001\pom.xml
<!--导入依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
第二步:配置文件
resources/application.yml
#端口号
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
第三步:写主启动类,添加注解启动服务
com/chw/springcloud/EurekaServer_7001.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //用于标注服务端的启动类,使可以接收别人注册
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class, args);
}
}
启动后访问http://localhost:7001/就可以进入监控页面
(注意http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/,是用于注册时候用的)
如何把提供者注册到Eureka里
第一步:导入依赖
在springcloud-provider-dept-8001这个module里的pom.xml引入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.7.RELEASE</version>
</dependency>
第二步:配置文件
下一步把服务注册进去,要在yml中配置Eureka
springcloud-provider-dept-8001/src/main/resources/application.yml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: 9527
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
第三步:写主启动类,添加注解启动服务
springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/DeptProvider_8001.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//启动类
@SpringBootApplication
@EnableEurekaClient //添加这个注解之后就会自动把提供者注册到Eureka中了
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
下面进行测试
先启动7001,再启动8001
浏览器访问http://localhost:7001/就可以进入监控页面。
可以看到监控页面中已经出现了提供者的yml里设置的项目名称。
修改Status的标题内容
下面要自定义一下这个Status
springcloud-provider-dept-8001/src/main/resources/application.yml
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改Eureka上的默认Status信息
修改完之后重新运行项目访问监控中心查看
补充服务提供者的监控信息
下面要去补充这个页面,配置关于服务加载的监控信息
第一步:导入依赖
springcloud-provider-dept-8001/pom.xml
<!--完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二步:配置文件
springcloud-provider-dept-8001/src/main/resources/application.yml
#info配置
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
启动项目测试,访问监控页面 localhost:7001
服务不可用时 Eureka的自我保护机制
提供者的控制器方法——获取微服务列表的清单
主要应用于团队协作开发中。
控制器方法
springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/controller/DeptController.java
.
.
.
import org.springframework.cloud.client.discovery.DiscoveryClient;
.
.
.
//这个类可以获取一些配置的信息,得到具体的微服务
@Autowired
private DiscoveryClient client;
//从注册进来的微服务里获取一些消息
@GetMapping("/dept/discovery")
public Object discovery() {
//获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("services = " + services);//观察控制台输出
//得到一个具体的微服务信息,通过具体的微服务id,即spring.application.name
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;
}
启动类添加注解
springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/DeptProvider_8001.java
package com.chw.springcloud;
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;
//启动类
@SpringBootApplication
@EnableEurekaClient //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient //服务发现
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
重新启动项目,访问http://localhost:8001/dept/discovery查看结果
5.? 集群环境配置(多个注册中心)
创建多个注册中心
创建两个子模块
springcloud-eureka-7002
springcloud-eureka-7003
第一步:导入依赖
把springcloud-eureka-7001的pom.xml中的依赖复制给它们
<!--导入依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
第二步:配置文件
分别配置springcloud-eureka-7002、springcloud-eureka-7003的yml配置文件
springcloud-eureka-7002/src/main/resources/application.yml
server:
port: 7002
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
springcloud-eureka-7003/src/main/resources/application.yml
server:
port: 7003
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
第三步:写主启动类,添加注解启动服务
分别创建springcloud-eureka-7002、springcloud-eureka-7003的主启动类
springcloud-eureka-7002/src/main/java/com/chw/springcloud/EurekaServer_7002.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7002.class, args);
}
}
springcloud-eureka-7003/src/main/java/com/chw/springcloud/EurekaServer_7003.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7003 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7003.class, args);
}
}
多个注册中心互相绑定形成集群
为了演示效果,先把C:\Windows\System32\drivers\etc中的hosts文件中添加三个默认地址映射
hosts
127.0.0.1 localhost
#2022-7-24 edit
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
配置文件中defaultZone互相关联
springcloud-eureka-7001/src/main/resources/application.yml
#端口号
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册自己,服务器就不用把自己注册进去了。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
# 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 如果是集群(关联):
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
springcloud-eureka-7002/src/main/resources/application.yml
server:
port: 7002
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
# 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 如果是集群(关联):
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/
springcloud-eureka-7003/src/main/resources/application.yml
server:
port: 7003
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
service-url: #监控页面
# 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 如果是集群(关联):
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/
启动springcloud-eureka-7001、springcloud-eureka-7002、springcloud-eureka-7003的主启动类测试,
分别访问localhost:7001、localhost:7002、localhost:7003。
这里我的浏览器页面并没有显示互相关联的效果
下面将服务提供者注册到三个注册中心
springcloud-provider-dept-8001/src/main/resources/application.yml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: 1113
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
#注册到多个服务中心
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改Eureka上的默认Status信息
#info配置
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
5.5 对比Zookeeper
6、Ribbon 负载均衡
第10集:负载均衡及Ribbon
实现客户端(消费端)集成Ribbon
第一步:导入依赖。
springcloud-consumer-dept-80/pom.xml
<!--Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
第二步:编写配置。
springcloud-consumer-dept-80/src/main/resources/application.yml
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false #不做服务提供者
service-url: #可以从3个地方取到服务
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
第三步:启动类注解@EnableEurekaClient
springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/DeptConsumer_80.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//Ribbon 和 Eureka 整合后,客户端可以直接调用,不用关心服务端的ip地址和端口号。
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
第四步:给RestTemplate添加负载均衡注解
springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/config/ConfigBean.java
package com.chw.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;
//@Configuration --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //Ribbon。为RestTemplate配置负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
第五步:修改controller中的服务访问地址
springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/controller/DeptConsumerController.java
package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate;
//private static final String REST_URL_PREFIX = "http://localhost:8001";
//应用了Ribbon后,这里的地址应该为变量。通过服务名SPRINGCLOUD-PROVIDER-DEPT来访问
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
@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/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
启动7001、7002、7003、8001、80。测试访问http://localhost/consumer/dept/list,成功。
总结:Ribbon 和 Eureka 整合后,客户端可以直接调用服务,不用关心服务端的ip地址和端口号。
第11集:使用Ribbon实现负载均衡
准备多个服务提供者
下面要实现多个服务提供者。展示负载均衡的效果。
有个问题,这些服务提供者要注册到哪个注册中心?根据什么原则进行分配注册?
可能原因:每个服务提供者都注册到所有注册中心,多个注册中心是为了保证总有能够正常运行的注册中心。
创建数据库
创建三个提供者。已经有一个8001了,还差8002和8003
8002和8003的依赖
把8001的pom.xml里的依赖全部复制给8002和8003
<dependencies>
<!--完善监控信息-->
<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.7.RELEASE</version>
</dependency>
<!--需要拿到实体类,所以要配置api模块,引入刚刚写的模块-->
<dependency>
<groupId>com.chw</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!--mybaits springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!--web相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
8002和8003的配置文件
springcloud-provider-dept-8002/src/main/resources/application.yml
server:
port: 8002
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db02?useUnicode=true&characterEncoding=utf-8
username: root
password: 9527
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8002 #修改Eureka上的默认Status信息
#info配置
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
springcloud-provider-dept-8003/src/main/resources/application.yml
server:
port: 8003
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db03?useUnicode=true&characterEncoding=utf-8
username: root
password: 9527
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8003 #修改Eureka上的默认Status信息
#info配置
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
注意:这其中,spring的配置的名称项,要求三个服务提供者都是一致的,这是前提。
spring:
application:
name: springcloud-provider-dept # 8001 8002 8003 三者的名称保持一致
8002和8003的其他文件
resources中的mybatis文件夹以及里面的xml文件。
java中的包和类。
8001、8002、8003三个模块几乎相同,除了数据库不一样之外。
启动测试负载均衡
注册中心:7001、7002、7003
服务提供者:8001、8002、8003
消费端:80
上图能看到3个服务都注册进来了。这就是获取到的服务的列表。
获取服务列表后,通过一种负载均衡算法来决定到底访问谁。
下面浏览器访问测试
第一次访问http://localhost/consumer/dept/list
第二次访问http://localhost/consumer/dept/list
第三次访问http://localhost/consumer/dept/list
看到数据源从db01到db03再到db02,这就是Ribbon默认的轮询机制。
第12集:自定义负载均衡算法
Ribbon对负载均衡的规则设置有个接口 IRule
,有以下实现类:
- RoundRobinRule : 默认的策略,轮询;
- AvailabilityFilteringRule : 过滤跳闸的服务,对剩下的进行轮询;
- RandomRule : 随机;
- RetryRule : 会先轮询获取服务,如果服务获取失败,则在指定时间内重试;
测试随机算法取代轮询算法
下面试一下把负载均衡算法设置成随机,取代默认的轮询算法。
springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/config/ConfigBean.java
package com.chw.springcloud.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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;
//@Configuration --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //Ribbon。为RestTemplate配置负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public IRule myRule() {
return new RandomRule();
}
}
重新启动测试
注册中心:7001、7002、7003
服务提供者:8001、8002、8003
消费端:80
看看list的获取的结果中,数据源是不是随机取的。
自定义负载均衡算法
先把刚刚定义的随机算法删去,因为不应该在那里面设定自定义的负载均衡算法。
1、重写IRule的实现类
自己重写一个负载均衡算法。把RandomRule复制过来重写,并重命名为ChwRandomRule
springcloud-consumer-dept-80/src/main/java/com/chw/myrule/ChwRandomRule.java
package com.chw.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
//import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class ChwRandomRule extends AbstractLoadBalancerRule {
public ChwRandomRule(){}
//现在想让每个服务访问5次,换下一个服务(一共有3个服务)。
//total=0, 默认为0,若=5,指向下一个服务节点
//index=0, 如果total=5,index+1
private int total = 0; //一个服务被调用的次数。
private int currentIndex = 0; //当前是谁在提供服务。
//@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 (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、写配置类
注意自己写的Ribbon配置组件不能和主启动类同级,会被@ComponentScan扫描到,所以要单独放在另外一个包里。
也就是这里的ChwRule.java要放在com.chw.myrule下,而不是放在com.chw.springcloud下。
springcloud-consumer-dept-80/src/main/java/com/chw/myrule/ChwRule.java
package com.chw.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChwRule {
@Bean
public IRule myRule() {
return new ChwRandomRule();//默认是轮询,现在自定义为了ChwRandomRule
}
}
3、主启动类添加注解
去主动类添加注解使自定义的Ribbon类生效
springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/DeptConsumer_80.java
package com.chw.springcloud;
import com.chw.myrule.ChwRule;
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;
//Ribbon 和 Eureka 整合后,客户端可以直接调用,不用关心服务端的ip地址和端口号。
@SpringBootApplication
@EnableEurekaClient
//在微服务启动时就去加载自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = ChwRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
启动测试即可。
7、Feign 负载均衡
准备工作
新建一个客户端module,命名为 springcloud-consumer-dept-feign
把springcloud-consumer-dept-80的依赖拷贝过来,另外加上Feign的依赖
(在springcloud-consumer-dept-feign模块和springcloud-api模块都加上)
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
在springcloud-consumer-dept-feign模块中创建 包 com.chw.springcloud;
把springcloud-consumer-dept-80的config包和controller包都拷贝过来;
把springcloud-consumer-dept-80的application.yml拷贝过来;
创建主启动类
springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/FeignDeptConsumer_80.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class FeignDeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeignDeptConsumer_80.class, args);
}
}
创建服务支持接口
在模块springcloud-api中创建包 com.chw.springcloud.service
创建服务接口
springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientService.java
package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
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;
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT") //value值写微服务的名字
public interface DeptClientService {
@GetMapping("/dept/get/{id}") //这里的映射地址要和服务端的controller方法的映射地址保持一致
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list") //这里的映射地址要和服务端的controller方法的映射地址保持一致
public List<Dept> queryAll();
@PostMapping("/dept/add") //这里的映射地址要和服务端的controller方法的映射地址保持一致
public Boolean addDept(Dept dept);
}
消费端写服务调用的controller方法
切换至springcloud-consumer-dept-feign模块,写controller方法
springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/controller/DeptConsumerController.java
package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptClientService;
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 java.util.List;
@RestController
public class DeptConsumerController {
//把springcloud-api中写的服务支持接口注入进来
@Autowired
private DeptClientService service = null;
@RequestMapping("/consumer/dept/add")
public Boolean add(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();
}
}
主启动类添加注解
springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/FeignDeptConsumer_80.java
package com.chw.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.annotation.ComponentScan;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.chw.springcloud"}) //激活feign及指定扫描范围
public class FeignDeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeignDeptConsumer_80.class, args);
}
}
Feign默认集成Ribbon,底层还是Ribbon,只是通过封装来简化了写法,更符合人们的开发习惯。
启动测试负载均衡
注册中心:7001、7002、7003
服务提供者:8001、8002、8003
消费端:FeignDeptConsumer_80
http://localhost/consumer/dept/list,测试成功,默认是轮询。
8、Hystrix 服务熔断
第14集:Hystrix 服务熔断
Hystrix官网资料:https://github.com/Netflix/Hystrix/wiki
下面演示。
准备工作
新建一个模块 springcloud-provider-dept-hystrix-8001,这个模块将在springcloud-provider-dept-8001的基础上进行改动,整合Hystrix。
把springcloud-provider-dept-8001的依赖、代码、resources都复制给springcloud-provider-dept-hystrix-800,其中主启动类是唯一要改的。
写主启动类
springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java
package com.chw.springcloud;
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;
//启动类
@SpringBootApplication
@EnableEurekaClient //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient //服务发现
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
}
实现服务端整合Hystrix熔断机制
现在这个服务提供者是不具有服务熔断功能的,下面要加入服务熔断功能。
步骤还是:导入依赖、编写配置、开启功能
第一步:导入Hystrix依赖
springcloud-provider-dept-hystrix-8001/pom.xml
<!--hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
第二步:编写配置
修改自定义名称 eureka.instance.instance-id
springcloud-provider-dept-hystrix-8001/src/main/resources/application.yml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.chw.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept #spring项目起个名字
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: 1113
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
#instance-id: springcloud-provider-dept8001 #修改Eureka上的默认Status信息
instance-id: springcloud-provider-dept-hystrix-8001 #修改Eureka上的默认Status信息
#info配置
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
第三步:编写一些功能
在controller中应用@HystrixCommand注解实现熔断机制
springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/controller/DeptController.java
package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
//提供RESTful服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") Long id) {
Dept dept = deptService.queryById(id);
if (dept == null) {
throw new RuntimeException("id 为 " + id + " 的用户不存在,或信息无法找到");
}
return dept;
}
//fallback方法
public Dept hystrixGet(@PathVariable("id") Long id) {
return new Dept()
.setDeptno(id)
.setDname("id 为 " + id + " 的信息不存在,null——@Hystrix")
.setDb_source("database is not exist");
}
}
第四步:主启动类添加注解支持熔断
springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java
package com.chw.springcloud;
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 chw1113
* @create 2022-07-25 10:55
*/
//启动类
@SpringBootApplication
@EnableEurekaClient //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //开启断路器,使支持熔断
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
}
启动测试
注册中心:7001、7002、7003
服务提供者:DeptProviderHystrix_8001
消费端:FeignDeptConsumer_80
http://localhost/consumer/dept/get/{id}
改变id测试。
小功能:显示服务提供方的ip
yml中设置 eureka.instance.prefer-ip-address设置为true
springcloud-provider-dept-hystrix-8001/src/main/resources/application.yml
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
#instance-id: springcloud-provider-dept8001 #修改Eureka上的默认Status信息
instance-id: springcloud-provider-dept-hystrix-8001
prefer-ip-address: true #设置为true时可显示服务的ip地址
第15集:Hystrix 服务降级
1、编写服务降级接口的实现类
在模块springcloud-api中写一个失败回调接口的实现
springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientServiceFallbackFactory.java
package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
// 服务降级
@Component //注册到spring中
public class DeptClientServiceFallbackFactory implements FallbackFactory {
//现在要停掉一个服务,所以返回的就是这个服务类。
//之前的熔断处理中,崩溃的是查询方法,返回的类型就是查询方法的返回类型。
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
return new Dept()
.setDname("id:" + id + "没有对应的数据,客户端提供了服务降级的信息,这个服务现在已经关闭")
.setDeptno(id)
.setDb_source("not exists");
}
@Override
public List<Dept> queryAll() { return null; }
@Override
public Boolean addDept(Dept dept) { return null; }
};
}
}
2、服务接口添加服务降级注解及属性
服务降级的操作写完之后,要和对应服务接口产生联系,就需要在该服务接口添加注解
springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientService.java
package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
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;
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
//value值写微服务的名字
//fallbackFactory属性写服务降级操作的类
public interface DeptClientService {
@GetMapping("/dept/get/{id}") //这里的映射地址要和服务端的controller方法的映射地址对应
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list") //这里的映射地址要和服务端的controller方法的映射地址保持一致
public List<Dept> queryAll();
@PostMapping("/dept/add") //这里的映射地址要和服务端的controller方法的映射地址保持一致
public Boolean addDept(Dept dept);
}
3、在客户端的yml配置文件中开启服务降级
切换至模块springcloud-consumer-dept-feign,
springcloud-consumer-dept-feign/src/main/resources/application.yml
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false #不做服务提供者
service-url: #可以从3个地方取到服务
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
#开启服务降级 feign.hystrix
feign:
hystrix:
enabled: true
注意到,刚才服务熔断的功能,是在服务端做的,而服务降级的功能,是在客户端(消费端)做的。
启动测试
注册中心:7001、7002、7003
服务提供者:springcloud-provider-dept-8001
消费端:FeignDeptConsumer_80
首先正常访问,http://localhost/consumer/dept/get/{id},顺利。
然后关闭FeignDeptConsumer_80,再次访问http://localhost/consumer/dept/get/{id},则被提示服务降级类里设置的信息。
服务熔断和服务降级的对比
服务熔断 | 服务降级 | |
---|---|---|
编写位置 | 服务端 | 客户端 |
是啥 | 某个服务超时或者异常,引起熔断 | 从整体网站负荷考虑,当某个服务关闭后,服务将不再被调用。此时客户端可以通过FallbackFactory来设置缺省值。整体的服务水平下降了。但客户端的请求还是有响应的。 |
第16集:Hystrix 流监控 Dashboard
新建模块springcloud-consumer-hystrix-dashboard
1、导入依赖
导入消费端的相关依赖
springcloud-provider-dept-hystrix-8001/pom.xml
<dependencies>
<!--Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Hystrix-dashboard依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--实体类——web-->
<dependency>
<groupId>com.chw</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>2.1.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
2、配置文件-指定监控页面端口号
写一下监控页面的端口号即可
springcloud-consumer-hystrix-dashboard/src/main/resources/application.yml
server:
port: 9001
3、写主启动类-添加监控注解
建包com.chw.springcloud。创建主启动类
springcloud-consumer-hystrix-dashboard/src/main/java/com/chw/springcloud/DeptConsumerDashboard_9001.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard //开启HystrixDashboard
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class, args);
}
}
既然有监控,那么服务端就必须得有监控相关的依赖。去检查,没有的话要补充
<!--完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
三个服务模块8001、8002、8003都检查过,之前已经添加了这个依赖。
启动主动类,访问http://localhost:9001/hystrix
下面进行配置。
注意 Hystrix Dashboard 生效条件
注意:Hystrix Dashboard监控是要监控已经实现了熔断功能的服务模块。比如springcloud-provider-dept-hystrix-8001这个模块里的controller中的get方法添加了@HystrixCommand注解实现了熔断功能。稍后监控也是监控与这个get方法对应的请求。
4、在被监控的服务的主动类里添加servlet的bean
稍后要打开7001和springcloud-provider-dept-hystrix-8001测试,先在hystrix-8001主启动类里注册一个servlet的bean。
springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java
package com.chw.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.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
//启动类
@SpringBootApplication
@EnableEurekaClient //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //开启熔断支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
//增加一个用于监控的Servlet
//这是段固定代码,用于配合监控
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
return servletRegistrationBean;
}
}
启动测试
启动7001、9001、DeptProviderHystrix_8001
访问http://eureka7001.com:7001/检查到服务已经注册进来。
访问http://localhost:8001/dept/get/1得到信息
访问http://localhost:8001/actuator/hystrix.stream看看监控流存不存在,会看到在不停地ping
访问http://localhost:9001/hystrix,填入监控流地址http://localhost:8001/actuator/hystrix.stream、delay和Title,点击监控
不断刷新http://localhost:8001/dept/get/1,会看到监控页面的变化
监控页面说明
9、Zuul路由网关
第17集:Zuul 路由网关
1、引入依赖
新建模块springcloud-zuul-9527
把springcloud-consumer-hystrix-dashboard模块中的依赖复制过来,再加上zuul的依赖
springcloud-zuul-9527/pom.xml
<dependencies>
<!--zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Hystrix-dashboard依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--实体类——web-->
<dependency>
<groupId>com.chw</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>2.1.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
2、写yml配置文件
springcloud-provider-dept-8001/src/main/resources/application.yml
server:
port: 9527
#这是一个应用,所以要有一个自己的名字
spring:
application:
name: springcloud-zuul
#这个应用要注册进Eureka,配置注册到哪个服务中心?
eureka:
client:
service-url:
#注册到了三个服务中心,就表示可以拿到服务中心里其他的服务了
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
#表示Status栏目下的实例的id
instance-id: zuul9527.com
prefer-ip-address: true #显示真实ip
#info配置,点进去Status下面的实例后跳转页面的内容。选配
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
为加强体验效果,
在C:\Windows\System32\drivers\etc的hosts文件里再添加一个默认域名映射
127.0.0.1 www.zuulgateway.com
3、写主启动类-添加注解@EnableZuulProxy
创建包com.chw.springcloud,创建主启动类
springcloud-zuul-9527/src/main/java/com/chw/springcloud/ZuulApplication_9527.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class, args);
}
}
启动测试
启动集群(注册中心):7001、7002、7003
启动服务端:8001
启动网关路由:9527
服务访问方式一:直接访问http://localhost:8001/dept/get/1,顺利。
服务访问方式二:http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1
即 http://{网关地址}:{网关端口}/{微服务名称小写}/请求路径
路由网关避免了真实地址的暴露问题
但是现在微服务的名字是被暴露的,下面进行优化,使它隐藏起来或者换成其他名字。
4、yml中配置zuul隐藏微服务名字
springcloud-provider-dept-8001/src/main/resources/application.yml
server:
port: 9527
#这是一个应用,所以要有一个自己的名字
spring:
application:
name: springcloud-zuul
#这个应用要注册进Eureka,配置注册到哪个服务中心?
eureka:
client:
service-url:
#注册到了三个服务中心,就表示可以拿到服务中心里其他的服务了
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
instance:
#表示Status栏目下的实例的id
instance-id: zuul9527.com
prefer-ip-address: true #显示真实ip
#info配置,点进去Status下面的实例后跳转页面的内容。选配
info:
app.name: Hyrule does not have shield back
company.name: breath.wild.zelda
#zuul配置
zuul:
routes:
#替换掉微服务的名字
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
重启9527,再次测试
访问http://www.zuulgateway.com:9527/mydept/dept/get/1,成功。
这样就把真实的微服务的名字隐藏了。
但此时原来的路径还是可以访问到服务http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1,现在我们要使原路径不能访问服务。
5、yml配置忽略原路径访问
在yml中添加配置 zuul.ignored-services
springcloud-provider-dept-8001/src/main/resources/application.yml
zuul:
routes:
#替换掉微服务的名字
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
ignored-services: springcloud-provider-dept # 不能再用这个路径访问服务
#ignored-services: "*" 表示隐藏全部
重启9527,再次测试
访问http://www.zuulgateway.com:9527/mydept/dept/get/1,成功。
访问原地址http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1,无法访问。
6、路由网关:设置公共的访问前缀
springcloud-provider-dept-8001/src/main/resources/application.yml
#zuul配置
zuul:
routes:
#替换掉微服务的名字
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
ignored-services: springcloud-provider-dept # 不能再用这个路径访问服务
#ignored-services: "*" 表示隐藏全部真实微服务路径
prefix: /chw #设置公共访问前缀。
#效果:http://www.zuulgateway.com:9527/mydept/dept/get/1
#---->http://www.zuulgateway.com:9527/chw/mydept/dept/get/1
10、SpringCloud Config分布式配置
第18集:Git环境搭建
这里的服务端指的就是配置服务中心,客户端指的就是一个个的微服务。
开始操作
登录码云,新建仓库
找一个文件夹存放即将要克隆的仓库
这里我的目录是D:\developer_tools\Git\git-space\localrepo\config-springcloud
在刚刚克隆下来的文件夹里新建application.yml文件
编辑内容
application.yml
spring:
profiles:
active: dev #表示激活谁,写dev就是激活dev
--- #单文件中可以通过`---`实现多文件的效果
spring:
profiles: dev #表示开发环境,用于赋值给active属性使自身得到激活
application:
name: springcloud-config-dev #给应用自定义名字
--- #单文件中可以通过`---`实现多文件的效果
spring:
profiles: test #表示测试环境
application:
name: springcloud-config-test #给应用自定义名字
下面要把刚刚的变动提交到gitee上
D:\developer_tools\Git\git-space\localrepo\config-springcloud\springcloud-config目录中右键选择Git Bash Here
第一步:add添加到暂存区
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git add .
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: application.yml
new file: application.yml.bak
第二步:commit提交(这一步还是在本地提交)
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git commit -m "first commit"
[master 676ff13] first commit
2 files changed, 36 insertions(+)
create mode 100644 application.yml
create mode 100644 application.yml.bak
第三步:push推送到远程
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 555 bytes | 555.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To gitee.com:*********/springcloud-config.git
6f65f50..676ff13 master -> master
第19集:服务端连接Git配置
新建模块:springcloud-config-server-3344
这个服务模块,我们想用它来连接远程仓库。
1、导入依赖
springcloud-config-server-3344/pom.xml
<dependencies>
<!-- spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--actuator监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2、编写配置
springcloud-config-server-3344/src/main/resources/application.yml
server:
port: 3344
spring:
application:
name: springcloud-config-server-3344
#重点:连接远程仓库
cloud:
config:
server:
git:
# gitee上springcloud-config仓库点击"克隆/下载"选https地址,复制过来。不是ssh
uri: https://gitee.com/chenwei_2022/springcloud-config.git
3、写主启动类
建包com.chw.springcloud。
springcloud-config-server-3344/src/main/java/com/chw/springcloud/Config_Server_3344.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer //开启配置服务
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class, args);
}
}
4、启动主动类测试,看能否得到gitee上仓库的文件。
访问http://localhost:3344/application-dev.yml,http://localhost:3344/application-dev.yml。看是否都访问到了配置文件。
这里我访问失败,依赖版本怎么换都不解决问题。
第20集:客户端连接服务端访问远程
下面要写客户端的
克隆到本地的仓库springcloud-config下新建文件config-client.yml
config-client.yml
spring:
profiles:
active: dev
---
server:
port: 8201
#spring的配置
spring:
#要测试多环境,先配置一个profiles,指明这是什么环境的配置文件
profiles: dev
application:
name: springcloud-provider-dept #spring项目起个名字
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
--- # "---"切换文件
server:
port: 8202
#spring的配置
spring:
#要测试多环境,先配置一个profiles,指明这是什么环境的配置文件
profiles: test
application:
name: springcloud-provider-dept #spring项目起个名字
#Eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
保存完push到远程
1、add添加暂存
2、commit提交
3、push到远程
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git add .
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: config-server.yml -> config-client.yml
renamed: config-server.yml.bak -> config-client.yml.bak
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git commit -m "3"
[master 0dc8b9a] 3
2 files changed, 0 insertions(+), 0 deletions(-)
rename config-server.yml => config-client.yml (100%)
rename config-server.yml.bak => config-client.yml.bak (100%)
Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 226 bytes | 226.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To gitee.com:********/springcloud-config.git
8b43ec4..0dc8b9a master -> master
刷新gitee仓库的网络页面,发现已经改完了
本地写一个客户端,来一个eureka,新建一个模块springcloud-config-client-3355
1、导入依赖
springcloud-config-client-3355/pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2、编写配置
先创建系统级别的配置文件:bootstrap.yml
再创建用户级别的配置文件:application.yml
这里设置系统级别的
springcloud-config-client-3355/src/main/resources/bootstrap.yml
#系统级别的配置
spring:
cloud:
config:
#我们想去远程读取一个config-client.yml的配置文件
name: config-client #需要从git上读取的资源名称,不需要后缀
# 指定拿哪个版本的配置
profile: dev
# 指定拿哪个版本的分支
label: master
#客户端是要连接服务器的,再由服务器连接远程获取配置
uri: http://localhost:3344
springcloud-config-client-3355/src/main/resources/application.yml
#用户级别的配置
#用户级别的配置
#配置一个名字即可
spring:
application:
name: springcloud-config-client-3355
3、写一个controller类用于测试是否能够从远程获取到配置信息
springcloud-config-client-3355/src/main/java/com/chw/springcloud/controller/ConfigClientController.java
package com.chw.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientController {
//之前是通过文件去绑定的,现在从远程绑定
@Value("${Spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig() {
return "applicationName: " + applicationName +
"eurekaServer: " + eurekaServer +
"port: " + port;
}
}
4、写主启动类
springcloud-config-client-3355/src/main/java/com/chw/springcloud/ConfigClient_3355.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class, args);
}
}
5、启动测试
把3344和3355都启动。
先检查server和gitee是否连通:http://localhost:3344/master/config-client-dev.yml
注意到,3355模块的配置文件中并没有设置端口号,而是由于bootstrap.yml里
#系统级别的配置
spring:
cloud:
config:
#我们想去远程读取一个config-client.yml的配置文件
name: config-client #需要从git上读取的资源名称,不需要后缀
# 指定拿哪个版本的配置
profile: dev
# 指定拿哪个版本的分支
label: master
#客户端是要连接服务器的,再由服务器连接远程获取配置
uri: http://localhost:3344
激活了dev,而config-client.yml的dev版本的端口是8201,所以这个客户端的配置现在就已经被设置成了config-client.yml的dev版本了。
访问一下控制器方法的路径,看能不能打印出来配置信息。http://localhost:8201/config
eureka,新建一个模块springcloud-config-client-3355
1、导入依赖
springcloud-config-client-3355/pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2、编写配置
先创建系统级别的配置文件:bootstrap.yml
再创建用户级别的配置文件:application.yml
这里设置系统级别的
springcloud-config-client-3355/src/main/resources/bootstrap.yml
#系统级别的配置
spring:
cloud:
config:
#我们想去远程读取一个config-client.yml的配置文件
name: config-client #需要从git上读取的资源名称,不需要后缀
# 指定拿哪个版本的配置
profile: dev
# 指定拿哪个版本的分支
label: master
#客户端是要连接服务器的,再由服务器连接远程获取配置
uri: http://localhost:3344
springcloud-config-client-3355/src/main/resources/application.yml
#用户级别的配置
#用户级别的配置
#配置一个名字即可
spring:
application:
name: springcloud-config-client-3355
3、写一个controller类用于测试是否能够从远程获取到配置信息
springcloud-config-client-3355/src/main/java/com/chw/springcloud/controller/ConfigClientController.java
package com.chw.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientController {
//之前是通过文件去绑定的,现在从远程绑定
@Value("${Spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig() {
return "applicationName: " + applicationName +
"eurekaServer: " + eurekaServer +
"port: " + port;
}
}
4、写主启动类
springcloud-config-client-3355/src/main/java/com/chw/springcloud/ConfigClient_3355.java
package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class, args);
}
}
5、启动测试
把3344和3355都启动。
先检查server和gitee是否连通:http://localhost:3344/master/config-client-dev.yml
注意到,3355模块的配置文件中并没有设置端口号,而是由于bootstrap.yml里
#系统级别的配置
spring:
cloud:
config:
#我们想去远程读取一个config-client.yml的配置文件
name: config-client #需要从git上读取的资源名称,不需要后缀
# 指定拿哪个版本的配置
profile: dev
# 指定拿哪个版本的分支
label: master
#客户端是要连接服务器的,再由服务器连接远程获取配置
uri: http://localhost:3344
激活了dev,而config-client.yml的dev版本的端口是8201,所以这个客户端的配置现在就已经被设置成了config-client.yml的dev版本了。
访问一下控制器方法的路径,看能不能打印出来配置信息。http://localhost:8201/config