SpringCloud
SpringCloud是什么
- SpringCloud, 基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监 控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
- SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。
- SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组合起来,通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套 简单易懂,易部署和易维护的分布式系统开发工具包
- SpringCloud 是 分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全桶。
- SpringBoot可以离开SpringClooud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
常用相关组件
- 服务发现——Netflix Eureka
- 客服端负载均衡 Netflix Ribbon
- 服务端负载均衡:Feign
- 断路器——Netflix Hystrix
- 服务网关——Netflix Zuul
与dubbo的对比
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服务网关 | 无 | Spring Cloud Netfilx Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总栈 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。dubbo是一套rpc框架但是springcloud确是微服的一站式解决方案
版本号
SpringCloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如最早的Realse版本:Angel,第二个Realse版本:Brixton,然后是Camden、DalstonEdgware,目前最新的是Hoxton SR4 CURRENT GA通用稳定版。
版本说明
BUILD-XXX 开发版 开发团队内部使用,不是很稳定
GA 稳定版 相比于开发版,基本上可以使用了
PRE(M1、M2) 里程碑版 主要是修复了一些BUG的版本,一个GA后通常有多个里程碑版
RC 候选发布版 该阶段的软件类似于最终版的一个发行观察期,基本只修复比较严重的BUG
SR 正式发布版
搭建微服环境
准备工作
1.内存起码8g的电脑
2.idea
3.搭建父项目
springcloud-demo
建最简单的maven项目
建出来之后内部只有.idea和pom.xml
pom文件设置如下
代码语言:javascript复制<?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>kj08</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common</module>
</modules>
<packaging>pom</packaging><!--必须是pom,使用maven分模块管理,都会有一个父级项目,pom文件一个重要的属性就是packaging(打包类型),一般来说所有的父级项目的packaging都为pom,packaging默认类型jar类型,如果不做配置,maven会将该项目打成jar包。-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<!--Maven中的dependencyManagement元素提供了一种管理依赖版本号的方式。
在dependencyManagement元素中声明所依赖的jar包的版本号等信息,那么
所有子项目再次引入此依赖jar包时则无需显式的列出版本号。Maven会沿着父子
层级向上寻找拥有dependencyManagement 元素的项目,然后使用它指定的版本号。
dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖。-->
<dependencies>
<!--SpringCloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Maven的继承和Java的继承一样,只能单继承,无法实现多继承,
你是否想过我们如果要继承多个父模块的时候应该怎么做呢?或许你会
想只往一个父模块中添加jar包依赖,只继承一个父模块就可以了,
但是这样的话所有的jar包都混合在一起了,jar包分类就不在清晰了,
其实我们可以用另外一种方式实现多模块继承,这个方法就是使用
<type>pom</type><scope>import</scope>,解释一下:type
标签的默认值是jar,代表我们依赖导入的是一个jar包,现在我们设置成了
pom,说明导入的是一个父模块,后面的scope标签中的值import代表把父
模块中的jar包导入进来,不过需要注意的是<type>pom</type><scope>
import</scope>,这种方式只能用在<dependencyManagement>
</dependencyManagement>-->
<!--SpringBoot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--=========日志相关=============-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.创建一个公共调用模块 也就是一个子工程(父工程右键加模块) 取名common
用quickstart原型就行了
里面放公共的实体类
后面创建的服务或者是客户端如果用到这些类的话就不用去自己创建了,只需在pom要引用这个common模块就行了
pom文件类似于这样
代码语言:javascript复制<?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-demo</artifactId>
<groupId>kj08</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<name>common</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
5.新建文本放旁边记录服务和对应端口,我们这边要创建约12个服务
创建生产者provider
在父工程右键加模块什么都不用勾 后面再pom中统一的去设置
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>kj08</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>kj08</groupId>
<artifactId>provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--=========日志相关=============-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>kj08</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编写相关配置
代码语言:javascript复制server:
port: 9001 #端口
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis配置输出日志
spring:
application:
name: cloud-provoder #服务名
datasource: #数据源配置
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/kj08?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
username: root
password: 9320
创建相关controller service mapper
访问看看能不能controller能不能调用能调用好就是创建完毕
创建消费者consumer
与生产者同样的创建方式
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>springcloud-demo</artifactId>
<groupId>kj08</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>kj08</groupId>
<artifactId>consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--=========日志相关=============-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>kj08</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件
代码语言:javascript复制server:
port: 9002 #端口
配置bean
创建conf文件夹并且编写配置类
代码语言:javascript复制package kj08.consumer.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class configBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
RestTemplate
RestTemplate 是由 Spring 提供的一个 HTTP 请求工具。开发者也可以不使用 RestTemplate ,使用 Java 自带的 HttpUrlConnection 或者经典的网络访问框架 HttpClient 也可以完成上文的案例,只是在 Spring 项目中,使用 RestTemplate 显然更方便一些。它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。
controller的返回值现在开始都要用统一返回类
常用方法
getForEntity
getForEntity方法的返回值是一个ResponseEntity<T>
,ResponseEntity<T>
是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。
@RequestMapping("/gethello")
public String getHello() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
String body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();
StringBuffer result = new StringBuffer();
result.append("responseEntity.getBody():").append(body).append("<hr>")
.append("responseEntity.getStatusCode():").append(statusCode).append("<hr>")
.append("responseEntity.getStatusCodeValue():").append(statusCodeValue).append("<hr>")
.append("responseEntity.getHeaders():").append(headers).append("<hr>");
return result.toString();
如果接口需要我们传递参数,除了直接在地址后面拼还有以下两种形式
代码语言:javascript复制@RequestMapping("/sayhello")
public String sayHello() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/sayhello?name={1}", String.class, "张三");
return responseEntity.getBody();
}
@RequestMapping("/sayhello2")
public String sayHello2() {
Map<String, String> map = new HashMap<>();
map.put("name", "李四");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/sayhello?name={name}", String.class, map);
return responseEntity.getBody();
还有一种是将地址变成UriComponents 然后参数也放进去,但是个人认为代码反而变得啰嗦,不如前三种,有兴趣的可以去了解一下
getForObject
对getForEntity的封装关注返回的消息体的内容,对其他信息都不关注,也是经常用到的
postForEntity
代码语言:javascript复制@RequestMapping("/book3")
public Book book3() {
Book book = new Book();
book.setName("红楼梦");
ResponseEntity<Book> responseEntity = restTemplate.postForEntity("http://127.0.0.1:8080/getbook2", book, Book.class);
return responseEntity.getBody();
}
//book就是你要传的参数
postForObject
同上都一样
还有put delete方法用法都一样没啥区别
总之两个forObject是最常用的
不要把他想象的多高端,就一工具类
所以你的controller里应该类似于这样去写
代码语言:javascript复制package kj08.consumer.controller;
import kj08.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class StudentController {
@Autowired
private RestTemplate restTemplate;
private static final String URL="http://xxxxx";
@RequestMapping("queryAll")
public List<Student> queryAll(){
return restTemplate.getForObject(URL "/queryAll", List.class);
}
}
然后我们调comsumer的controller看看是否能成功调用到provider的controller
Eureka
Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper;
CAP原则
一致性 c
可用性a
分区容错性 p
特点
三者不可兼得eureka是ap dubbo是cp
原理
Eureka采用了C-S的架构设计,EurekaServer 作为服务注册功能的服务器,他是服务注册中心而系统中的其他微服务注册再其中。使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块(比如Zuul)就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑;包含两个组件
Eureka Server
提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client
Eureka Client是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉(默认周期为90秒)
与zookeeper的区别
1.Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则(尽管现在2.0发布了,但是由于其闭源的原因 ,博主一直无法进一步的研究,但是目前 Ereka 1.x 任然是比较活跃的)。Eureka Server 也可以运行多个实例来构建集群(后面专门的文章讲解),解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构(参看:微服务与微服务架构思想与原则),无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。
在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制(replicate To Peer)操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。
当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳(默认周期为30秒),Eureka Server 将会注销该实例(默认为90秒,如果某个 eureka.instance.lease-expiration-duration-in-seconds 进行自定义配置)。当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式(后面有文章会谈及关于 Eureka Server 的自我保护机制)
2.与 Eureka 有所不同,Apache Zookeeper 在设计时就紧遵CP原则,即任何时候对 Zookeeper 的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是 Zookeeper 不能保证每次服务请求都是可达的。从 Zookeeper 的实际应用情况来看,在使用 Zookeeper 获取服务列表时,如果此时的 Zookeeper 集群中的 Leader 宕机了,该集群就要进行 Leader 的选举,又或者 Zookeeper 集群中半数以上服务器节点不可用(例如有三个节点,如果节点一检测到节点三挂了 ,节点二也检测到节点三挂了,那这个节点才算是真的挂了),那么将无法处理该请求。所以说,Zookeeper 不能保证服务可用性。
当然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是 Zookeeper 设计紧遵CP原则的另一个原因。但是对于服务发现来说,情况就不太一样了,针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。因为对于服务消费者来说,能消费才是最重要的,消费者虽然拿到可能不正确的服务实例信息后尝试消费一下,也要胜过因为无法获取实例信息而不去消费,导致系统异常要好(淘宝的双十一,京东的幺六八就是紧遵AP的最
搭建服務
同样的创建子模块(server端)
pom
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>springcloud-demo</artifactId>
<groupId>kj08</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>kj08</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--eureka服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在这边说一下springcloud的组件使用方式一般都是创建服务 加依赖 写配置 在启动类加对应的注解(创加写注===cjxz)
为了方便识别 可以在C:WindowsSystem32driversetc的host文件下加本地域名
代码语言:javascript复制127.0.0.1 eureka
编写配置文件
代码语言:javascript复制server:
port: 7003
eureka:
instance:
hostname: eureka #实例名称 本来应该是127.0.0.1
client:
register-with-eureka: false #是否向eureka注册自己
fetch-registry: false #如果是false就表示自己是注册中心
service-url: ##如果你的eureka只有一个就填自己地址,是个集群就填其他两个eureka的地址
defaultZone: http://eureka:7003/eureka/
spring:
application:
name: eureka-server
启动类加注解
代码语言:javascript复制@SpringBootApplication
@EnableEurekaServer//启动类
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
启动 访问http://eureka:7003/能看到对应的网页就代表能成功了
eureka页面介绍从上到下
system status(系统状态)
environment代表运行环境描述
可以在配置文件里面去改
代码语言:javascript复制eureka:
environment: xxx
datacenter 同理
Current time:当前的系统时间 Uptime:已经运行了多少时间 Lease expiration enabled:是否启用租约过期 ,自我保护机制关闭时,该值默认是true, 自我保护机制开启之后为false。 Renews threshold: 每分钟最少续约数,Eureka Server 期望每分钟收到客户端实例续约的总数。 Renews (last min): 最后一分钟的续约数量(不含当前,1分钟更新一次),Eureka Server 最后 1 分钟收到客户端实例续约的总数。
DS Replicas:代表的是Eureka Server相邻节点,互为一个集群,但是我们现在只有一个服务治理中心(Eureka Server),默认不显示出来
Instances currently registered with Eureka:当前注册再eureka的实例
general info(通用的信息)
total-avail-memory : 总共可用的内存 environment : 环境名称,默认test num-of-cpus : CPU的个数 current-memory-usage : 当前已经使用内存的百分比 server-uptime : 服务启动时间 registered-replicas : 相邻集群复制节点 unavailable-replicas :不可用的集群复制节点 available-replicas :可用的相邻集群复制节点 instance info:实例信息
eureka自我保护机制()
代码语言:javascript复制eureka:
server:
enable-self-preservation: false #关闭自我保护机制 默认是打开的
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行
自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
- Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
- Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
- 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
说了这么多仅适用于网络波动并且原来的服务是好的,如果真的是服务宕机了,神仙来了都没用
生产者消费者注册eureka并消费者调用生产者服务
我们之前看到了两者之间可以通过http请求书写ip地址的方式相互通信,现在我们利用eureka方式进行交互
生产者注册到eureka
加依赖
代码语言:javascript复制 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
加配置
代码语言:javascript复制eureka:
client:
service-url:
defaultZone: http://eureka:7001/eureka/
instance:
instance-id: springcloud-provider-9001 #修改默认描述状态信息
prefer-ip-address: true #改为true默认显示的是ip地址,而不是Localhost(鼠标移到status下的链接,在网页的左下角会出现地址)
启动类加注解
代码语言:javascript复制@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
启动服务你就可以看到provider就被注册到了eureka中了
看application那一行 的status就对应这你的instanceid了 但是你现在点是点不出来东西的,但是你只要加一个依赖和一个配置就能看到一些东西了(也就是告诉别人你这个服务是干嘛的)
代码语言:javascript复制 <!--完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
代码语言:javascript复制info: #描述信息
appp: ddd
消费者注册带eureka
加依赖
代码语言:javascript复制 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
加配置
代码语言:javascript复制eureka:
client:
register-with-eureka: false #不注册自己
service-url:
defaultZone: http://eureka:7001/eureka/
启动类加注解
代码语言:javascript复制package kj08.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
eureka集群
我们到目前为止使用eureka连接了消费者和生产者,但是在正式环境中,往往不能只依赖于一个注册中心,因为一旦这一个挂了整个项目就完蛋了,所以还是那句话,既然一个不行就弄多个也就衍生了集群这个概念,下面我们就来学习如何配置eureka集群
准备工作 还是把localhost 再加eureka1 eureka2
复制两个eureka项目
改配置文件
1.改端口
2.改hostname
3.改defaultzone :不写自己,把其他两个地址弄上去就行了 然后他们三个就互联了
消费者和服务者需要改变 因为是集群了 所以消费者生产者的defaultzone 也变成三个就搞定了
Ribbon
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超时、重试等等。简单的说,就是在配置文件中列出LoadBalancer(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。
LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡软件有 Nginx,Lvs 等等
分类:
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施
- Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方!
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
- Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!
注意,这个东西是在消费者端玩的不是在生产者端玩的
我们可以再复制一个消费者和多个生产者去玩这个ribbon 为了区别他调了不同的服务,你可以把生产者的方法返回值稍稍的改一改以作区分最终要看到的效果是一个消费者不断的访问,出来的结果不一样。
消费者加依赖
代码语言:javascript复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
在template上加注解
代码语言:javascript复制@Configuration
public class configBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
生产者复制多份
既然多个消费之那么你要通过eureka服务了,那么就不需要用ip地址去访问这个生产者的服务了,你就用服务名去获取就完事了,也就是说,你在controller中 把url的ip地址变成生产者的服务名就行了
代码语言:javascript复制package kj08.consumer.controller;
import kj08.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class StudentController {
@Autowired
private RestTemplate restTemplate;
private static final String URL="http://CLOUD-PROVODER";
@RequestMapping("queryAll")
public List<Student> queryAll(){
return restTemplate.getForObject(URL "/queryAll", List.class);
}
}
只需要改端口和 instance-id就行,但是请spring: application: name:xxx 必须得是一致的
Feign负载均衡
之前的ribbon是通过地址去调用的,但是还有一种调用方式我们更加熟悉,就是通过接口的方式去调用服务
这就演变成出了feign负载均衡
Feign集成了Ribbon,feign其实不是做负载均衡的,负载均衡是ribbon的功能,feign只是集成了ribbon而已,但是负载均衡的功能还是feign内置的ribbon再做,而不是feign。
使用步骤
复制之前的消费者
加依赖
代码语言:javascript复制 <!--Feign相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
写接口
代码语言:javascript复制package kj08.comsumerfeign.service;
import kj08.comsumerfeign.factory.StudentServiceFallbackFactory;
import kj08.pojo.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(value = "cloud-provoder")
public interface IStudentService {
@RequestMapping("queryAll")
List<Student> queryAll();
}
改controller
代码语言:javascript复制package kj08.comsumerfeign.controller;
import kj08.comsumerfeign.service.IStudentService;
import kj08.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class StudentController {
@Autowired(required = false)
private IStudentService studentService;
@RequestMapping("queryAll")
public List<Student> queryAll(){
return studentService.queryAll();
}
}
加注解在启动类
代码语言:javascript复制package kj08.comsumerfeign;
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
public class ComsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ComsumerFeignApplication.class, args);
}
}
Hystrix断路器,服务熔断
我们微服当中现在能想象的到。目前来说有很多的微服,而这些微服是一个调用一个的这种关系,但是如果一旦中建环节出了问题,我们的请求服务不能得到正常的释放,就会一直挂在那从而导致过多占用服务器的资源,进而导致服务器内存撑不住而宕机,所以我们想了要有一个机制就是当我们某个服务不行的时候,就绕过他,就像保险丝一样,有个保险,不会出现以上的情况,而这个机制就是Hystrix
什么是Hystrix? Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。 “断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩.
这个是在生产者这边去玩的,复制一分生产者
加依赖
代码语言:javascript复制 <!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
改端口配置
把实例id改一改
把controller改一改
代码语言:javascript复制package kj08.providerhystrix.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import kj08.pojo.Student;
import kj08.providerhystrix.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class StudentController {
@Autowired
private IStudentService studentService;
@HystrixCommand(fallbackMethod="cc")
@RequestMapping("queryAll")
public List<Student> queryAll(){
int i = 1/0;
return studentService.queryAll();
}
public List<Student> cc(){
ArrayList list = new ArrayList();
Student studnet = new Student();
studnet.setStudentName("我粗了");
list.add(studnet);
return list;
}
}
//其实就是加个方法写个 @HystrixCommand注解
启动类加注解
代码语言:javascript复制package kj08.providerhystrix;
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;
c
//@EnableDiscoveryClient
public class ProviderHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderHystrixApplication.class, args);
}
}
效果是你的服务抛异常了你就进你写的那个方法了有点trycatch的感觉了
服务降级
1.什么是服务降级?
整体资源快不够了,忍痛先将某些服务关闭,等度过难关,再开启回来
服务降级是指当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要或不紧急的服务或任务进行服务的延迟使用或暂停使用。
降级的方式可以根据业务来,可以延迟服务,比如延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行 ;或者在粒度范围内关闭服务,比如关闭相关文章的推荐。
2.服务降级需要考虑的问题
1)那些服务是核心服务,哪些服务是非核心服务 2)那些服务可以支持降级,那些服务不能支持降级,降级策略是什么 3)除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
3.自动降级分类
- 1)超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
- 2)失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
- 3)故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
- 4)限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
降级是在消费端玩的
在之前的fegin消费者下用
1.改配置
代码语言:javascript复制feign:
hystrix:
enabled: true #服务降级
2.建工厂
代码语言:javascript复制package kj08.comsumerfeign.factory;
import feign.hystrix.FallbackFactory;
import kj08.comsumerfeign.service.IStudentService;
import kj08.pojo.Student;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class StudentServiceFallbackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
return new IStudentService() {
@Override
public List<Student> queryAll() {
ArrayList list = new ArrayList();
Student studnet = new Student();
studnet.setStudentName("降级了");
list.add(studnet);
return list;
}
};
}
}
接口里加注解配置
代码语言:javascript复制package kj08.comsumerfeign.service;
import kj08.comsumerfeign.factory.StudentServiceFallbackFactory;
import kj08.pojo.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(value = "cloud-provoder",fallbackFactory = StudentServiceFallbackFactory.class)
public interface IStudentService {
@RequestMapping("queryAll")
List<Student> queryAll();
}
效果关闭生产者 照样能运行但是调的是工厂里写的那个service
服务熔断—>服务端:一般是某个服务故障或者异常引起,类似现实世界中的 “保险丝” , 当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时! 服务降级—>客户端:所谓降级,一般是从整体负荷考虑,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
- 实现方式不太一样,服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
熔断,降级,限流:
- 熔断:依赖的下游服务器故障触发熔断,避免引发本地系统崩溃,系统自动执行和恢复
- 降级:服务分优先级,牺牲非核心业务,保证核心服务稳定,从整体符合考虑
- 限流:限制并发的请求访问量,超过阈值则拒绝
hystrixDashboard(调用监控)
创建监控工程
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>kj08</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>kj08</groupId>
<artifactId>dashboard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dashboard</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置端口
启动类加注解
代码语言:javascript复制package kj08.dashboard;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard//开启监控 被监控的必须的有springboot的actuator的监控依赖
public class DashboardApplication {
public static void main(String[] args) {
SpringApplication.run(DashboardApplication.class, args);
}
}
在浏览器输入地址加/hystrix回车 出来页面
他要你填地址
你这时候就要在生产者那边的启动类去配置
代码语言:javascript复制@Bean
public ServletRegistrationBean getBean(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
return servletRegistrationBean;
}
在之前的页面上填地址 然后下面两个一个是延时一个是标题你自己看着写 然后点击下面的button 监控流的意思
你调到第二个页面就成功了
我们可以在监控信息的左上部分找到两个重要的图形信息:一个实心圆和一条曲线。
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,如下图所示,它的健康度从绿色、黄色、橙色、红色递减。该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,我们就可以在大量的实例中快速的发现故障实例和高压力实例。 曲线:用来记录2分钟内流量的相对变化,我们可以通过它来观察到流量的上升和下降趋势。
zuul网关
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
Zuul服务最终还是会注册进Eureka
功能:代理 路由 过滤
搭建zuul服务
代码语言:javascript复制<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>springcloud-demo</artifactId>
<groupId>kj08</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>kj08</groupId>
<artifactId>zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置
代码语言:javascript复制server:
port: 9101
spring:
application:
name: zuul
eureka:
client:
service-url:
defaultZone: http://eureka:7003/eureka/,http://eureka1:7004/eureka/,http://eureka2:7005/eureka/
instance:
instance-id: zuul
zuul:
routes:
stu.serviceId: cloud-provoder #隐藏微服的名字 变成以下的配置
stu.path: /stu/** #注意这个地方就要这样写 xx.serviceId 和xx.path
ignored-services: "cloud-provoder" #忽略服务 * 代表所有
prefix: /kj08 #地址前缀
启动类
代码语言:javascript复制package kj08.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
总结 :使用zuul能代替原生产者的地址比如原来是127.0.0.1:8080地址 但是现在你可以用zuul的地址 服务名去访问,而且加上路由过滤之后你可以吧服务名给替换掉,并且把还能使用原服务名不能用,还能加前缀后缀之类的