Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
springboot可以单独使用,它不依赖于springcloud
而springcloud必然依赖于springboot,属于依赖关系
最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) |
创建一个纯maven项目
pom.xml文件
<packaging>pompackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.1.4.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
dependencies>
dependencyManagement>
pom.xml文件
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
<version>1.4.6.RELEASEversion>
dependency>
dependencies>
创建pojo项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9dAKYpJc-1663401681471)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220912141340366.png)]
编写Dept实体类
@Data
@NoArgsConstructor
@Accessors(chain = true)//链式写法
public class Dept implements Serializable { //Dept 实体类 orm 类表关系映射
private Long deptno;//主键
private String dname;
//这个数据存在哪个数据库的字段~ 微服务,一个服务对应一个数据库,同一个信息可能存在不同的数据库
private String db_source;
public Dept(String dname){
this.dname = dname;
}
}
数据库成功对应实体类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zkdSp4sk-1663401681472)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220912141512888.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-llb4H2Zl-1663401681473)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220912141528847.png)]
pom.xml文件
<dependencies>
<dependency>
<groupId>org.luffygroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
搭建项目路径创建对应文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IGhZQSaJ-1663401681473)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220912142124978.png)]
application.yaml
server:
port: 8001
# mybatis配置
mybatis:
type-aliases-package: com.luffy.springcloud.pojo
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.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?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="cacheEnabled" value="true"/>
settings>
configuration>
DeptMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luffy.springcloud.dao.DeptDao">
<insert id="addDept" parameterType="Dept">
insert into dept (dname, db_source) values (#{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>
DeptDao
@Mapper
@Repository
public interface DeptDao {
boolean addDept(Dept dept);
Dept queryById(Long id);
List<Dept> queryAll();
}
DeptService
public interface DeptService {
boolean addDept(Dept dept);
Dept queryById(Long id);
List<Dept> queryAll();
}
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();
}
}
DeptController
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(@RequestBody Dept dept) {
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
Dept dept = deptService.queryById(id);
if (dept == null) {
throw new RuntimeException("Fail");
}
return dept;
}
@GetMapping("/dept/list")
public List<Dept> queryAll() {
return deptService.queryAll();
}
}
启动类
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
pom.xml文件
<dependencies>
<dependency>
<groupId>org.luffygroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
搭建项目路径创建对应文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juQdFBVj-1663401681473)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220912142535263.png)]
application.yaml
server:
port: 80
ConfigBean
@Configuration
public class ConfigBean {
// 配置负载均衡
// IRule
// RoundRobinRule 轮询
// RandomRule 随机
// AvailabilityFilteringRule 会先过滤,跳闸,访问故障的服务,对剩下的进行轮询
// RetryRule:会先按照论照轮询获取服务~如果服务获取失败,则会在指定的时间内进行,重试
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
DeptConsumerController(消费者没有Service层)
@RestController
public class DeptConsumerController {
//理解:消费者,不应该有service层
//RestTemplate ... 供我们直接调用就可以了!注册到spring中
@Autowired
private RestTemplate restTemplate;
//提供多种远程便捷访问远程http服务的方法
//使用ribbon作为负载均衡,这里的地址应该是一个变量,通过服务名来访问
private static final String REST_URL_PREFIX = "http://localhost:8001";
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
//没有service层,通过http://localhost:8001/dept/list返回
@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);
}
}
启动类
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
Netflix在设计Eureka时,遵循的就是AP原则
Eureka是Netflix的一个子模块,也是核心模块之-。 Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper
Eureka三大角色
Eureka Server:提供服务注册与发现。和zookeeper客户端一样
Service Provider:将自身服务注册到Eureka中,从而使消费方能够找到
Service Consumer:服务消费方从Eureka中获取注册服务列表,从而找到消费服务
、
前言
一、springcloud-eureka-7001 maven项目
pom.xml
<artifactId>springcloud-eureka-7001artifact
>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
application.yaml
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureaka服务端的实例名称
client:
register-with-eureka: false #表示是否向Eureka注册中心注册自己
fetch-registry: false #如果fetch-registry为false,则表示自己为注册中心
service-url: #监控页面
#点进去参考源码,可看到默认的url端口配置为8761,我们设置为自己的端口。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
启动类
@SpringBootApplication
@EnableEurekaServer //表示为Eureka服务端的启动类,可以接收别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
运行7001端口可进入注册中心自带的网址,可查看已注册的服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLy1VLsV-1663401681474)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220913153451643.png)]
二、springcloud-provider-dept-8001项目
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.6.RELEASEversion>
dependency>
application.yaml
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-provider-dept8001
启动类
@SpringBootApplication
@EnableEurekaClient//在服务启动后,自动注册到Eureka注册中心中
@EnableDiscoveryClient //注册进来的微服务,获取一些信息。服务发现,扩展内容
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
Controller
//获取一些配置的信息,得到具体的微服务
@Autowired
private DiscoveryClient client;
//注册进来的微服务,获取一些信息。没有实际作用
@RequestMapping("/dept/discovery")
public Object discovery(){
//获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("discovery=>services "+services);
//得到一个具体的微服务信息,通过具体的微服务id,7001中的applicationName
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost()+"\t\t\t"+
instance.getPort()+"\t\t\t"+
instance.getUri()+"\t\t\t"+
instance.getServiceId());
}
return this.client;
}
三、自我保护机制
四、Eureka监控信息info
监控信息是添加在服务项目里的
pom.xml
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
application.yaml
#info配置 Eureka的status的xx/info链接点开后的info监控信息。没有太大意义
info:
app.name: Love Zhao Jinmai
company.name: Love Zhao Jinmai
在Eureka界面对应的服务点击,即可显示application.yaml里info写的信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vzJIJ0uq-1663401681474)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220913154501689.png)]
复制两个7001项目名为7002,7003,除了别名其他全部一致
修改C:\Windows\System32\drivers\etc路径下的hosts文件,在末尾增加如下代码
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
三个项目的yaml文件要修改,增加集群代码
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka7001.com #Eureaka服务端的实例名称
client:
register-with-eureka: false #表示是否向Eureka注册中心注册自己
fetch-registry: false #如果fetch-registry为false,则表示自己为注册中心
service-url: #监控页面
#单机:点进去参考源码,可看到默认的url端口配置为8761,我们设置为自己的端口。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
#集群(除自身外 关联其他所有)
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
修改8001项目的yaml文件,将服务添加到集群
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka,http://localhost:7003/eureka
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-provider-dept8001
#info配置 Eureka的status的xx/info链接点开后的info监控信息。没有太大意义
info:
app: love Zhao Jinmai
company.name: love Zhao Jinmai
访问7001或7002、7003都会挂载其他两个不同的集群
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaPPKBSF-1663401681475)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220913155454163.png)]
当一个集群崩了不会影响其他集群
理论核心
一个分布式系统不可能同时很好的满足-致性,可用性和分区容错性这三个需求
根据CAP原理,将NoSQL数据库分成了满足CA原则,满足CP原则和满足AP原则三大类:
CA:单点集群,满足-致性,可用性的系统,通常可扩展性较差
CP: 满足- -致性,分区容错性的系统,通常性能不是特别高
AP:满足可用性。分区容错性的系统,通常可能对一致性要求低一些
Eureka保证的是AP
Zookeeper保证的是CP
ACID是什么?
A (Atomicity)原子性
C (Consistency)- 致性
I (Isolation) 隔离性
D (Durability) 持久性
CAP是什么?
负载均衡简单分类:
**理解:**负载均衡就是不要把压力全部放到一个服务上,让它们平摊
把8001
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
<version>1.4.6.RELEASEversion>
dependency>
ConfigBean
@Configuration
public class ConfigBean {//@Configuration=spring的 application.xml
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced //ribbon负载均衡的作用
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
application.yaml
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false #不向Eureka中注册自己
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
Controller
//声明提供者的localhost路径
//private static final String rest_url_prefix = "http://localhost:8001";
//通过ribbon去实现负载均衡,这里服务应该是一个变量,通过服务名来访问 *
private static final String rest_url_prefix = "http://SPRINGCLOUD-PROVIDER-DEPT";
启动类
//消费者 运行方式80可省略 例:localhost/consumer/dept/list
//Ribbon 和 Eureka 整合后,客户端可直接调用,不用关心Ip地址和端口号,会在定义的多个地址中随机选择
@SpringBootApplication
@EnableEurekaClient//在服务启动后,自动注册到Eureka注册中心中
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
启动7001、7002、7003、8001、80,可以正常查出数据,但因为服务只有一个,所有负载均衡效果不明显
复制两个8001项目名为8002,8003,除了别名其他全部一致,数据库的db_source字段更改,代表数据源不一样,对应端口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LymKxE9J-1663401681475)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220913160859488.png)]
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) DEFAULT NULL,
`db_source` varchar(60) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='部门表'
INSERT INTO dept(dname,db_source) VALUES ('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('项目部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('研发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('运维部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('人事部',DATABASE());
在最底层目录的上一级创建myrule文件夹(否则会被扫描到)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpLH5cun-1663401681475)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220913161124356.png)]
RuleConfig
//自定义Ribbon配置的负载均衡类,客户端RibbonConfiguration中已存在的组件与FooConfiguration中的任何组件组成(后者通常会覆盖前者)
//自定义的组件请注意 它不在|程序的上下文的ComponentScan中,所以要放在单独的不重叠的包中
@Configuration
public class RuleConfig {
@Bean
public IRule myRule() {
//默认是轮询,现在我们自定义为DiyRandomRule 自定义负载均衡
return new DiyRandomRule();
}
}
DiyRandomRule
public class DiyRandomRule extends AbstractLoadBalancerRule {
//代码全是复制的 DiyRandomRule.class的,自定义负载均衡需要自己修改
//当前自定义负载均衡:
//每个服务访问5次。换下一个服务
//total=0,默认=0,如果=5,指向下一个服务节点
//index=0,默认0,如果total=5,则inedx+1
private int totla=0;//被调用的次数
private int currentIndex=0;//当前是谁在提供服务
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(totla<5){
server = upList.get(currentIndex);
totla++;
}else{
totla=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) {
}
}
启动类
@SpringBootApplication
@EnableEurekaClient//在服务启动后,自动注册到Eureka注册中心中
//在微服务启动的时候就能去加载我们自定义Ribbon配置的负载均衡类,自定义为跳转5次切换节点
@RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = RuleConfig.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
因为自定义了负载均衡类就会代替自带的负载均衡
自定义的负载均衡执行后效果是每执行五次换一次数据源
feign 主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法
Feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service. SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
只需要创建一一个接口,然后添加注解即可!
Ribbon和Feign都是用于调用其他服务的,不过方式不同。
Ribbon RestFul风格
Feign 面向接口
feign项目(客户端)
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.luffygroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
<scope>compilescope>
dependency>
dependencies>
在service中创建DeptClientService(这里由于项目结构原因创建在api项目中)
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@PostMapping("/dept/list")
public List<Dept> queryAll();
@GetMapping("/dept/add")
public boolean addDept(Dept dept);
}
启动类
//消费者 运行方式80可省略 例:localhost/consumer/dept/list
@SpringBootApplication
@EnableEurekaClient //在服务启动后,自动注册到Eureka注册中心中
@EnableFeignClients(basePackages = {"com.luffy.springcloud"}) //Feign被扫描到
public class DeptConsumer_feign {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_feign.class,args);
}
}
ConfigBean
@Configuration
public class ConfigBean {
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced //ribbon负载均衡的作用
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
controller
@RestController
public class DeptConsumerController {
//Feign面向接口编程
@Autowired
private DeptClientService deptClientService =null;
//添加数据
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return deptClientService.addDept(dept);
}
//通过id查询
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return deptClientService.queryById(id);
}
//查询所有
@RequestMapping("/consumer/dept/list")
public List<Dept> queryAll(){
return deptClientService.queryAll();
}
}
熔断制机,主要用于应对雪崩效应的一种保护机制。指的是为了保全整体牺牲局部。编程中的熔断主要是为了避免整个服务崩溃,所以行相应的处理,比如将服务降级。
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”、如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒中内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一 个依赖出问题的情况下, 不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝) , 向调用方返回-一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
服务降级
服务熔断
服务限流
接近实时的监控
·····
理解:熔断是存于服务器的,通常是服务器异常崩掉的情况下启用熔断,在被动的情况下显示启动异常或预备的方案代码
springcloud-provider-dept-hystrix-8001
是一个服务器
pom.xml(在正常8001上添加一个依赖)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
<version>1.4.6.RELEASEversion>
dependency>
controller
//提供Restful服务
@RestController
public class DeptController{
@Autowired
private DeptService deptService;
@RequestMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet") //如果失败 去调用Hystrix的备选方案
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){//如果当前id值为空 抛出异常
throw new RuntimeException("id=> "+ id+"不存在该用户,或者该信息无法找到");
}
return dept;
}
//如果出现异常 采取Hystrix的备选方案
public Dept hystrixGet(@PathVariable("id") Long id){
return new Dept()
.setDeptno(id)
.setDname("id=> "+id+"没有找到相关信息,null by Hystrix")
.setDb_source("not found database in mysql");
}
}
启动类
@SpringBootApplication
@EnableEurekaClient//在服务启动后,自动注册到Eureka注册中心中
@EnableDiscoveryClient //注册进来的微服务,获取一些信息。服务发现,扩展内容
@EnableCircuitBreaker //添加Hystrix服务熔断 断路器的支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
}
其余文件通过8001项目copy正常修改
访问数据库没有的数据会服务异常,这时候就会启动服务熔断启用controller里的备选方案
理解:降级是存于客户端的,在我们选择关闭服务器的时候,主动选择显示给客户端的数据
修改代码
在api项目的service层添加DeptClientServiceFallbackFactory类
//Hystrix 降级,当服务端关闭后的提示信息
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDname("id=>" +id+"没有对应的信息,客户端提供了降级的信息,这个服务现在已经关闭")
.setDb_source("已降级 未查找到数据");
}
@Override
public List<Dept> queryAll() {
return null;
}
@Override
public boolean addDept(Dept dept) {
return false;
}
};
}
}
DeptClientService接口(在FeignClient注解增加服务降级功能,在api项目)
@Component//注册到Spring容器
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
}
application.yaml(在feign项目)
#开启降级Feign Hystrix
feign:
hystrix:
enabled: true
测试运行一切正常,关闭服务器之后会按照降级的方案执行
新建Maven项目 springcloud-consumer-hystrix-dashboard(客户端)
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
<version>1.4.6.RELEASEversion>
dependency>
application.yaml
#Hystrix的dashboard流监控 端口
server:
port: 9001
启动类
@SpringBootApplication
@EnableHystrixDashboard//开启监控
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
进入localhost:9001/hystrix可以查看官方主页
springcloud-provider-dept-hystrix-8001项目(服务端)
启动类
@SpringBootApplication
@EnableEurekaClient//在服务启动后,自动注册到Eureka注册中心中
@EnableDiscoveryClient //注册进来的微服务,获取一些信息。服务发现,扩展内容
@EnableCircuitBreaker //添加Hystrix服务熔断 断路器的支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
//端口号是被监控的项目端口,在页面输入这个网址即可监控
//增加一个servlet,配合dashboard监控使用,固定的代码 http://localhost:8001/actuator/hystrix.stream访问监控
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
根据监控的网址进入指定项目的监控页面,指定的项目只要进行了操作监控页面都会有响应
Zuul包含了对请求的路由和过滤两个最主要的功能:
注意:Zuul服务最终还是会注册进Eureka
提供:代理+路由+过滤 三大功能
创建maven项目 `springcloud/springcloud-zuul-9527
pom.xml
<!--Zuul路由网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
C:\Windows\System32\drivers\etc\hosts修改这个文件模拟地址
//在最后添加
127.0.0.1 www.luffy.com
启动类
@SpringBootApplication
@EnableZuulProxy//zuul路由注解
public class zuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(zuulApplication_9527.class,args);
}
}
application.yaml
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
#之前的查询链接地址 http://www.luffy.com:9527/springcloud-provider-dept/dept/get/1
#现在的查询链接地址,配置后为 http://www.luffy.com:9527/mydept/dept/get/1
#两者都皆可访问(原路径+现配路径)。配置自定义的前缀后 可不让客户端知道真实的ip地址
mydept.path: /mydept/**
#加上此配置后 原路径不能访问(springcloud-provider-dept/dept/get/6),只能通过自定义的规则路径访问。
ignored-services: springcloud-provider-dept
#星号(*) 隐藏全部的项目真实名字
ignored-services: "*"
prefix: /li #设置公共的地址前缀 配置后链接为:www.luffy.com:9527/li/mydept/dept/get/11
再次启动zuul启动类,可看到是我们自定义的路由规则,可有效的隐藏真实服务名及地址。
访问:www.luffy.com:9527/li/mydept/dept/get/2
Spring Cloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
Spring Cloud Config 分为服务端和客户端两部分
服务端也称为分布式配置中心,它是一 个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理。并且可以通过git客户端工具来方便的管理和访问配置内容。
作用
git初始基础命令
# 显示当前的Git配置
$ git config --list
# 设置提交代码时的用户信息
$ git config --global user.name "[name]"
$ git config --global user.email "[email address]"
git提交远程仓库命令
git add . 将文件添加到暂存区
git status 查看状态
git commit -m “一次提交” 本地提交,-m为提交时写的信息
git push origin master 提交到远程的当前路径分枝
将这段代码提交到码云(application.yaml)
spring:
profiles:
active: dev
---
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
新建maven项目springcloud-config-server-3344
pom.xml
<artifactId>springcloud-config-server-3344artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
<version>2.1.1.RELEASEversion>
dependency>
dependencies>
application.yaml
server:
port: 3344
spring:
application:
name: springcloud-config-server
#连接远程的仓库
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/springcloud-config.git #自己远程仓库的https地址
# 通过 config-server可以连接到git,访问其中的资源以及配置~
启动类
@SpringBootApplication
@EnableConfigServer //开启配置服务
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class,args);
}
}
输入localhost:3344/application-test.yaml可以访问之前的application.yaml里的test内容
输入localhost:3344/application-dev.yaml可以访问之前的application.yaml里的dev内容
创建一个要读取的yaml放到远程
config-client.yaml
#启动环境选择的配置
spring:
profiles:
active: dev
#springboot启动多环境的配置
---
server:
port: 8201
#spring的配置
spring:
profiles: dev
application:
name: springcloud-config-client-dev
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
---
server:
port: 8202
#spring的配置
spring:
profiles: test
application:
name: springcloud-config-client-test
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
新建Maven项目springcloud-config-client-3355
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
<version>2.1.1.RELEASEversion>
dependency>
dependencies>
application.yaml
#用户级别的配置 配置去读取谁
spring:
application:
name: springcloud-config-client-3355
bootstrap.yaml
# 系统级别的配置
spring:
cloud:
config:
name: config-client # 需要从git上读取的资源名称,不要后缀
profile: dev #dev环境端口:8201 test环境端口:8202
label: master #需要在git上的哪个分支拿
#连接到3344服务,中转站的形式连接服务端访问远程地址
uri: http://localhost:3344
controller
//@Value为git上的client-config的值
@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;
}
}
启动类
@SpringBootApplication
public class Config_Client_3355 {
public static void main(String[] args) {
SpringApplication.run(Config_Client_3355.class,args);
}
}
启动3344和3355,因3355中的bootstrap.yml配置的git仓库中的dev环境,dev环境的端口为8201,所以是通过8201访问项目
访问localhost:8201/config即可将远程仓库的数据读取显示
远程仓库创建config-eureka.yaml
#启动环境选择的配置
spring:
profiles:
active: dev
---
server:
port: 7001
#spring的配置
spring:
profiles: dev
application:
name: springcloud-config-eureka-dev
#Eureka配置
eureka:
instance:
hostname: eureka7001.com #Eureaka服务端的实例名称
client:
register-with-eureka: false #表示是否向Eureka注册中心注册自己
fetch-registry: false #如果fetch-registry为false,则表示自己为注册中心
service-url: #监控页面
#单机:点进去参考源码,可看到默认的url端口配置为8761,我们设置为自己的端口。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
#集群(除自身外 关联其他所有)
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
---
server:
port: 7001
#spring的配置
spring:
profiles: test
application:
name: springcloud-config-eureka-test
#Eureka配置
eureka:
instance:
hostname: eureka7001.com #Eureaka服务端的实例名称
client:
register-with-eureka: false #表示是否向Eureka注册中心注册自己
fetch-registry: false #如果fetch-registry为false,则表示自己为注册中心
service-url: #监控页面
#单机:点进去参考源码,可看到默认的url端口配置为8761,我们设置为自己的端口。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
#集群(除自身外 关联其他所有)
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
远程仓库创建config-dept.yaml
#启动环境选择的配置
spring:
profiles:
active: dev
---
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.luffy.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
profiles: dev
application:
name: springcloud-config-dept
#数据源的配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源为druid
driver-class-name: com.mysql.jdbc.Driver #数据库驱动
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&servetTimeZone=Asia/Shanghai
username: root
password: root
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-provider-dept8001
prefer-ip-address: true #改为true后 Eureka中的status就会显示真实ip地址
#info配置 Eureka的status的xx/info链接点开后的info监控信息。没有太大意义
info:
app.name: ti zi zui bang,jiayou
company.name: tizi.lemon.com
test.name: hahah test
---
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.luffy.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
profiles: test
application:
name: springcloud-config-dept
#数据源的配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源为druid
driver-class-name: com.mysql.jdbc.Driver #数据库驱动
url: jdbc:mysql://localhost:3306/db02?useUnicode=true&characterEncoding=utf-8&servetTimeZone=Asia/Shanghai
username: root
password: root
#Eureka的配置。 服务注册到Eureka中,需要一个路径地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
#修改Eureka中status的默认描述信息。不配置默认为DESKTOP-XXX描述
instance-id: springcloud-provider-dept8001
prefer-ip-address: true #改为true后 Eureka中的status就会显示真实ip地址
#info配置 Eureka的status的xx/info链接点开后的info监控信息。没有太大意义
info:
app.name: ti zi zui bang,jiayou
company.name: tizi.lemon.com
test.name: hahah test
创建Maven项目springcloud-config-eureka-7001 客户端
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
<version>2.1.1.RELEASEversion>
dependency>
dependencies>
复制 springcloud-eureka-7001 项目所有的内容,删掉application.yaml所有配置
application.yaml
spring:
application:
name: sorubgckiyd-config-eureka-7001
bootstrap.yaml
# 系统级别的配置
spring:
cloud:
config:
name: config-eureka # 需要从git上读取的资源名称,不要后缀
profile: dev #dev环境端口:8201 test环境端口:8202
label: master #需要在git上的哪个分支拿
#连接到3344服务,中转站的形式连接服务端访问远程地址
uri: http://localhost:3344
创建Maven项目springcloud-config-dept-8001 服务端
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
<version>2.1.1.RELEASEversion>
dependency>
dependencies>
复制 springcloud-config-dept-8001 项目所有的内容,删掉application.yaml所有配置
application.yaml
spring:
application:
name: springcloud-config-dept-8001
bootstrap.yaml
#系统级的配置
# 系统级别的配置
spring:
cloud:
config:
name: config-dept # 需要从git上读取的资源名称,不要后缀
profile: dev #dev环境端口:8201 test环境端口:8202
label: master #需要在git上的哪个分支拿
#连接到3344服务,中转站的形式连接服务端访问远程地址
uri: http://localhost:3344
运行
注意:远程代码更新需要重新push,否则代码没更新会报错