微服务架构4个核心问题:
问题原因:网络不可靠!!
解决方案:
spring cloud 生态!
Spring Cloud NetFlix 一站式解决方案
Apache Dubbo Zookeeper 半自动,需要整合别人的
Spring Cloud Alibaba 一站式解决方案,更加简单了
新概念:服务网格 Service Mesh
微服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持微服务。一个微服务的策略可以让工作变得更为简便,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议。
简而言之,微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。
微服务
强调的是服务的大小,他关注的是某一个点,是具体解决某一个问题/提供者落地对应服务的一个服务应用,侠义的看,可以看作是IDEA中的一个个微服务工程,或者Moudel
IDEA工具里面使用Maven开发的一个个独立Moudle,它具体是使用springboot开发的一个小模块,专业的事情交给专业的模块来做,一个模块就做着一件事情
强调的是一个个的个体,每个个体完成一个具体的任务或者功能
微服务架构
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅专注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。如果在大规模系统协作下,如何保持系统的稳定性、可快速迭代性就变得非常紧迫。微服务架构便是在这样的历史背景下诞生,提供了一套大型项目开发的解决方案。
优点
缺点
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot、Spring、SpringMVC |
服务配置与管理 | Netflix公司的Archaius、阿里的Diamind等 |
服务注册与发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Spectator |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpernStack、Kubernetes |
数据流操作开发包 | SpringCloud Stream(封装与Redis,Rabbit,Kafaka等发送接收消息) |
事件消息总线 | Spring Cloud Bus |
1. 选型依据
2. 当前各大IT公司用的微服务架构有哪些?
功能点/服务框架 | Netflix/SpringCloud | Motan | gRPC | Thrift | Dubbo/DubboX |
---|---|---|---|---|---|
功能定位 | 完整的微服务框架 | RPC框架,但整合了ZK或Consul,实现集群环境的基本服务注册/发现 | RPC框架 | RPC框架 | 服务框架 |
支持Rest | 是,Ribbon支持多种可插拔的序列化选择 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是(Hession2) | 是 | 是 | 是 |
支持多语言 | 是(Rest形式)? | 否 | 是 | 是 | 否 |
负载均衡 | 是(服务端zuul+客户端Ribbon),zuul-服务,动态路由,云端负载均衡Eureka(针对中间层服务器) | 是(客户端) | 否 | 否 | 是(客户端) |
配置服务 | Netfix Archaius,Spring Cloud Config Server集中配置 | 是(zookeeper提供) | 否 | 否 | 否 |
服务调用链监控 | 是(zuul),zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
高可用/容错 | 是(服务端Hystrix+客户端Ribbon) | 是(客户端) | 否 | 否 | 是(客户端) |
典型应用案例 | Netflix | Sina | |||
社区活跃程度 | 高 | 一般 | 高 | 一般 | 2017年后重新开始维护,之前中断了5年 |
学习难度 | 中等 | 低 | 高 | 高 | 低 |
文档丰富程度 | 高 | 一般 | 一般 | 一般 | 高 |
其他 | Spring Cloud Bus为我们的应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践的公司比较多 |
Spring Cloud为开发人员提供了一些工具来快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导层选举、分布式会话、群集状态)。分布式系统的协调导致了锅炉板模式,使用Spring Cloud开发人员可以快速建立实现这些模式的服务和应用程序。它们在任何分布式环境中都能很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心和云计算(Cloud Foundry)等托管平台。
SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了一些快速构建分布式系统的工具,包括配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导层选举、分布式会话、群集状态等,他们都可以用springboot的开发风格做到一键启动和部署。
Springboot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考验的微服务架构组合起来,通过Springboot风格再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者流出了一套简单易懂,易部署和易维护的分布式系统开发工具包
SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
1. 分布式+服务治理Dubbo
目前成熟的互联网架构:应用服务化拆分+消息中间件
2. Dubbo和SpringCloud对比
Dubbo | Spring Cloud | |
---|---|---|
服务注册中心 | 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 |
最大区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWDUQYFL-1592228653357)(upload\image-20200613111208064.png)]
Spring Cloud是一个由众多独立子项目组成的大型综合项目,每个子项目有不同的发行节奏,都维护着自己的发布版本号,SPring Cloud通过一个资源清单BOM(Bill of Materials)来管理每个版本的子项目清单,为了避免与子项目的发布好混淆,所以没有采用版本号的方式,而是通过命名的方式。
这些版本名称的命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本的时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden,Dalston,Edgware等
参考文档
https://www.springcloud.cc/spring-cloud-netflix.html
中文API文档:https://www.springcloud.cc/spring-cloud-dalston.html
springcloud中文网:https://www.springcloud.cc/
大版本说明
SpringCloud | SpringBoot |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
实际开发版本
Spring Boot | Spring Cloud |
---|---|
1.5.2.RELEASE | Dalston.RC1 |
1.5.9.RELEASE | Edgware.RELEASE |
2.0.2.RELEASE | Finchley.BUILD-SNAPSHOT |
2.0.3.RELEASE | Finchley.RELEASE |
创建一个普通的maven项目
删除src目录
在pom.xml中修改打包方式为pom
<packaging>pompackaging>
导入依赖
<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.0modelVersion>
<groupId>cn.codeweigroupId>
<artifactId>springcloud-studyartifactId>
<version>1.0-SNAPSHOTversion>
<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>
<lombok.version>1.18.12lombok.version>
<log4j.version>1.2.17log4j.version>
<logback.version>1.2.3logback.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR5version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.3.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.20version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.22version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.2version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>${log4j.version}version>
dependency>
dependencies>
dependencyManagement>
project>
新建模块,普通的maven模块 springcloud-api
在该模块中导入需要的依赖
<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-studyartifactId>
<groupId>cn.codeweigroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>springcloud-apiartifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
新建实体类
@Data
@NoArgsConstructor
@Accessors(chain = true) // 开启链式写法
public class Dept implements Serializable {
private Long deptno; // 主键
private String dname;
private String db_source; // 这个数据是存在哪个数据库的字段,同一个信息可能存在不同的数据库
public Dept(String dname) {
this.dname = dname;
}
}
新建模块,普通的maven模块,服务提供者,springcloud-provider-dept-8081
在该模块中导入需要的依赖
<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-studyartifactId>
<groupId>cn.codeweigroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>spirngcloud-provider-dept-8081artifactId>
<dependencies>
<dependency>
<groupId>cn.codeweigroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
新建文件application.yaml进行配置
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/db01?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: shw123zxc
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
application:
name: spirngcloud-provider-dept
mybatis:
type-aliases-package: cn.codewei.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
写一个mapper接口 DeptMapper
@Mapper
@Repository
public interface DeptMapper {
public boolean addDept(Dept dept);
public Dept queryById(@Param("depton") Long depton);
public List<Dept> queryAll();
}
写对应的DeptMapper.xml
<mapper namespace="cn.codewei.springcloud.mapper.DeptMapper">
<insert id="addDept" parameterType="cn.codewei.springcloud.pojo.Dept">
insert into dept(dname,db_source) values (#{dname},DATABASE())
insert>
<select id="queryById" resultType="cn.codewei.springcloud.pojo.Dept" parameterType="Long">
select * from dept where deptno = #{deptno}
select>
<select id="queryAll" resultType="cn.codewei.springcloud.pojo.Dept">
select * from dept
select>
写一个service接口,DeptSerivce
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
DeptServiceImpl 实现类
@Service
@Transactional // 开启事务
public class DeptServiceImpl implements DeptService {
@Autowired
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();
}
}
写一个Controller接口,来提供RestFul服务
// 提供Restful服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(@RequestBody Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/queryById/{deptno}")
public Dept queryById(@RequestParam("deptno") Long deptno){
return deptService.queryById(deptno);
}
@GetMapping("/dept/queryAll")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
写一个主启动类 DeptProvider_8081
// 启动类
@SpringBootApplication
public class DeptProvider_8081 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8081.class,args);
}
}
新建模块,普通的maven模块,服务消费者,springcloud-consumer-dept-80
在该模块中导入需要的依赖
<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-studyartifactId>
<groupId>cn.codeweigroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>springcloud-consumer-dept-80artifactId>
<dependencies>
<dependency>
<groupId>cn.codeweigroupId>
<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>
project>
新建文件application.yaml进行配置
server:
port: 80
写一个配置类来将RestTemplate放入IOC容器中
@Configuration // 相当于spring中的 applicationContext.xml 来把对象放入IOC中
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
写一个Controller接口 DeptConsumerController 来给前端提供访问
@Controller
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate; //提供多种便捷访问远程http服务的方法,简单的Restful服务模板
private static final String Rest_URL_PREFIX = "http://localhost:8081"; // 把请求的前缀定义出来 避免重复写
// 消费者,不应该有service层
@GetMapping("/consumer/queryAll")
@ResponseBody
public List<Dept> queryAll(){
return restTemplate.getForObject(Rest_URL_PREFIX+"/dept/queryAll", List.class);
}
@PostMapping("/consumer/add")
@ResponseBody
public Boolean addDept(Dept dept){
return restTemplate.postForObject(Rest_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
@GetMapping("/consumer/queryById/{deptno}")
@ResponseBody
public Dept queryById(@PathVariable("deptno") Long deptno){
return restTemplate.getForObject(Rest_URL_PREFIX+"/dept/queryById/"+deptno,Dept.class);
}
}
编写主启动类 DeptConsumer_80
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
这样,服务消费者通过RestTemplate就可以发起请求获取到远程的服务了
又称服务中心,管理各种服务功能包括服务的注册、发现、熔断、负载、降级等。
Eureka的基本架构
Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现
Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心
而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑
和Dubbo架构对比
Eureka 包含两个组件:Eureka Server 和 Eureka Client
Eureka Server 提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉(默认周期90秒)
三大角色
新建一个模块springcloud-eureka-7001
导入相关依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
<version>2.2.3.RELEASEversion>
dependency>
新建一个application.yaml,编写配置
server:
port: 7001 # 通过该端口来访问监控管理后台
# Eureka
eureka:
instance:
hostname: localhost # Eureka服务端的实例名字
client:
register-with-eureka: false # 是否向Eureka注册中心注册自己
fetch-registry: false # 表示fetch-registry 如果为 false,则表示自己为注册中心
service-url:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/ # 通过该地址来获取与注册服务 使用 ${} 来获取上面配置好的参数
编写一个主启动类
添加**@EurekaServer**注解
@SpringBootApplication
@EnableEurekaServer // 服务端的启动类,可以接受别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
这样,就可以访问localhost:7001 来访问监控管理后台了
服务注册
在服务提供者模块spirngcloud-provider-dept-8081添加Eureka依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.7.RELEASEversion>
dependency>
在application.yaml中进行配置eureka
# Eureka 的配置
eureka:
client:
service-url: # 指定注册到哪里
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept-8081 # 修改在eureka监控管理后台的描述
在主启动类上开启注解支持@EnableEurekaClient
// 启动类
@SpringBootApplication
@EnableEurekaClient // 在服务启动后,自动注册到Eureka中
public class DeptProvider_8081 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8081.class,args);
}
}
监控信息配置
点击 springcloud-provider-dept-8081
就能够跳转到一个页面,展示出详细的信息
在服务提供者模块spirngcloud-provider-dept-8081添加spring-boot-starter-actuator
依赖,来完善监控信息
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
在spirngcloud-provider-dept-8081的application.yaml中进行配置监控信息
# info配置,完善监控信息
info:
app.name: codewei-springcloud
company.name: Awei
自我保护机制
一句话总结:某时刻某一个服务不可以用了,eureka不会立刻清理,依旧会对该服务的信息进行保存
eureka.server.enable-self-preservation = false
禁用自我保护模式【不推荐关闭自我保护机制】拓展
在服务提供者模块spirngcloud-provider-dept-8081的Controller中,写一个方法,来获取一些消息,在团队开发中才会用到
// 获取一些配置的信息,得到的具体的微服务
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/dept/getClient")
public Object discovery(){
// 获取微服务列表的清单
List<String> services = discoveryClient.getServices();
System.out.println("discovery===>service:"+services);
// 得到一个具体的微服务信息,通过具体的微服务ID,applicationName
List<ServiceInstance> instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri()+"\t"+ instance.getServiceId());
}
return this.discoveryClient;
}
然后,在启动类DeptProvider_8081上添加一个注解@EnableDiscoveryClient
为了防止注册中心崩掉!
再新建两个moudle,springcloud-eureka-7002和springcloud-eureka-7003
分别这两个模块导入eureka的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
<version>2.2.3.RELEASEversion>
dependency>
再为两个模块分别创建application.yaml进行配置
server:
port: 7002 # 通过该端口来访问监控管理后台
# Eureka
eureka:
instance:
hostname: localhost # Eureka服务端的实例名字
client:
register-with-eureka: false # 是否向Eureka注册中心注册自己
fetch-registry: false # 表示fetch-registry 如果为 false,则表示自己为注册中心
service-url:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/ # 通过该地址来获取与注册服务 使用 ${} 来获取上面配置好的参数
server:
port: 7003 # 通过该端口来访问监控管理后台
# Eureka
eureka:
instance:
hostname: localhost # Eureka服务端的实例名字
client:
register-with-eureka: false # 是否向Eureka注册中心注册自己
fetch-registry: false # 表示fetch-registry 如果为 false,则表示自己为注册中心
service-url:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/ # 通过该地址来获取与注册服务 使用 ${} 来获取上面配置好的参数
只是端口号不同~~
分别为两个模块创建主启动类,并添加注解@EnableEurekaServer
来开启EurekaServer服务
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7002.class,args);
}
}
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7003 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7003.class,args);
}
}
为了我们的集群搭建测试成功和方便理解,我们要修改一些hosts文件,修改一下域名映射
在C:\Windows\System32\drivers\etc\
下的hosts文件添加
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
然后每个eureka模块分别修改其yaml文件中的hostname
,如
eureka:
instance:
hostname: eureka7001.com # Eureka服务端的实例名字
并且修改yaml中的defaultZone,如
在springcloud-eureka-7001模块
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
在springcloud-eureka-7002模块
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
在springcloud-eureka-7003模块
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
这样,这三个eureka注册中心就相互关联起来了!!
然后,修改spirngcloud-provider-dept-8081模块中的yaml,修改defaultZone,更改发布的位置
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
启动三个eureka服务和provider服务,就可以看到
这样,访问其中一个注册中心,也会看到其他的注册中心已经被关联了
并且,每个注册中心都会被注册provider
这样,就算是其中某一个或者两个eureka注册中心崩了,也不会影响程序的正常运行
回顾CAP原则
RDBMS(MySQL,Oracle,SqlServer) ====> ACID
NoSQL(Reids,Mongdb) ===> CAP
ACID是什么?
CAP是什么?
CAP的三进二:CA,AP,CP
作为服务注册中心,Eureka比Zookeeper好在哪里?
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)、和P(分区容错性)。由于分区容错性P在分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。
Zoopkeeper保证CP:
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但是不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样的一种情况,当master节点因网路故障与其他节点失去联系时,剩余的节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群是都是不可用的,这就导致在选举期间注册服务瘫痪,在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka保证AP:
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时如果发现连接失败,则会自动切换至其他的节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1.Eureka不再从注册列表中移除因为长时间没有收到心跳而应该过期的服务
2.Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3.当前网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此,Eureka可以很好的应对因网络故障导致节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
Ribbon是什么?
Ribbon能干什么?
集成Ribbon
在消费者模块springcloud-consumer-dept-80
添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.2.3.RELEASEversion>
dependency>
在消费者模块springcloud-consumer-dept-80
编写配置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/ # 去哪拿服务
register-with-eureka: false # 不向Eureka中注册自己
在主启动类添加注解@EnableEurekaClient
,让Eureka生效
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
在消费者模块springcloud-consumer-dept-80
的配置类ConfigBean中进行配置负载均衡,只需要在RestTemplate注入IOC的方法上添加一个注解@LoadBalanced
@Configuration // 相当于spring中的 applicationContext.xml 来把对象放入IOC中
public class ConfigBean {
@Bean
@LoadBalanced // Ribbon开启负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
在消费者模块springcloud-consumer-dept-80
修改Controller的访问路径,路径不能写死,应该通过服务名来访问
private static final String Rest_URL_PREFIX = "http://SPRIRNG-PROVIDER-DEPT"; // 这里指向的是服务提供者模块中application.yaml中配置的application.name
Ribbon和Eureka整合以后,客户端可以之间调用,不用关心IP地址和端口号!
Ribbon默认算法:轮询
新建两个数据库,db02和db03,都和db01相同
再新建两个服务提供者模块,spirngcloud-provider-dept-8082
和spirngcloud-provider-dept-8083
分别对新建的两个模块导入和spirngcloud-provider-dept-8081
相同的依赖,修改application.yaml配置,改为自己所指向的数据库
一定要保证三个服务的application.name一致!!
将spirngcloud-provider-dept-8081
中java下的所有和resource下的mybatis复制到两个模块,只需要修改主启动类为自己的类名
这三个服务提供者模块,只有连接的数据库不同~~
启动集群进行测试
可以看到注册进来了三个服务,相同的服务的名字
然后访问服务消费者的接口localhost:80/consumer/queryAll
,不断的刷新,我们就可以看到,我们拿到的数据是来自不同的数据库,这样就实现了负载均衡
Ribbon实现负载均衡,最核心的一个注解就是@LoadBalanced
负载有一个核心的接口 IRule
实现类:
我们要自定义Ribbon负载均衡算法,在消费者模块springcloud-consumer-dept-80
中进行自定义,注意,我们自定义的负载均衡算法,不能和主启动类同级,可以主启动类的上一级
在主启动类的上一级进一个包myrule,新建一个类CodeweiRule,继承AbstractLoadBalancerRule
,然后写负载均衡算法
public class CodeweiRule extends AbstractLoadBalancerRule {
// 每个服务,访问5次,换下一个服务
// index=0,默认0,如果total=5,index+1
private int total = 0; // 被调用的次数
private int currentIndex=0; // 当前是谁在提供服务
// @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
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 = chooseRandomInt(serverCount); // 生成区间随机数
// 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();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
然后再myrule包下,写一个配置类AweiRule,添加注解Configuration
,进行配置,指定我们自定义的负载均衡算法
@Configuration
public class AweiRule {
@Bean
public IRule myRule(){
return new CodeweiRule(); // 默认是轮询,现在我们定义为CodeweiRule负载均衡算法了
}
}
在主启动类上,添加注解@RibbonClient(name="SPIRINTCLOUD-PROVIDER-DEPT",configuration = MyRule.class)
,通过name指定服务的名字,通过configuration来指定自己自定义的Ribbon配置类
@SpringBootApplication
@EnableEurekaClient
// 在微服务启动的时候,就能够去去加载我们自定义的Ribbon类
@RibbonClient(name="SPIRNGCLOUD-PROVIDER-DEPT",configuration = AweiRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
这样,通过我们自定义的负载均衡算法,就可以实现,每一个服务访问5次后,访问下一个服务,一个轮回后,再回来访问第一个服务
feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
只需要创建一个接口,然后添加注解即可!
Feign,主要是社区,大家都习惯面向接口编程,这个是很多开发人员的规范。调用微服务访问两种方法
Feign能干什么?
Feign集成了Ribbon
利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
新建一个模块,springcloud-consumer-dept-feign
和springcloud-consumer-dept-80
相同,导入和springcloud-consumer-dept-80
相同的依赖,再导入feign的依赖,修改主启动类
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
<version>1.4.7.RELEASEversion>
dependency>
在springcloud-api
模块下添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
<version>1.4.7.RELEASEversion>
dependency>
在springcloud-api
模块下新建一个serivce包,创建一个接口DeptClientService,在类上添加注解@FeignClient(value="SPIRNGCLOUD-PROVIDER-DEPT")
,value是指定服务的名字,然后写对应提供者中服务的方法
@FeignClient
@Serivce
public interface DeptClientService {
@GetMapping("/dept/queryById/{deptno}")
public Dept queryById(@PathVariable("deptno") Long deptno);
@GetMapping("/dept/queryAll")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public boolean addDept(@RequestBody Dept dept);
}
这样在springcloud-consumer-dept-feign
模块的Controller中,之间通过@Autowired
来获取到服务
@Controller
public class DeptConsumerController {
@Autowired
private DeptClientService deptClientService;
// 消费者,不应该有service层
@GetMapping("/consumer/queryAll")
@ResponseBody
public List<Dept> queryAll(){
return deptClientService.queryAll();
}
@PostMapping("/consumer/addDept")
@ResponseBody
public boolean addDept(Dept dept){
return deptClientService.addDept(dept);
}
@GetMapping("/consumer/queryById/{deptno}")
@ResponseBody
public Dept queryById(@PathVariable("deptno") Long deptno){
return deptClientService.queryById(deptno);
}
}
在springcloud-consumer-dept-feign
模块的主启动类上添加注解@EnableFeignClients
,让Feign生效
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {
"cn.codewei.springcloud"}) // 扫描提供服务的包
public class FeignDeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeignDeptConsumer_80.class,args);
}
}
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败!
当一切正常时,请求看起来是这样的:
当其中有一个系统有延迟时,它可能阻塞整个用户请求:
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
什么是Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix能干什么
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand
代码实现
新建一个服务提供者模块spirngcloud-provider-dept-hystrix-8081
内容保持和spirngcloud-provider-dept-8081
相同,修改主启动类的类名和yaml中的instance-id
导入Hystrix的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.2.3.RELEASEversion>
dependency>
把之前的Controller中的代码删掉,重新编写
// 提供Restful服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/queryById/{deptno}")
@HystrixCommand(fallbackMethod = "hystrixQueryById") // 知道备选方法
public Dept queryById(@PathVariable("deptno") Long deptno){
Dept dept = deptService.queryById(deptno);
if (dept == null){
throw new RuntimeException("ID==>"+deptno+",不存在该用户,或信息无法找到");
}
return dept;
}
// 备选方案
public Dept hystrixQueryById(@PathVariable("deptno") Long deptno){
return new Dept().setDeptno(deptno)
.setDname("ID==>"+deptno+",没有对应的信息,为null---@Hystrix")
.setDb_source("no this database in mysql");
}
}
在主启动类添加对熔断的支持@EnableCircuitBreaker
// 启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class HystricDeptProvider_8081 {
public static void main(String[] args) {
SpringApplication.run(HystricDeptProvider_8081.class,args);
}
}
启动测试
这样,在访问localhost/consumer/queryById/8
时,因为id为8不存在,所以会出现异常,这样就调用了我们备用的方法
什么是服务降级
服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。比如电商平台,在针对618、双11等高峰情形下采用部分服务不出现或者延时出现的情形。
代码实现
在springcloud-api
模块中,新建一个类DeptClientServiceFallbackFactory
,实现FallbackFactory
接口,并添加注解@Component
,将该类放入IOC中,实现其中的方法
// 降级!
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long deptno) {
return new Dept()
.setDeptno(deptno)
.setDname("id==>"+deptno+",没有对应的信息,客户端提供了降级的信息,这个服务现在已经被关闭")
.setDb_source("没有数据");
}
@Override
public List<Dept> queryAll() {
// ...
return null;
}
@Override
public boolean addDept(Dept dept) {
// ...
return false;
}
};
}
}
在DeptClientService
接口上,添加注解@FeignClient(fallbackFactory = DeptClientServiceFallbackFactory.class)
@FeignClient(value = "SPIRNGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
@Service
public interface DeptClientService {
@GetMapping("/dept/queryById/{deptno}")
public Dept queryById(@PathVariable("deptno") Long deptno);
@GetMapping("/dept/queryAll")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public boolean addDept(@RequestBody Dept dept);
}
在springcloud-consumer-dept-feign
模块的yaml中配置,开启降级服务
feign:
hystrix:
enabled: true
测试
在访问该服务时,关闭该服务后,会出现提示
服务熔断与服务降级对比
新建一个模块springcloud-consumer-hystrix-dashboard
,导入依赖
<dependencies>
<dependency>
<groupId>cn.codeweigroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
配置application.yaml
server:
port: 9001
创建一个启动类,并添加注解@EnableHystrixDashboard
,开启监控
@SpringBootApplication
@EnableHystrixDashboard
public class ConsumerDashBoard {
public static void main(String[] args) {
new SpringApplication().run(ConsumerDashBoard.class,args);
}
}
要保证我们的服务者模块都要有spring-boot-starter-actuator
依赖和hystrix
依赖,来完成监控
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.2.3.RELEASEversion>
dependency>
只启动该监控服务,访问localhost:9001/hystrix
如果想要一个服务被监控,我们需要加一个Bean
在spirngcloud-provider-dept-8081
模块,主启动类中,添加
// 启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class DeptProvider_8081 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8081.class,args);
}
// 增加一个Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
测试
启动注册中心,启动流量监控,启动该8081服务
访问localhost:9001/hystrix
,将localhost:8081/actuator/hystrix.stream
,添加到监控
会自动跳转到监控页面
什么是Zuul?
Zuul包含了对请求的路由和过滤两个最主要的功能
Zuul包含了对请求的路由和过滤两个最主要的功能。其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时还可以从Eureka中获取其他微服务的消息,以后访问微服务都是通过Zuul跳转后获得的。
注意:Zuul服务最终还是会注册进Eureka
提供:代理、路由、过滤三大功能!
代码实现
新建一个模块springcloud-zuul-9527
导入依赖
<dependencies>
<dependency>
<groupId>cn.codeweigroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
<version>2.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
新建application.yaml,进行配置
server:
port: 9527
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: zuul9527
prefer-ip-address: true # 显示出ip
info:
app.name: springcloud-study
author.name: codewei
app.company: codewei.cn
app.version: 1.0
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
ignored-services: "*" # 不能再使用这个路径访问了 ignored: 忽略,隐藏全部的
prefix: /codewei # 添加统一的访问前缀
新建一个主启动类
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理。
SpringCloud Config可以从github上获取配置服务信息,为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
SpringCloud Config分为服务端和客户端两部分。
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
SpringCloud config分布式配置中心能干嘛
集中管理配置文件
不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
运行期间动态调整配置,不再需要每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息。
当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
将配置信息以REST接口的形式暴露
SpirngCloud config分布式配置中心与github整合
由于SpringCloud Config默认使用GIT来存储配置文件(也支持SVN和本地文件),但最推荐的还是git,而且使用http/https访问的形式。
在码云新建一个仓库springcloud-config
在电脑中,某一个文件夹下,右键打开Git Bash Here,将我们刚才创建的仓库克隆下来,遇到问题,输入yes
在克隆下来的项目目录下,新建一个文件application.yaml
,在该文件中,写一些简单的配置
spring:
profiles:
active:dev
---
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
将我们刚写好的application.yaml
提交到码云上
git add .
git status
git commit -m "描述"
git push origin master
这样,就成功提交到了远程仓库
新建一个moudlespringcloud-config-server-3344
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
<version>2.2.3.RELEASEversion>
dependency>
新建application.yaml,编写配置
server:
port: 3344
spring:
application:
name: springcloud-config-server
# 连接远程仓库
cloud:
config:
server:
git:
uri: https://gitee.com/code_weic/springcloud-config.git # HTTPS的 不是SSH
编写主启动类,并添加注解@EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class,args);
}
}
启动该模块,访问localhost:3344/application.yaml
就可以访问到我们上传到码云中的配置了
在我们克隆下来的项目目录下,新建一个文件config-client.yaml
,写一些配置
spring:
profiles:
active: dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: spirngcloud-provider-dept
# 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:
profiles: test
application:
name: spirngcloud-provider-dept
# Eureka 的配置
eureka:
client:
service-url: # 指定注册到哪里
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
将该配置文件push到远程仓库
这样我们的的文件就提交到了远程仓库
新建一个模块springcloud-config-client-3355
,导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
<version>2.2.3.RELEASEversion>
dependency>
新建配置文件bootstrap.yaml
,这也是springboot的配置文件
bootstrap.yaml和application.yaml的区别:
bootstrap.yaml
spring:
cloud:
config:
name: config-client # 需要从git上读取的资源名称,不要后缀
uri: http://localhost:3344
profile: dev
label: master # 指定分支
application.yaml
spring:
application:
name: springcloud-config-client-3355
写一个Controller接口
@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 "applicationNmae:" + applicationName
+"\teurekaServer:"+eurekaServer
+"\tport"+ port;
}
}
创建主启动类
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class,args);
}
}