文章目录
一、SpringCloud介绍
1.1 微服务架构
微服务架构的提出者:马丁福勒
https://martinfowler.com/articles/microservices.html
简而言之,微服务架构样式[1]是一种将单个应用程序开发为一组小服务的方法,每个小服务都在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以由全自动部署机制独立部署。这些服务的集中管理几乎没有,它可以用不同的编程语言编写并使用不同的数据存储技术。
1、 微服务架构只是一个样式,一个风格。
2、 将一个完成的项目,拆分成多个模块去分别开发。
3、 每一个模块都是单独的运行在自己的容器中。
4、 每一个模块都是需要相互通讯的。 Http,RPC,MQ。
5、 每一个模块之间是没有依赖关系的,单独的部署。
6、 可以使用多种语言去开发不同的模块。
7、 使用MySQL数据库,Redis,ES去存储数据,也可以使用多个MySQL数据库。
总结:将复杂臃肿的单体应用进行细粒度的划分,每个拆分出来的服务各自打包部署。
1.2 SpringCloud介绍
SpringCloud是微服务架构落地的一套技术栈。
SpringCloud中的大多数技术都是基于Netflix公司的技术进行二次研发。
SpringCloud的中文社区网站:http://springcloud.cn/
SpringCloud的中文网:http://springcloud.cc/
八个技术点:
- Eureka - 服务的注册与发现
- Robbin - 服务之间的负载均衡
- Feign - 服务之间的通讯
- Hystrix - 服务的线程隔离以及断路器
- Zuul - 服务网关
- Stream - 实现MQ的使用
- Config - 动态配置
- Sleuth - 服务追踪
二、服务的注册与发现-Eureka【重点
】
2.1 引言
Eureka就是帮助我们维护所有服务的信息,以便服务之间的相互调用
Eureka |
---|
![]() |
2.2 Eureka的快速入门
2.2.1 创建EurekaServer
创建一个SPringBoot父工程,并且在父工程中指定SpringCloud的版本,并且将packaing修改为pom
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
创建eureka的server,创建SpringBoot工程,并且导入依赖,在启动类中添加注解,编写yml文件
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
启动类添加注解
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
编写yml配置文件
server:
port: 8761 # 端口号
eureka:
instance:
hostname: localhost # localhost
client:
# 当前的eureka服务是单机版的
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
2.2.2 创建EurekaClient
创建Maven工程,修改为SpringBoot
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在启动类上添加注解
@SpringBootApplication
@EnableEurekaClient
public class CustomerApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerApplication.class,args);
}
}
编写配置文件
# 指定Eureka服务地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
#指定服务的名称
spring:
application:
name: CUSTOMER
2.2.3 测试Eureka
创建了一个Search搜索模块,并且注册到Eureka
# 指定Eureka服务地址 eureka: client: service-url: defaultZone: http://localhost:8761/eureka #指定服务的名称 spring: application: name: SEARCH
使用到EurekaClient的对象去获取服务信息
@Autowired
private EurekaClient eurekaClient;
正常RestTemplate调用即可
@GetMapping("/customer")
public String customer(){
//1. 通过eurekaClient获取到SEARCH服务的信息
InstanceInfo info = eurekaClient.getNextServerFromEureka("SEARCH", false);
//2. 获取到访问的地址
String url = info.getHomePageUrl();
System.out.println(url);
//3. 通过restTemplate访问
String result = restTemplate.getForObject(url + "/search", String.class);
//4. 返回
return result;
}
2.3 Eureka的安全性
实现Eureka认证
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
编写配置类
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 忽略掉/eureka/**
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
编写配置文件
# 指定用户名和密码
spring:
security:
user:
name: root
password: root
其他服务想注册到Eureka上需要添加用户名和密码
eureka:
client:
service-url:
defaultZone: http://用户名:密码@localhost:8761/eureka
2.4 Eureka的高可用
如果程序的正在运行,突然Eureka宕机了。
如果调用方访问过一次被调用方了,Eureka的宕机不会影响到功能。
如果调用方没有访问过被调用方,Eureka的宕机就会造成当前功能不可用。
搭建Eureka高可用
准备多台Eureka
让服务注册到多台Eureka
eureka:
client:
service-url:
defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
让多台Eureka之间相互通讯
eureka:
client:
registerWithEureka: true # 注册到Eureka上
fetchRegistry: true # 从Eureka拉取信息
serviceUrl:
defaultZone: http://root:root@localhost:8762/eureka/
2.5 Eureka的细节
EurekaClient启动时,将自己的信息注册到EurekaServer上,EurekaSever就会存储上EurekaClient的注册信息。
当EurekaClient调用服务时,本地没有注册信息的缓存时,去EurekaServer中去获取注册信息。
EurekaClient会通过心跳的方式去和EurekaServer进行连接。(默认30sEurekaClient会发送一次心跳请求,如果超过了90s还没有发送心跳信息的话,EurekaServer就认为你宕机了,将当前EurekaClient从注册表中移除)
eureka:
instance:
lease-renewal-interval-in-seconds: 30 #心跳的间隔
lease-expiration-duration-in-seconds: 90 # 多久没发送,就认为你宕机了
EurekaClient会每隔30s去EurekaServer中去更新本地的注册表
eureka:
client:
registry-fetch-interval-seconds: 30 # 每隔多久去更新一下本地的注册表缓存信息
Eureka的自我保护机制,统计15分钟内,如果一个服务的心跳发送比例低于85%,EurekaServer就会开启自我保护机制
- 不会从EurekaServer中去移除长时间没有收到心跳的服务。
- EurekaServer还是可以正常提供服务的。
- 网络比较稳定时,EurekaServer才会开始将自己的信息被其他节点同步过去
eureka:
server:
enable-self-preservation: true # 开启自我保护机制
CAP定理,C - 一致性,A-可用性,P-分区容错性,这三个特性在分布式环境下,只能满足2个,而且分区容错性在分布式环境下,是必须要满足的,只能在AC之间进行权衡。
如果选择CP,保证了一致性,可能会造成你系统在一定时间内是不可用的,如果你同步数据的时间比较长,造成的损失大。
Eureka就是一个AP的效果,高可用的集群,Eureka集群是无中心,Eureka即便宕机几个也不会影响系统的使用,不需要重新的去推举一个master,也会导致一定时间内数据是不一致。
三、服务间的负载均衡-Ribbon【重点
】
3.1 引言
Robbin是帮助我们实现服务和服务负载均衡,Robbin属于客户端负载均衡
客户端负载均衡:customer客户模块,将2个Search模块信息全部拉取到本地的缓存,在customer中自己做一个负载均衡的策略,选中某一个服务。
服务端负载均衡:在注册中心中,直接根据你指定的负载均衡策略,帮你选中一个指定的服务信息,并返回。
Robbin |
---|
![]() |
3.2 Ribbon的快速入门
启动两个search模块 vm options设置值为-Dserver.port=8082
在customer导入robbin依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
配置整合RestTemplate和Robbin
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
在customer中去访问search
@GetMapping("/customer")
public String customer(){
String result = restTemplate.getForObject("http://SEARCH/search", String.class);
//4. 返回
return result;
}
3.3 Ribbon配置负载均衡策略
负载均衡策略
- RandomRule:随机策略
- RoundRobbinRule:轮询策略
- WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重
- BestAvailableRule:根据被调用方并发数最小的去分配
采用注解的形式,所有的服务都采用这个负载均衡策略
@Bean
public IRule robbinRule(){
return new RandomRule();
}
配置文件去指定负载均衡的策略(推荐)
# 指定具体服务的负载均衡策略
SEARCH: # 编写服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule # 具体负载均衡使用的类
四、服务间的调用-Feign【重点
】
4.1 引言
Feign可以帮助我们实现面向接口编程,就直接调用其他的服务,简化开发。
4.2 Feign的快速入门
customer导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Customer启动类上添加一个注解
@EnableFeignClients
customer项目创建一个client包,创建一个接口,并且和Search模块做映射
@FeignClient("SEARCH") // 指定服务名称
public interface SearchClient {
// value -> 目标服务的请求路径,method -> 映射请求方式
@RequestMapping(value = "/search",method = RequestMethod.GET)
String search();
}
controller测试使用
@Autowired
private SearchClient searchClient;
@GetMapping("/customer")
public String customer(){
String result = searchClient.search();
return result;
}
fegin底层使用了ribbon作为负载均衡
SEARCH:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
4.3 Feign的传递参数方式
注意事项
- 如果你传递的参数,比较复杂时,默认会采用POST的请求方式。
- 传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,不要省略value属性
- 传递对象信息时,统一采用json的方式,添加@RequestBody
- Client接口必须采用@RequestMapping
在Search模块下准备三个接口
@GetMapping("/search/{id}")
public Customer findById(@PathVariable Integer id){
return new Customer(