SpringCloud
简介
通常来说微服务架构是一种架构模式,或者说是一种架构风格,它提供将单一的应用程序划分成一组小的服务,每个服务运行在自己独立的进程中,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制互相沟通,每个服务都围绕着具体的业务进行构建,并且能够独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
微服务的核心就是将传统的一站式应用,根据业务分拆一个个的服务,彻底的去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。
微服务与微服务架构
微服务
强调的是服务的大小,他关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应用,狭义的看,可以看作是IDEA的一个个微服务工程,或者moudel;
IDEA工程中使用Maven开发一个个独立的Moudel,它具体是用SpringBoot开发一个小模块,专业的事交给专业的模块去做,一个模块专注做一件事。
微服务架构
一种新的架构形式,Martin Fowler。2014年提出。
微服务架构是一种架构模式,它提倡将单一的应用程序划分成一组小的服务,服务之间互相协调,互相配合,为用户提供最终价值,,每个服务独立运行在进程中,服务之间采用轻量级的通信机制互相协作,每个服务都围绕着具体的业务进行构建,并且能够独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建。
微服务的的优缺点
优点:
- 每个服务足够内聚,足够小,代码容易理解,这样可以焦距一个指定的业务功能或业务需求。
- 开发简单,开发效率高,一个服务专注一件事,单一职责。
- 微服务是松耦合的,有功能意义的服务,无论在开发阶段还是部署阶段都是独立的。
- 微服务能够使用不同的语言开发。
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo。
- 微服务易于开发人员理解,修改和和维护。
- 微服务只是业务逻辑代码,不会和 CSS,HTML 或其它界面混合。
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有同一的数据库。
缺点:
- 开发人员要处理分布式系统的复杂性。
- 多服务运维难度,随着服务的增加,运维的压力也在增大。
- 服务间通信成本。
- 数据一致性。
- 系统集成测试。
- 性能监控。
微服务技术栈
微服务条目 | 技术 |
---|---|
服务开发 | SpringBoot,Spring,SpringMVC |
服务配置与管理 | NetFlix公司的 Archaius,阿里的Diamond等 |
服务注册与发现 | Eureka,Consul,Zookeeper等 |
服务调用 | Rest,RPC,gRPC |
服务熔断器 | Hystrix,Envoy等 |
负载均衡 | Ribbon,Nginx等 |
服务接口调用(客户端调用接口的简化工具) | Feign等 |
消息队列 | Kafka,RabbitMQ,ActiveMq等 |
服务配置中心管理 | SpringCloudConfig,Chef等 |
服务路由(API网关) | Zuui等 |
服务监控 | Zabbix,Nagios,Metrics,Specatator等 |
全链路追踪 | Zipkin,Brave,Dapper等 |
服务部署 | Docker,OpenStack,Kubernetes等 |
数据流操作开发包 | SpringCloud Stream(封装与Redis,Kafka等发送接收消息) |
事件消息总线 | SpringCloud Bus |
为什么选择SpringCloud作为微服务架构
选型依据
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
当前各大IT公司的微服务架构有哪些
- 阿里:dubbo+HFS
- 京东:JSF
- 新浪:Motan
- 当当:DubboX
- ......
各微服务框架对比
功能点/服务架构 | Netflix/SpringCloud | Motan | gRPC | Thrift | Dubbo/DubboX |
---|---|---|---|---|---|
功能定位 | 完整的微服务架构 | RPC框架。但整合了ZK或Consul,实现了集群环境的基本服务注册/发现 | RPC框架 | RPC框架 | 服务框架 |
支持Rest | 是,Ribbon支持多种可拔插的序列化选择 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是(Hession2) | 是 | 是 | 是 |
支持多语言 | 是(Rest形式) | 否 | 是 | 是 | 否 |
负载均衡 | 是(服务端zuul+客户端Ribbon),zuul服务,动态路由,云端负载均衡Eureka(针对中间层服务器) | 是(客户端) | 否 | 是 | 是(客户端) |
配置服务 | Netflix Archaius,SpringCloud Config Server集中配置 | 是(zookeeper提供) | 否 | 否 | 否 |
服务调用链监控 | 是(zuul),zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
高可用/容错 | 是(服务器Hystrix+客户端Rubbon) | 是(客户端) | 否 | 否 | 是(客户端) |
典型应用案例 | Netflix | Sina | |||
社区活跃程度 | 高 | 一般 | 高 | 一般 | 2017年后重新开始维护,中断了5年 |
学习难度 | 中 | 低 | 高 | 高 | 低 |
文档丰富程度 | 高 | 一般 | 一般 | 一般 | 高 |
其它 | SpringCloud Bus为我们的应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践的公司比较多 |
-
Spring Cloud Netflix 一站式解决方案
api网关,zuul组件
Feign --- HttpClint --- Http通信方式,同步,阻塞
服务注册发现:Eurake
熔断机制:Hystrix
.....
-
Apache Dubbo Zookeeper 半自动,需要整合别人的
api:找第三方插件,或自己实现
Dubbo
Zookeeper
借助Hystrix
(不完善)
-
Spring Cloud Alibaba 一站式解决方案
新概念:服务网格--Server Mesh
istio
万变不离其宗:
- API
- Http,RPC
- 注册与发现
- 熔断机制
什么是SpringCloud
SpringCloud基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于Netflix开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot开发的便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,熔断器。路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,它们都可以用SpringBoot的开发风格做到一键启动和部署。
SpringBoot没有重复造轮子,它只是将目前各个公司开发的比较成熟的,经得住实际考验的服务框架组合起来,通过SpringBoot的风格进行再封装,屏蔽了复杂的配置和实现原理,最终给开发者留出了一条简单易懂,易部署和易维护的分布式系统开发工具包。
SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。
SpringBoot和SpringCloud的关系
- SpringBoot专注于快速方便的开发单个个体微服务。
- SpringCloud是关注全局的微服务协调整治治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,熔断器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等集成微服务。
- SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
- SpringBoot专注于快速,方便地开发单个个体微服务,SpringCloud关注全局的服务治理框架。
Dubbo和SpringCloud技术选型
Dubbo | Spring Cloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eurake |
服务调用方式 | 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 |
最大区别:Spring Cloud抛弃了Dubbo的RPC通信,采用的是基于Http的REST方式
严格来说,这两种方式各有优劣,虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题,而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。
Dubbo定位的是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案。
Spring Cloud特性
- Distributed/versioned configuration(分布式/版本化配置)
- Service registration and discovery(服务注册和发现)
- Routing(路由)
- Service-to-service calls(服务到服务的调用)
- Load balancing(负载均衡)
- Circuit Breakers(熔断器)
- Global locks(全局锁)
- Distributed messaging(分布式消息传递)
- .....
参考学习:
- Spring官网
- SpringCloud中文API文档
- SpringCloud中国社区
- SpringCloud中文网
SpringCloud学习环境搭建
项目结构目录(eureka是下一节)
数据库db1
springcloud下pom.xml
<!--打包方式-->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.18.16</lombok.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<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>
<!--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.21</version>
</dependency>
<!-- spring boot启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
springcloud-api
Dept
package com.zr.springcloud.pojo;
import java.io.Serializable;
@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;
}
}
pom.xml
<!--当前的moudle自己需要的依赖,如果父依赖中配置了版本,这里就不用写了-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
springcloud-provider-dept-8001
结构
pom.xml
<dependencies>
<!--要拿到实体类,需要配置api.module-->
<dependency>
<groupId>com.zr</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 8001
mybatis:
type-aliases-package: com.zr.springcloud.pojo
config-location: classpath:mybatis/mybatis.config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
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=utf-8
username: root
password: 123456
mybatis.config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
DeptDao
package com.zr.springcloud.dao;
@Mapper
@Repository
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
DeptMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zr.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>
DeptService
package com.zr.springcloud.service;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
DeptServiceImpl
package com.zr.springcloud.service;
@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
package com.zr.springcloud.controller;
//提供Restful服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List queryAll(){
return deptService.queryAll();
}
}
DeptProvider_8001
package com.zr.springcloud;
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
springcloud-consumer-dept
pom.xml
<dependencies>
<dependency>
<groupId>com.zr</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 80
ConfigBean
package com.zr.springcloud.config;
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
DeptConsumerController
package com.zr.springcloud.controller;
@RestController
public class DeptConsumerController {
//消费者 ,不应该有servicer层
// RestTemplate 供我们直接调用就可以了
@Autowired
private RestTemplate restTemplate; //提供多种便捷访问远程http的方法,简单的restful服务模板
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);
}
@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);
}
}
DeptConsumer_80
package com.zr.springcloud;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
按顺序启动服务,访问localhost:8080/consumer/dept/list等
Eureka服务注册与发现
Neatflix在设计Eureka时,遵循的是AP原则。
Eureka是Netflix的一个子模块,也是核心模块之一,Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于dubbo的注册中心,比如Zookeeper。
原理讲解
Eureka的基本架构:
- SpringCloud封装了Netflix公司开发的Eureka模块来实现服务注册与发现(对比Zookeeper)
- Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,它是注册服务中心
- 而系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接,这样系统的维护人员就可以通过EurekaServer来监控系统中的各个微服务是否正常运行,SpringCloud的一些其它模块(比如Zuul)就可以通过EurekaServer来发现系统中的其它微服务,并执行相关的逻辑。
- Eureka包含两个组件:Eureka Server和 Eureka Client
- Eureka Server提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用节点服务的信息,服务节点的信息可以在界面中直观的看到。
- Eureka Client是一个Java客户端,用于简化Eureka Server交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器,在应用启动后,会向Eureka Server发送心跳(默认为30秒),如果Eureka Server在多个心跳内没有收到某个节点的信息,Eureka Server会从服务注册表中将这个服务节点移除(默认周期为90秒)。
Eureka Server:提供服务注册与发现,zookeeper
Server Provider:将自身的服务注册到Eureka中,从而使消费者找到
Server Consumer:服务消费方从Eureka中获取注册服务列表,从而找到消费者
测试:在springcloud项目下创建model:springcloud-eureka-7001
springcloud-eureka-7001
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 7001
eureka:
instance:
hostname: localhost #服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false #如果为false,则表示自己为注册中心
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
EurekaServer_7001
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaServer //服务端的启动类,可以接受别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
启动,访问http://localhost:7001/ ,如下图所示
修改springcloud-provider-dept-8001
pom.xml添加
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml中增加
#eureka的配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-8001 #修改eureka默认的描述信息
info:
app.name: zhou-springcloud
company.name: cnblogs.com/zhou-zr
DeptController中增加(可以不加),增加后访问可获取一些信息
@GetMapping("/dept/discovery")
//注册进来的微服务,获取一些信息
public Object discovery(){
//获得微服务列表的清单
List<String> services = client.getServices();
System.out.println("discoveryService"+services);
//得到一个具体微服务的信息 通过具体的微服务id来取:applicationName
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println("ServiceInstance"+instance.getHost()+" "+
instance.getPort()+" "+
instance.getUri()+" "+
instance.getServiceId()
);
}
return this.client;
}
DeptProvider_8001中增加两个注解
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中
@EnableDiscoveryClient //服务发现
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
先启动eureka再启动provider访问http://localhost:7001/,如下所示
自我保护机制
默认情况下,如果EurekaServer在一定的时间内没有收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒),但是当网络发生故障时,微服务与Eureka之间无法正常通信,此时不应该注销这个服务,Eurake通过自我保护机制来解决这个问题,当EurekaServer在短时间内失去过多客户端时,那么这个节点就会进入自我保护模式。EurekaServer会保护服务注册表中的信息,不在删除服务注册表中的信息,当网络故障恢复后,收到服务心跳后,EurekaServer会自动退出自我保护模式。
自我保护模式是一种应对网络安全的保护措施,把所有的服务保存(健康的和不健康的),使用自我保护机制,是Eureka的集群更加健壮和稳定。
在SpringCloud中,可以使用eureka.server.enable.self.preservation = false 禁用自我保护模式【不建议关闭】。
集群环境配置
进入C:WindowsSystem32driversetchosts
添加
127.0.0.1 eureka7001.com
127.0.0.1 eureka7003.com
127.0.0.1 eureka7005.com
修改springcloud-eureka-7001
application.yml
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false #如果为false,则表示自己为注册中心
service-url: #监控页面
#单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#关联: http://eureka7003.com:7003/eureka/,http://eureka7005.com:7005/eureka/
defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7005.com:7005/eureka/
新建springcloud-eureka-7003 Model
pom.xml与7001相同
application.yml
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false #如果为false,则表示自己为注册中心
service-url: #监控页面
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7005.com:7005/eureka/
EurekaServer_7003
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaServer //服务端的启动类,可以接受别人注册进来
public class EurekaServer_7003 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7003.class,args);
}
}
新建springcloud-eureka-7005 Model
pom.xml与7001相同
application.yml
server:
port: 7005
eureka:
instance:
hostname: eureka7005.com #服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false #如果为false,则表示自己为注册中心
service-url: #监控页面
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
EurekaServer_7005
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaServer //服务端的启动类,可以接受别人注册进来
public class EurekaServer_7005 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7005.class,args);
}
}
更改springcloud-provider-dept-8001的注册地址 defaultZone:
#eureka的配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7005.com:7005/eureka/
instance:
instance-id: springcloud-provider-8001 #修改eureka默认的描述信息
先启动7001,7003,7005,再启动springcloud-provider-dept-8001
随意访问7001,7003,7005,
CAP原则及对比Zookeeper
RDBMS(Mysql,Oracle,sqlServer)===>ACID
NoSQL(redis,mongdb)===>CAP
CAP:
- C(Consistency):强一致性
- A(Availability):可用性
- P(Partition tolerance):分区容错性
CAP的三进二:CA,CP,AP
CAP理论的核心:
- 一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。
- 根据CAP原理:将NoSQl数据库分成了满足CA原则,满足CP原则和满足AP原则三大类。
- CA:单点集群,满足一致性,可用性的系统,通常可拓展性较差。
- CP:满足一致性,分区容错性的系统,通常性能不是特别高。
- AP:满足可用性,分区容错性的系统,通常可能对一致性要求低一些。
作为服务注册中心,Eureka比Zookeeper好在哪里?
一个分布式系统不可能同时满足CAP,由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间权衡。
- Zookeeper保证的是CP
- Eureka保证的是AP
Zookeeper保证的是CP:
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接收服务直接Down掉不可用,也就是说,服务注册功能的可用性要求高于一致性,但是Zk会出现这样一种情况,当master节点因网络故障与其它节点失去联系时,剩余节点会重新进行leader选举,问题在于,选举的时间太长,30-120s,且选举期间整个Zk集群是不可用的,这就导致在选举期间注册服务瘫痪,在云部署环境下,因为网络问题使Zk集群失去master节点是大概率的事件,虽然服务器能够恢复,但是在漫长的选举过程导致的注册长期不可用是不能容忍的。
Eureka保证的是AP:
Eureka就避开了这一点,因此在设计时就优先保证可用性,Eurake各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点仍然可以提供注册服务和查询服务,而Eureka的客户端在向某个Eureka注册时,如果发生连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保证服务注册的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有自我保护机制。如果在15分钟内,超过85%的节点都没有正常的心跳,那么Eureka就会认为客户端和注册中心发生了网络故障,会出现以下几种情况:
- Eureka不再从注册列表中移除长时间没收到心态而过期的服务
- Eureka仍然能够接收新服务的注册和查询功能,但是不会同步到其他节点上(即保证当前节点仍然可用)
- 当网络稳定时,当前实例新的注册信息会被被同步到其它节点中
因此:Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个注册服务瘫痪。
负载均衡及Ribbon
SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon是Netflix开发的项目,主要是提供客户端软件的负载均衡算法,将Netflix的中间层服务连接在一起,Ribbon的客户端组件提供一系列的配置项如:连接超时,重试等等,简单的说,就是在配置文件中列出LoadBalancer(简称:LB,负载均衡)后面所有的机器,Ribbon会自动地帮助你基于某种规则(简单轮询,随机查询等)去连接这些机器,我们也很容易实现Ribbon自定义的负载均衡算法。
Ribbon能干嘛
- LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
- 负载均衡简单的说就是将用户的请求平均分配到多个服务上,从而达到系统的HA(高可用)。
- 常见的负载均衡软件有Nginx,Lvs等
- dubbo,SpringCloug中都给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
- 负载均衡简单分类:
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施,如Ngnix,由该设施负责把访问请求通过某种策略转发至服务的提供方
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获得有哪些地址可用,然后自己从这些地址中选出一个合适的服务器
- Ribbon就属于进程式LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址。
- 集中式LB
服务端集成Ribbon
修改springcloud-consumer-dept
pom.xml中增加
<!--集成Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
application.yml中增加
#Eureka
eureka:
client:
register-with-eureka: false #不向Eureka中注册自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7005.com:7005/eureka/
修改DeptConsumerController
//通过ribbon实现的时候我们这里的地址应该是变量,通过服务名来访问
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
修改ConfigBean
package com.zr.springcloud.config;
@Configuration
public class ConfigBean {
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced //Ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
DeptConsumer_80中增加注解@EnableEurekaClient
package com.zr.springcloud;
//Ribbon和 Eureka整合以后,客户端可以直接调用,不用关心ip地址和端口号
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
启动7001,7003,7005三个服务注册中心,再启动springcloud-provider-dept-8001,最后启动springcloud-consumer-dept
访问:http://localhost/consumer/dept/list
使用Ribbon实现负载均衡
创建数据库db02,db03与环境搭建中的db01相同,修改db_source为对应数据库名
项目结构:
如图,建立三个服务提供者的model,复制springcloud-provider-dept-8001的内容到8003和8005,修改其application.yml的对应端口(三个提供者application.name相同)及其主启动类对应的数据库名。
启动三个Eureka服务注册中心,再启动三个服务提供者,最后启动消费者,访问http://localhost/consumer/dept/list,多次访问,可以发现数据是从db01,db02,db03依次读取的,默认的是轮询算法。
自定义负载均衡算法
可进入IRule接口查看它的实现类,查看定义好的负载均衡算法(默认的是RoundRobinRule,轮询算法)。
自定义负载均衡算法:
在zr下新增myrule包(不能放在主启动类的同级包下,同级包下会被扫描到,具体原因可参考Ribbon文档)
DiyRule (复制RandomRule修改部分代码,使其满足我们的要求,每个服务访问5次,换下一个服务)
package com.zr.myrule;
public class DiyRule extends AbstractLoadBalancerRule {
//每个服务访问5次,换下一个服务
//total=0 默认等于0 如果等于5 指向下一个服务节点
//index=0 total=5时 index+1
private int total = 0; //被调用的次数
private int currentIndex = 0; //当前是谁在提供服务
public DiyRule() {
}
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers(); //获得活着的服务
List<Server> allList = lb.getAllServers(); //获得全部的服务
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// int index = this.chooseRandomInt(serverCount); //生成区间随机数
// server = (Server)upList.get(index); //从活着的服务随机获取一个
//自定义
if (total<5){
server = upList.get(currentIndex);
total++;
}else {
total = 0;
currentIndex++;
if (currentIndex>upList.size()-1){
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) {
}
}
MyRule(调用自定义算法)
package com.zr.myrule;
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new DiyRule(); //自定义的rule
}
}
DeptConsumer_80增加注解RibbonClient
package com.zr.springcloud;
//Ribbon和 Eureka整合以后,客户端可以直接调用,不用关心ip地址和端口号
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = DiyRule.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客户端。
只需要创建一个接口,然后添加注解即可。
Feign:主要是社区,大家都习惯面向接口编程,这个是很多开发人员的规范 ,调用微服务访问两种方法
- 微服务名字【Ribbon】
- 接口和注解【Feign】
Fegin能干什么?
- Feign旨在使编写 Java Http客户端变得更容易
- 前面使用Ribbon+RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对某个微服务自行封装一些客户端类来包装这些依赖服务的调用,所以,Feign在此基础上做了进一步的封装,由它来帮助我们实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上标注一个Fegin注解即可)。即可完成对服务提供方的接口绑定,简化了使用SpringCloud Ribbon时,自动封装服务调用客户端的开发量。
Fegin集成了Ribbon
- 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而且简单的实现了服务调用。
代码实现:springcloud-api下新增一个包service
DeptClientService
package com.zr.springcloud.service;
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
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 add(Dept dept);
}
复制springcloud-consumer-dept到springcloud-consumer-dept-feign(myrule包可以不用复制)
pom.xml(springcloud-api和springcloud-consumer-dept-feign都增加)
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
修改DeptConsumerController
package com.zr.springcloud.controller;
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService service = null;
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return this.service.add(dept);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return this.service.queryById(id);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return this.service.queryAll();
}
}
FeginDeptConsumer_80
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.zr.springcloud"})
@ComponentScan("com.zr.springcloud")
public class FeginDeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeginDeptConsumer_80.class,args);
}
}
启动Eureka注册中心,再启动provider服务提供者,最后启动springcloud-consumer-dept-feign测试。
访问http://localhost/consumer/dept/list。
Hystrix
分布式系统的问题:
服务雪崩:多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的”扇出“,如果扇出的链路上某个微服务调用时间过长或不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是雪崩效应。
对于高流量应用来说,单一的后端依赖可能会导致所有服务器上的所有资源在几秒钟内饱和,比失效更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其它系统资源紧张,导致整个系统发生更多的连级故障,这些都表示需要对故障和延迟进行隔离和管理,以使单个依赖关系的失败,不能取消整个应用程序或系统。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免地会调用失败,比如超时,异常等。Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失效,避免级联故障,以提高分布式系统的弹性。
”断路器“本身是一种开关装置,当整个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或弹出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间不必要的占用,从而避免了故障在分布式系统中的蔓延乃至雪崩。
Hystrix:
- 服务降级
- 服务熔断
- 服务限流
- 接近实时的额监控
- ...
服务熔断
熔断机制是对应雪崩效应的一种微服务链路保护机制。
当扇出的某个微服务不可用或响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息,当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过 Hystrix 实现,Hystrix会监控微服务调用的状态,当失效的调用到一定的阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是 @HystrixCommand。
测试代码:
复制springcloud-provider-dept-8001 到 springcloud-provider-dept-hystrix-8001
pom.xml添加
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
修改DeptController
package com.zr.springcloud.controller;
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("id==>"+id+"不存在该用户,信息无法找到");
}
return dept;
}
//备选方法
public Dept hystrixGet(@PathVariable("id") Long id){
return new Dept().setDeptno(id)
.setDname("id==>"+id+"没有对应的信息:null--hystrix")
.setDb_source("no this database in MySQL");
}
}
DeptProviderHystrix_8001主启动类添加EnableCircuitBreaker
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
启动Eureka服务注册中心,再启动springcloud-provider-dept-hystrix-8001,再启动springcloud-consumer-dept
访问一个数据库中不存在的id,http://localhost/consumer/dept/get/8
服务降级
springcloud-api中修改
增加DeptClientServiceFallbackFactory
package com.zr.springcloud.service;
//降级~
@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 add(Dept dept) {
return false;
}
};
}
}
springcloud-consumer-dept-fegin的application.yml添加配置开启降级
#开启降级fegin.hystrix
feign:
hystrix:
enabled: true
启动Eureka服务注册中心,再启动一个服务提供者,如springcloud-provider-dept-8001,最后启动springcloud-consumer-dept-fegin
访问http://localhost/consumer/dept/get/1可得到正确结果,当关闭服务提供者8001,再访问http://localhost/consumer/dept/get/1
服务熔断:服务器,某个服务超时或者异常,引起熔断,保险丝。
服务降级:客户端,从整体网站请求负载考虑,当某个服务熔断或者关闭后,服务将不被调用,此时在客户端,我们卡哇伊准备一个FallbackFactory,返回一个默认的值(缺省值),整体的服务水平下降了,但是能使用。
Dashboard流监控
新建model , springcloud-consumer-hystrix-dashboard
复制springcloud-consumer-dept的pom依赖,并增加以下
<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>
application.yml
server:
port: 9001
DeptConsumerDashboard_9001
package com.zr.springcloud;
@SpringBootApplication
@EnableHystrixDashboard //开启监控页面
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
修改springcloud-provider-dept-hystrix-8001的启动类,增加一个servlet
package com.zr.springcloud;
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
//增加一个servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
启动springcloud-consumer-hystrix-dashboard监控,启动服务注册中心,启动springcloud-provider-dept-hystrix-8001
http://localhost:9001/hystrix(/actuator/hystrix.stream 流地址)
访问http://localhost:8001/dept/get/3,再访问http://localhost:8001/actuator/hystrix.stream,可以看到
然后在http://localhost:9001/hystrix页面的监控地址填入监控地址http://localhost:8001/actuator/hystrix.stream,如下图
点击Monitor Stream(此时可一直访问http://localhost:8001/dept/get/3,观察下图的变化)
实心圆的颜色代表了健康状态,监控程度从绿色,黄色,橙色,红色递减。
实心圆除了颜色变化外,它的大小根据流量的变大而变大,所以可以通过实心圆的展示,在大量的实例中快速发现故障实例和高压力测试。
Zuul路由网关
Zuul包含了对请求的路由和过滤两个最主要的功能。
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。而过滤功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础,Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获取其它微服务的消息,即以后的访问服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka
提供:代理,路由,过滤三大功能
代码测试
系统host配置文件中增加:127.0.0.1 www.zr.com 【注意】
新建model,springcloud-zuul-9527
复制springcloud-consumer-hystrix-dashboard的pom.xml,并添加zuul依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
application.yml
server:
port: 9527
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7005.com:7005/eureka/
instance:
instance-id: zuul9527.com
prefer-ip-address: true #显示真实ip
info:
app.name: zr-springcloud
company.name: www.cnblogs.com/zhou-zr
zuul:
routes:
mydept.serviceId: springcloud-provider-dept #mydept自己定义
mydept.path: /mydept/** #使用这个路径访问
ignored-services: springcloud-provider-dept #不能再使用这个路径访问了
#ignored-services: "*" #隐藏全部的
# prefix: /zr #设置统一的前缀
ZuulApplication_9527
package com.zr.springcloud;
@SpringBootApplication
@EnableZuulProxy //开启
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class,args);
}
}
启动Eureka服务注册中心,再启动springcloud-provider-dept-hystrix-8001 和 springcloud-zuul-9527
访问http://localhost:7001/,可以看到注册了两个服务
当application.yml中未添加zuul的配置时,访问http://www.zr.com:9527/springcloud-provider-dept/dept/get/5,可以查询。
当application.yml中添加zuul的配置后,访问http://www.zr.com:9527/mydept/dept/get/5查询
注意:未配置ignored-services: springcloud-provider-dept这一行时,以上两个路径都可以查询,配置后,只能通过访问http://www.zr.com:9527/mydept/dept/get/5查询。
SpringCloud config分布式配置
分布式系统面临的配置文件问题:
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的。SpringCloud提供了CongifServer来解决这个问题,我们每一个微服务自己带着一个application.yml,那上百的配置文件修改起来就非常麻烦。
SpringCloud Config为微服务架构中的微服务提供集中式的外部配置支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
SpringCloud Config分为客户端和服务端两部分:
服务端也称为:分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息,这样有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
SpringCloud Config:
- 集中式管理配置文件
- 不同环境,不同配置,动态化的配置更新,分环境部署,比如/dev,/test,/prod,/beta,/release
- 运行期间动态调整配置,不需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时,服务不需要重启,即可根据配置的变化,重新加载新的配置
- 将配置信息以Rest接口的形式暴漏
SpringCloud Config分布式配置中心与github整合
由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,如SVN和本地文件),但最推荐的还是Git,而且使用的是 http/https访问的形式。
Git
本地下载安装 git
# 设置提交代码时的用户信息--global全局生效
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"
# 显示当前的Git配置
$ git config --list
# 添加当前目录的所有文件到暂存区
$ git add .
# 提交暂存区到仓库区
$ git commit -m "[message]"
#提交
git push origin master
#生成密钥 在C盘用户下的 .ssh文件中(id_rsa.pub)
ssh-keygen -t rsa -C "这里换上你的邮箱"
复制密钥,在码云上设置SSH,添加密钥
登录gitee(码云),创建一个springcloud-config仓库
在指定的目录下右击,点击Git Bash Here,下载项目:git clone ssh的地址,
在下载下来的项目中新建application.yml
spring:
profiles:
active: dev
---
spring:
profiles: dev
applicartion:
name: springcloud-config-dev
---
spring:
profiles: test
applicartion:
name: springcloud-config-test
cd springcloud-config/
git add . (加到缓冲区)
git status (查看状态)
git commit -m "first commit"(提交,-m 提交的消息,此时本地提交成功)
git push origin master(提交到远程仓库)
微服务连接Git配置
新建springcloud-config-server-3344的model
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 3344
spring:
application:
name: springcloud-config-server
#连接远程仓库
cloud:
config:
server:
git:
uri: https://gitee.com/zhouruizr/springcloud-config.git
Config_Server_3344
package com.zr.springcloud;
@SpringBootApplication
@EnableConfigServer
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class,args);
}
}
启动springcloud-config-server-3344
访问http://localhost:3344/application-dev.yml(其它访问方式参考Spring Cloud中文文档)
客户端连接服务端访问远程
在下载的springcloud-config中,新建config-client.yml,通过git提交上去
spring:
profiles:
active: dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: springcloud-provider-dept
#eureka的配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: springcloud-provider-dept
#eureka的配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
新建springcloud-config-client-3355如下
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
bootstrap.yml
# 系统级别的配置
spring:
cloud:
config:
name: config-client #需要从git上读取的资源名称 不需要后缀
profile: dev #test
label: master
uri: http://localhost:3344
application.yml
#用户级别的配置
spring:
application:
name: springcloud-config-client
ConfigClientController
package com.zr.springcloud.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 "applicationName:"+applicationName
+"eurekaServer:"+eurekaServer
+"port:"+port;
}
}
Config_Client_3355
package com.zr.springcloud;
@SpringBootApplication
public class Config_Client_3355 {
public static void main(String[] args) {
SpringApplication.run(Config_Client_3355.class,args);
}
}
先启动springcloud-config-server-3344,再启动springcloud-config-client-3355
访问http://localhost:8201/config(端口是git上配置好的,修改bootstrap.yml中的profiles为test即可更换端口为8202)
远程配置实战
将Eureka-7001的配置放到git上,参考客户端连接服务端访问远程的方法配置即可。