SpringCloud第二天
内容回顾
(1)系统架构的演变过程
单体架构
垂直架构
分布式之RPC架构
分布式之SOA架构
分布式之微服务架构
(2)微服务概念理解
微服务: 虽小五脏俱全。
开发项目所用到的所有内容【前端、后台】,微服务里面都包含。
基于SpringBoot来构建的。
(3)微服务与微服务之间肯定要进行系统调用
RPC 【dubbo Socket /ServerSocket】
HTTP【微服务采用 HttpClient/OkHttp/RestTemplate】
(4)SpringCloud就是用来治理微服务。组件来决定。
SpringCloud不是一个具体的框架而是很多组件的一个集合。组件就是用来治理微服务的。
Spring / SpringBoot /SpringCloud
(5)Eureka
EurekaServer
(1)管理微服务的列表 (2)通过心跳检测管理微服务的状态
EurekaClient
(1)注册 (2)发现
步骤:
EurekaServer:
(1)创建一个Springboot的项目
<parent> spring-boot-starter-parent
引导 : @SpringBootApplication
application.yml
(2)pom.xml
spring-cloud-starter-netflix-eureka-server
(3)引导类上
@EnableEurekaServer
(4)application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
fetch-registry: false
register-with-eureka: false
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
EurekaClient:
(1)创建一个Springboot的项目
<parent> spring-boot-starter-parent
引导 : @SpringBootApplication
application.yml
(2)pom.xml
spring-cloud-starter-netflix-eureka-client
(3)引导类上
@EnableDiscoveryClient
(4)application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
fetch-registry: true
register-with-eureka: true
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
Eureka的高可用环境搭建。
执行流程:
服务消费方根据EurekaServer上注册的服务名称ServerName,根据ServerName找到服务提供者的服务列表,找到服务列表后,从服务列表中获取一个具体的服务对应的IP和端口,然后进行远程调用。
Ribbon
实现就是负载均衡,以轮询/随机的方式从服务列表中获取一个具体的服务IP和端口
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
restTemplate.getForObject();
Hystrix熔断保护(***)
Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速 失败判定时间。
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超 时,则会进行降级处理,什么是服务降级?
服务降级:优先保证核心服务,而非核心服务不可用或弱可用。
用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友 好的提示信息) 。
服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服 务没有响应。
触发Hystrix服务降级的情况:
线程池已满
请求超时
雪崩问题
(1)线程隔离
为每一个微服务设定一个线程池,在线程池中指定一定的线程数。当用户的请求过来以后,先从线程池中获取线程,如果能获取到继续访问,如果获取不到,判断用户此次请求失败,一旦判断请求失败,直接走降级服务。目的:加速访问失败的判定。
(2)服务降级
服务的消费方进行编写。
》pom.xml
spring-cloud-starter-netflix-hystrix
》引导类上
@EnableCircuitBreaker 熔断保护 hystrix单独使用
@SpringCloudApplication
集成了
@SpringBootApplication springboot启动注解
@EnableDiscoveryClient eureka客户端
@EnableCircuitBreaker 熔断保护 hystrix
》降级服务
类上:类中的所有方法可以使用
在类上添加
@DefaultProperties(defaultFallback=“降级服务方法”)
在方法上添加
@HystrixCommand
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TP31EeNpXT1sGROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5kjM0AjM1UTM5ETMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
方法上:只对当前方法有效
@HystrixCommand(fallback=“methodName”)
原服务方法和降级服务方法的[参数]和[返回值]一致即可。
(3)熔断保护
熔断保护原理
closed : 默认情况下,熔断包含关闭状态,服务正常访问远程服务。
open: 10秒访问20次有50%失败,熔断保护的状态就会被打开,一旦被打开,访问的时候直接走的是降级服务。
half Open: 状态为打开后,过5秒,熔断保护的状态会更改为half open,允许部分请求走原来的远程服务调用,调用成功,则将状态改为closed。如果调用失败继续保持为half open,下个5秒后继续进行该操作。
Feign的学习(*******)
微服务与微服务之间的调用。
RestTemplate(HttpClient/OkHttp)
restTemplate.getForObject("http://localhost:9091/user/findById/8",返回类.class)
Ribbon
restTemplate.getForObject("http://server_name/user/findById/8",返回类.class)
(1)server_name
(2)url [/user/findById]
(3)请求的参数
(4)请求的返回值
更想要的效果是像调用本地方法一样调用远程服务。
(1)pom.xml
spring-cloud-starter-openfeign
(2)引导类上
@EnableFeignClients
(3)编写一个接口
@FeignClient("??") //server_name
public interface FeignClient{
@RequestMapping("/.....") //url
public xx methodName(....); //.... 请求的参数
//xx方法的放回值 请求的返回值
}
(4)需要远程调用的类中
@Autowired
private FeignClient feignClient;
xx = feignClient.method(...);
Feign底层是使用的是Ribbon+RestTemplate+动态代理实现远程调用。
Feign解决的问题是:微服务与微服务之间的系统调用
Feign支持Hystrix
feign:
hystrix:
enable: true
Gateway学习(*******)
(1)过滤拦截
服务鉴权(判断用户是否已经登录、判断用户是否具有访问某一个资源的权限)
日志记录
…
(2) 路由
建立的是一种对应关系
SpringMVC: 用户发送请求的URL和handler之间的关系 @RequestMapping
RabbitMQ: 交换机和队列之间的关系,routingKey
路由:用户发送请求的URL和访问的微服务之间的关系 ?
使用Gateway
pom.xml
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-gateway
引导类
@EnableDiscoveryClient
application.yml
spring:
application:
name: heima-gateway
cloud:
gateway:
routes:
- id: user-service-route
uri: http://127.0.0.1:9092
predicates:
- Path=/user/**
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka #EurekaServer的地址
register-with-eureka: true
fetch-registry: true
访问微服务的时候,未实现负载均衡的功能
路由配置中uri所用的协议为lb时(以uri: lb://user-service为例),gateway将使用 LoadBalancerClient把 user-service通过eureka解析为实际的主机和端口,并进行ribbon负载均衡。
spring:
application:
name: heima-gateway
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://server_name #@LoadBalanced
predicates:
- Path=/user/**
添加和去除一层访问地址的配置方式
-
id: user-service-route2
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1 #去除一层
- My=name,age,gender
-
id: user-service-route3
uri: lb://user-service
predicates:
-
Path=/**
filters:
- PrefixPath=/user #添加一层
-
-
过滤器
Web中的过滤器
(1)编写一个过滤器
public class MyFilter implements Filter{
init
destroy
doFilter(request,reponse,filterChain){
//拦截前的业务操作1
filterChain.doFilter(request,reponse);
//拦截后的业务操作1
}
}
public class MyFilter implements Filter{
init
destroy
doFilter(request,reponse,filterChain){
//拦截前的业务操作2
filterChain.doFilter(request,reponse);
//拦截后的业务操作2
}
}
//拦截前的业务操作1
/拦截前的业务操作2
//拦截后的业务操作2
//拦截后的业务操作1
(2)配置过滤器
web.xml <filter> <filter-name> <filter-class> 。。。。。
常见的应用场景如下:
请求鉴权:一般 GatewayFilterChain 执行filter方法前,如果发现没有访问权限,直接就返回空。
异常处理:一般 GatewayFilterChain 执行filter方法后,记录异常并返回。
服务调用时长统计: GatewayFilterChain 执行filter方法前后根据时间统计。
局部过滤器 [某一些路由有效]
(1)GatewayFilterFactory
@Component
public class MyFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<MyFilterGatewayFilterFactory.MyConfig> {
/**
* 构造函数,自己编写的MyCOnfig.class告诉给父类
*/
public MyFilterGatewayFilterFactory(){
super(MyConfig.class);
}
/**
* 设定参数的顺序
* param1:JERRY
* param2:laoshu
* param3:200
* @return
*/
public List<String> shortcutFieldOrder() {
return Arrays.asList("param1","param2","param3");
}
/**
* 过滤拦截方法
* @param config
* @return
*/
@Override
public GatewayFilter apply(MyConfig config) {
return (exchange, chain) -> {
//System.out.printf("=====>%s是一个%s,今年%s岁",config.param3,config.param1,config.param2);
ServerHttpRequest request = exchange.getRequest();//请求数据
MultiValueMap<String, String> queryParams = request.getQueryParams();//请求数据
if(queryParams.containsKey("name")){
List<String> valueList = queryParams.get("name");
String nameValue = valueList.get(0);
System.out.println("name对应的值是:"+nameValue);
}
if(queryParams.containsKey("age")){
List<String> valueList = queryParams.get("age");
String ageValue = valueList.get(0);
System.out.println("age对应的值是:"+ageValue);
}
if(queryParams.containsKey("gender")){
List<String> valueList = queryParams.get("gender");
String genderValue = valueList.get(0);
System.out.println("gender对应的值是:"+genderValue);
}
System.out.println();
return chain.filter(exchange); // doFilter 放行
};
}
/*
用来接收过滤器配置传递的参数
*/
public static class MyConfig {
private String param1;
private String param2;
private String param3;
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 MyGlobalFilter1 implements GlobalFilter,Ordered{ //implemnts Filter
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//doFilter方法
System.out.println("MyGlobalFilter1全局过滤器执行了");
return chain.filter(exchange);//filterChain.doFiler()放行
}
@Override
/**
* 返回值决定了过滤器的执行顺序,值越小越先执行
*/
public int getOrder() {
return 4;
}
}
@Component
public class MyGlobalFilter2 implements GlobalFilter,Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("MyGlobalFilter2全局过滤器执行了");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 2;
}
}
补充:跨域问题。
浏览器的同源策略:
当请求的协议、主机、PORT,只要有一个不一样,违法了浏览器的同源策略。
http://localhost:8080/user/findById
http://192.168.200.131:8080/user/findById 不满足
http://localhost:8080/user/findById
https://localhost:8080/user/findById 不满足
http://localhost:8080/user/findById
http://localhost:8081/user/findById 不满足
http://localhost:80/user/findById
http://localhost/user/findById 满足
http://localhost:8080/user/findById
http://localhost:8080/teacher/findById 满足
两台服务器,A和B,当从A服务器的页面发送异步请求请求B服务器的后台数据,如果服务器A和B不满足浏览器的同源策略,就会产生跨域问题。
跨域是浏览器报的错误,请求能正确发送也能正确返回,但是浏览器不会去解析返回的数据,并且报了上述错误,这就是我们说的跨域问题。
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 #去除一层
- MyFilter=name,age,gender
- id: user-service-route3
uri: lb://user-service
predicates:
- Path=/**
filters:
- PrefixPath=/user #添加一层
globalcors:
cors-configurations:
‘[/**]’: #所有请求
allowedOrigins: “*” #服务器允许哪些服务地址进行跨域访问
allowedMethods: #允许跨域的请求方式
- GET
- POST
- PUT
- DELETE
发生跨域问题时,可以配置允许哪些服务地址通过某种方式进行跨域访问
globalcors:
cors-configurations:
‘[/**]’: #所有请求
allowedOrigins: “*” #服务器允许哪些服务地址进行跨域访问
allowedMethods: #允许跨域的请求方式
- GET
- POST
- PUT
- DELETE
Config学习
作用:
实现:
(1)使用码云创建GIT远程仓库
(2)在GIT仓库中创建配置文件
xxxx-yyyy.yml
xxxx:应用名称
yyyy:环境名称 dev 开发环境 test 测试环境 pro 生产环境
(3)搭建配置中心微服务
》创建Springboot项目
》pom.xml添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
》启动引导类
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
》application.yml
server:
port: 12000
spring:
application:
name: config-springcloud
cloud:
config:
server:
git:
uri: https://gitee.com/lgang1217/git_cloud.git #存放配置文件的GIT地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
》启动测试
微服务中
(1)pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(2)引导类中添加
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
(3)application.yml删除
(4)bootstrap.yml
xxxx-yyyy.yml
spring:
cloud:
config:
name: xxxx
profile: yyyy
label: master
discovery:
service-id: config-springcloud
enabled: true
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
(5)启动
总结
SpringCloud
创建一个springboot项目
pom.xml添加起步依赖
引导类上添加一些注解
application.yml添加配置
Eureka[必须]
注册中心
EurekaServer
(1)记录服务提供者的服务列表
(2)通过心跳检测管理服务的状态
EurekaClient
(1)注册
(2)发现
步骤:
EurekaServer
spring-cloud-starter-netflix-eureka-server
@EnableEurekaServer
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: false
fetch-registry: false
EurekaClient
spring-cloud-starter-netflix-eureka-client
@EnableDiscoveryClient
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: true
fetch-registry: true
通过RestTemplate是可以实现远程调用,但是未能实现负载均衡
Ribbon[服务的消费方][次次之]
@LoadBalanced
restTemplate.getForObject("http://service_name/...",返回值类型.class);
Hystrix[服务的消费方][次之]
由于某个微服务请求失败导致整体出现雪崩问题。
(1)线程隔离
加速用户请求的失败判定,如果判定失败,则走降级服务
(2)熔断保护
closed 10秒20次请求50%失败 open 5秒 half open
(3)服务降级
spring-cloud-starter-netflix-hystrix
@EnableCircuitBreaker / @SpringCloudApplication
方法
@HystrixCommand(fallback="methodName")
降级服务和原服务对应的参数和返回值一定要保持一致
类上
@DefaultProperties(defaultFallback="methodName")
@HystrixCommand
Feign[必须]
实现的微服务与微服务之间的系统调用。对Ribbon+RestTemplate通过动态代理进行一层封装。
pom.xml
spring-cloud-starter-openfeign
引导类上添加一些注解
@FeignClients
编写一个Feign的接口
@FeignCient("server_name")
接口中的方法参考服务提供方Controller方法进行编写
@RequestMapping("")
public Xxx methodName(...);
需要远程调用的地方
@Autowired
通过接口调用方法的方式来完成远程调用。
(1)Feign对Ribbon+RestTemplate通过动态代理进行一层封装。
(2)Feign支持Hystrix的使用
》application.yml开启服务
feign:
hystrix:
enable:
true
》编写一个实现类实现FeignClient接口
public class Xxx implements FeignClient{}
》@FeignClient指定使用实现类
@FeignClient(value = "server_name",fallback = Xxx.class)
(3)微服务与微服务之间的系统调用
Gateway[必须]
(1) 过滤拦截
(2) 路由:分发请求
pom.xml
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-gateway
引导类上添加一些注解
@EnableDiscoveryClient
application.yml
spring:
cloud:
gateway:
routes:
- id: user-service-route2
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- PrefixPath=/user 加
- StripPrefix=1 减
(3)过滤器
局部过滤器
自定义:GatewayFilerFactory / AbstractGatewayFilterFactory
内置的局部过滤器
全局过滤器
GlobalFilter,Ordred
(4)跨域问题
Config[整理了解]
管理所有微服务的配置文件
Bus[整理了解]