系统架构的演变
单体架构
垂直架构
分布式之RPC
分布式之SOA
分布式之微服务
微服务架构简介
微服务:
微:小
服务:
以前面向SOA的时候,服务—>Service层
现在微服务中的服务—>完整的模块对应的独立项目(controller->service->Mapper->domain…)
微服务虽小五脏俱全。
服务间的系统调用
微服务和微服务之间的系统调用:
Socket (Dubbo)
HTTP (微服务–>restful风格)
具有Http远程调用的实现
HttpClient
OkHttp[效率高一些]
RestTemplate:封装HttpClient/OkHttp来实现远程调用
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
restTemplate.getForObject(); //返回数据
restTemplate.getForEntity(); //返回数据+返回头信息
问题:微服务会越来越多,如何管理微服务是目前需要解决的问题。
注册中心 Eureka
远程调用负载均衡 Ribbon
微服务与微服务之间的系统调用 Feign
网关 Gateway
微服务的雪崩问题 Hystrix
微服务的配置文件管理 Config / bus
SpringCloud的概述
SpringCloud不是一个具体的框架,而是很多组件的一个集合,主要的作用用来管理微服务。
Spring/SpringBoot/SpringCloud关系
Eureka
注册中心,
EurekaServer
EurekaClient
如何使用?
(1)pom.xml添加起步依赖
EurekaServer
spring-cloud-starter-netflix-eureka-server
@EurekaClient
spring-cloud-starter-netflix-eureka-client
(2)application.yml添加配置
EurekaServer
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka #EurekaServer的访问地址
register-with-eureka: false #是否要将自己注册到EurekaServer,默认是true
fetch-registry: false #是否从EurekaServer上获取服务列表
EurekaClient
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: true #true是默认值
fetch-registry: true #true是默认值
(3)引导类上添加对应的启动注解
EurekaServer
@EnableEurekaServer
@EurekaClient
@EnableEurekaDiscovery
(4)具体的业务操作
启动即可进行远程调用。
问题:EurekaClient从EurekaServer中获取所需要服务的列表,获取到【服务列表】以后,如何进行远程调用?
RestTemplate
getForObject
getForEntity
url
ServerName---->服务的列表集合—>如何从集合中获取要调用的具体服务地址?
Ribbon
就是微服务列表的负载均衡调用。
负载均衡的算法: 轮询、随机
用到的几率很低,因为有Feign的存在。
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
restTemplate.getForObject(); //返回数据
restTemplate.getForEntity(); //返回数据+返回头信息
url
:“http://”+serverName+"/…"
Hystrix
微服务的雪崩问题。
(1)线程隔离
用来解决雪崩问题,加速访问失败的判定,一旦判断访问失败,就直接走降级服务
(2)熔断保护
close关闭 : 直接访问原服务
open打开 : 降级服务
half open半开 : 允许部分请求访问原服务,访问会得到一个结果(成功[close]、失败[open])
close–>open: 10秒中20次访问中有50%失败,就会状态的转换
open—>half open : 每个5秒
half open -->close /open
A----调用----B
降级服务要编写在微服A上
(1)pom.xml添加起步依赖
(2)application.yml添加配置信息
(3)编写降级服务
org.springframework.cloud spring-cloud-starter-netflix-hystrix微微服A的controller方法中添加下面注解
@HystrixCommand
降级服务:
局部:只针对于某一个方法进行降级
@RequestMapping("/findAll")
@HystrixCommand(fallbackMethod = “queryAll”)
public List findAll(){
return null;
}
//queryAll和findAll方法的参数和返回值要一致。
public List<User> queryAll(){
return null;
}
全局:对当前controller中的所有方法进行降级
类上:@DefaultProperties(defaultFallback = "methodName")
方法上: @HystrixCommand
Feign
常用,作用:实现微服务与微服务之间的远程调用。
编写的是一个接口,用的时候,用的肯定是代理对象的方法, JDK动态代理。 Ribbon + RestTemplate
使用步骤:
(1)pom.xml添加注解
org.springframework.cloud spring-cloud-starter-openfeign (2)application.yml添加配置feign: #开启feign支持Hystrix
hystrix:
enabled: true
(3)引导类添加启动注解
@EnableFeignClients
(4)编写一个远程服务调用的接口
@FeignClient(value = “serverName”)
//serverName是从EurekaServer中获取的,不能乱写
public interface MyFeignClient {
//编写服务的时候,要根据具体调用远程服务的接口文档
//1.调用服务的URL
//2.调用服务的请求方式 GET /POST /DELETE / PUT ....
//3.调用服务的参数
//4.调用服务的返回值
@RequestMapping(name="/url",method = {RequestMethod.POST})
//@GetMapping | @PostMapping | @DeleteMapping | @PutMapping
public Xxx mehtodName(...);
}
(5)需要进行远程调用的位置注入接口[代理类]
@Autowrid
private MyFeignClient myFeignClient;
(6)注入的对象调用方法实现远程服务调用。
Xxx xx = myFeignClient.mehtodName(…);
当获取到返回值以后,就实现了远程调用
Gateway
拦截:
全局过滤器
局部过滤器
路由:配置
(1)pom.xml添加起步依赖
org.springframework.cloud spring-cloud-starter-gateway (2)application.yml添加配置eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka #EurekaServer的地址
register-with-eureka: true
fetch-registry: true
instance:
ip-address: 127.0.0.1
prefer-ip-address: true #在通过ServerName获取服务地址的时候,优先获取的是IP地址
spring:
application:
name: heima-gateway #当前微服务的名称
cloud:
gateway:
routes: #路由的配置 路由的一个集合
- id: user-service-route #路由的唯一标识
uri: http://127.0.0.1:9092 #路由分发的微服务请求地址 没办法实现负载均衡
predicates: # 断言
- Path=/user/** #断言规则/路由规则
- id: user-service-route2
uri: lb://user-service #可以在分发请求到具体微服的同时实现负载均衡
predicates:
- Path=/api/user/**
filters: #添加局部过滤器 框架提供的内置过滤器, 自定义过滤器
- StripPrefix=1 #去除一层 http://ip:port/user/addUser
- MyFilter=name,age,gender #自定义局部过滤器
- id: user-service-route3
uri: lb://user-service
predicates:
- Path=/**
filters:
- PrefixPath=/user #添加一层 http://ip:port/user/findUserById
globalcors:
cors-configurations:
‘[/**]’:
allowedOrigins: “*”
allowedMethods:
- GET
- POST
- PUT
- DELETE
浏览器的同源策略:
协议、IP域名、端口三者中的任何一个不一样,就微服了浏览器的同源策略。
跨域问题:
A和B服务,A服务的页面发送异步请求到B服务获取数据,如果A和B微服浏览器的同源策略,浏览器就会报跨域的错误。页面无法获取到后台返回的数据。
(3)如果用到了拦截,编写对应的过滤器
(4)引导类上添加
@EnableDiscoveryClient //Gateway分发请求,从EurekaServer上获取分发请求对应微服务的服务地址
Gateway的过滤器:
局部过滤器
@Component
public class HalfGatewayFilterFactory extends AbstractGatewayFilterFactory{
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
//拦截的业务操作
return chain.filter(exchange); //放行
};
}
}
spring:
application:
name: heima-gateway #当前微服务的名称
cloud:
gateway:
routes:
- id: user-service-route2
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- Half = a,b,c
问题:如何给过滤器传入参数
@Component
public class HalfGatewayFilterFactory extends AbstractGatewayFilterFactory<HalfGatewayFilterFactory.MyConfig>{
public HalfGatewayFilterFactory(){
super(MyConfig.class);
}
@Override
/**
* Half=a,b,c
*/
public List<String> shortcutFieldOrder() {
return Arrays.asList("param1","param2","param3");
}
@Override
public GatewayFilter apply(MyConfig config) {
return (exchange, chain) -> {
//拦截以后的业务操作
return chain.filter(exchange);
};
}
public static class MyConfig{
private String param3;
private String param2;
private String param1;
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
public String getParam2() {
return param2;
}
public void setParam2(String param2) {
this.param2 = param2;
}
public String getParam3() {
return param3;
}
public void setParam3(String param3) {
this.param3 = param3;
}
}
}
全局过滤器
@Component
public class MyGlobalFilter implements GlobalFilter,Ordered{
//拦截方法
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//业务操作,
// 获取请求参数
ServerHttpRequest request = exchange.getRequest();
//请求都封装在request
//响应结果
ServerHttpResponse response = exchange.getResponse();
//response设置响应信息
return chain.filter(exchange); //放行
}
//指定全局过滤器执行的顺序,顺序和数字的大小有关,数字越小,越先执行
@Override
public int getOrder() {
return 0;
}
}
Config
作用: 将所有微服务的配置文件提取出来,通过GIT的仓库进行统一管理。后台用户就可以直接维护GIT仓库中的配置文件,达到微服务配置文件的修改。
Bus
作用: 当微服务的配置文件从配置中心获取后,如何配置文件的内容发生变化,只有当微服务重新启动以后,才能达到及时更新效果,SpringCloudBus解决的问题是,当配置文件发生变化后,微服务不需要重新启动也能获取到配置文件最新的更新内容。
借助一个消息中间件(RabbitMQ)
SpringCloud小结下:
【Eureka Feign Gateway】 Ribbon Hystrix Config Bus