1.项目脚手架构建
1.1项目结构介绍和框架选择
了解VO、POJO、BO对象、之前学习用的DTO、Entity、VO对象
1.2脚手架构建
-
springboot 2.1.8
-
SprinCloud Green
-
Mybatis-plus
1.3mybatis-plus使用优势
-
Mybatis-plus是基于Mybatis开发的开源框架
-
Mybatis-plus封装了分页、条件查询等内容
-
Mybatis-helper可以帮助我们更好的使用Mybatis框架
2.Eureka核心特性
2.1核心特性
Eureka的核心特性:服务注册register、服务续约renew、服务下线cancel、获取注册列表信息
1.服务注册
-
Eureka Chient在第一次心跳时向Eureka Server注册
-
注册时会提供诸多自身元数据:主机名、端口、健康指标URL等
2.服务续约
-
Eureka Client通过发送心跳进行续约
-
默认情况下每30秒发送一次心跳
-
如90秒内Eureka Server未收到续约,则会进行服务剔除
3.服务下线
- Eureka Client优雅退出时会发送cancel命令
- Eureka Service收到cancel命令时会删除该节点
4.获取注册列表信息
- Eureka Client会缓存由Server获取的注册表信息
- Eureka Client会定期更新注册表信息【默认是30秒】
- Eureka Client会处理注册表的合并等内容
2.2面试点
1.多注册中心的比较
- 分布式基础:CAP理论
- 常见的注册中心:Zookeeper、Eureka等
- Eureka主要保证的是AP特性
- Zookeeper是典型的CP特性
CAP理论
- 一致性:Consistency,强一致性,一个节点的crud变动,其他节点随之变动;弱一致性,一段时间可能不一致,但是最终会一致
- 可用性:Availability,
- 分区容错性:Partition tolerance,保证系统部署在不同的分区
2.Eureka注册慢
- 注册慢的根本原因在于Eureka的AP特性
- Eureka Client延迟注册,默认是30秒
- Eureka Server的响应缓存,默认是30秒
- Eureka Server的缓存刷新,默认30秒
3.Eureka的自我保护
- Eureka Server会自动更新续约更新阈值
- Eureka Server续约更新频率低于阈值的时候,则进入保护模式
- 自我保护模式下Eureka Server不会提出任何注册信息
3.Eureka实战
3.1模块介绍
理解:影片服务去Eureka Server上去注册自己的服务地址,院线服务就会去Eureka Server获取影片服务的注册信息,然后院线服务就会通过Http的形式去调用影片服务,就完成了一次业务调用
4.负载均衡Ribbon
4.1 Ribbon架构图展示
注意:Eureka Client和Ribbon Client 在一个客户端
4.2 Ribbon概述
-
Ribbon是客户端负载均衡器
-
Ribbon核心功能:服务发现
-
Ribbon核心功能:服务选择规则
-
Ribbon核心功能:服务监听
注意:
-
哪些内容能被负载均衡:ServerList
-
怎么负载均衡:负载均衡算法----1.allServerList 2.upServerList
-
IPing:探测服务存活状态
负载均衡算法:
配置类具体实现如下:
如上图所示:Eureka实现负载均衡可以直接在restTemplate()方法上加 @LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
4.3 Ribbon核心之IPing
IPing的功能是探测服务的存活状态,也是为了给ServerList提供支持
- IPing是Ribbon保证服务可用的基石
- 常见的实现:NIWSDiscoveryPing、PingURL
4.4 Ribbon核心之ServerList
- ServerList是Ribbon存储的可用服务列表
- ServerList可以手动设置
- ServerList常见应用是从Eureka中自动获取
5 熔断器Hystrix
5.1 Hystrix介绍
- Hystrix适用于处理延迟和容错的开源库
- Hystrix主要是用于避免级联故障,提高系统弹性,也就是提高系统等可用性
- Hystrix解决了由于扇出导致的“雪崩效应”
- Hystrix的核心是“隔离术”和“熔断机制”
扇出示意图:
扇出可以理解为像扇子一样展开,一个节点的崩溃,会导致上面的父节点全部崩溃,也就是雪崩效应
5.2 Hystrix主要作用
- 提供了服务隔离和服务熔断
- 提供了服务降级、限流和快速失败
- 提供了请求合并和请求缓存
- 自带单体和集群监控
5.3 Hystrix架构图
主要流程如下:
- 1.调用“Main”方法,Command或者Observable Command两种命令模式
- 2.进行业务验证
- a. 判断有没有缓存
- b. 熔断有没有开启
- c. 限流有没有触发
- d. 业务执行有没有失败
- e. 业务执行有没有超时
- f. 所有失败方法都会触发fallBack方法
- 3.业务成功后,直接返回
5.4 Hystrix入门
1.Hystrix两种命令模式
-
HystrixCommand和HystrixObservableCommand
-
Command会以隔离的形式完成run方法的调用
-
ObservableCommand使用当前线程进行调用
2.Hystrix配置之GroupKey
-
Hystrix中GroupKey是唯一必填项
-
GroupKey可以作为分组监控和报警的作用
6 Feign入门
6.1 Feign介绍
-
Feign是一个声明式、模版化的HTTP客户端
-
Feign很大程度上简化了HTTP的调用方式
-
Feign很好的弥补了SpringCloud的HTTP的调用的时候,重复的工作量——restTemplate也可以
6.2 Feign能干什么
- Feign包含了多种的HTTP的调用形式
- Feign整合了Ribbon和Hystrix
- Feign提供了多种HTTP底层支持
6.3 Feign特性
-
Feign实现了可插拔的注解支持,包括Feign和JAX-RS注解
-
Feign支持了可插拔的HTTP编码器和解码器
-
Feign支持HTTP请求和响应的压缩
6.4 Feign配置
1.引入openFeign依赖包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.开启EnableFeignClients支持
@EnableFeignClients
@EnableDiscoveryClient // DiscoverClient可以集成大部分注册中心
// @EnableEurekaClient 只对Eureka使用
@SpringBootApplication
public class BackendShowConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BackendShowConsumerApplication.class, args);
}
}
在启动类上添加@EnableFeignClients
3.创建业务接口
import com.mooc.meetingfilm.consumer.feign.vo.UserModel;
import com.mooc.meetingfilm.feignconf.FeignHelloConf;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(name = "hello-service-provider",
primary = true,
path = "/provider",
fallbackFactory = FallbackFactory.class
// fallback = ProviderFallbackAPIImpl.class
// configuration = FeignHelloConf.class,
// url = "http://localhost:7101"
)
public interface ProviderApi {
@RequestMapping(value = "/sayhello",method = RequestMethod.GET)
String invokerProviderController(@RequestParam("message") String message);
// @RequestLine("GET /sayhello?message={message}")
// String invokerProviderController(@Param("message") String message);
// @RequestMapping(value = "/{providerId}/sayhello",method = RequestMethod.POST)
// String providerPost(
// @RequestHeader("author") String author,
// @PathVariable("providerId")String providerId,
// @RequestBody UserModel userModel);
//@RequestBody还支持json和对象
//@RequestHeader请求头
}
- 添加FeignClient注解,name为自己自定义,url为注解的访问目标地址,包含协议、host、post。
- 添加业务方法,添加URL和method方法支持,编写入参和返回值
6.5 Feign支出多种HTTP的调用形式
6.6 Feign多组件集成
1.集成Ribbon,编写配置类
也可以在yml里面配置最大注册数:
ribbon:
MaxAutoRetries : 3
hello-service-provider:
ribbon:
MaxAutoRetries : 3
2.集成Hystrix熔断器
2.1 配置yml
feign:
hystrix:
enabled: true
2.2 编写降级实现类,继承接口,重写方法
import org.springframework.stereotype.Service;
/**
* @author :
* @program : com.mooc.meetingfilm.consumer.feign
* @description : 降级实现
**/
@Service
public class ProviderFallbackAPIImpl implements ProviderApi{
@Override
public String invokerProviderController(String message) {
return "invokerProviderController fallback message="+message;
}
}
2.3 在api接口上的FeignClient中添加fallback=ProviderFallbackAPIImpl.class
@FeignClient(name = "hello-service-provider",
primary = true,
path = "/provider",
// fallbackFactory = FallbackFactory.class
fallback = ProviderFallbackAPIImpl.class
// configuration = FeignHelloConf.class,
// url = "http://localhost:7101"
)
public interface ProviderApi {
@RequestMapping(value = "/sayhello",method = RequestMethod.GET)
String invokerProviderController(@RequestParam("message") String message);
// @RequestLine("GET /sayhello?message={message}")
// String invokerProviderController(@Param("message") String message);
// @RequestMapping(value = "/{providerId}/sayhello",method = RequestMethod.POST)
// String providerPost(
// @RequestHeader("author") String author,
// @PathVariable("providerId")String providerId,
// @RequestBody UserModel userModel);
}
6.7 Feign的降级处理
import org.springframework.stereotype.Service;
/**
* @author :
* @program : com.mooc.meetingfilm.consumer.feign
* @description :
**/
@Service
public class FallbackFactory implements feign.hystrix.FallbackFactory {
@Override
public ProviderApi create(Throwable throwable) {
return new ProviderApi() {
@Override
public String invokerProviderController(String message) {
return "invokerProviderController FallbackFactory message="+message;
}
};
}
}
这里的类名重名,所以添加的是feign.hystrix.FallbackFactory
6.8 Feign面试点分析
1.Feign之HTTP性能优化
- Feign默认使用的JDK自带的HTTP方式
- Feign最大的优化点事更换HTTP底层的实现
- 目前的Apache HTTPClient事一个非常好的选择
-
添加pom依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
-
修改配置文件yml,在之前的基础上添加
feign: #开启熔断器 hystrix: enabled: true #开启自定义的HttpClinet httpclient: enabled: true
2.Feign之HTTP解压缩
- HTTP常见的优化项是数据压缩
- Feign可以支持GZip的请求解压缩
- 注意:解压缩是把双刃剑,一定要谨慎使用
feign:
hystrix:
enabled: true
httpclient:
enabled: true
#请求压缩
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
6.9 Feign实战技巧
6.9.1.Feign继承
- 微服务的目标是大量复用,Feign会导致重复工作量
- Feign提供了继承特性帮助我们解决这个问题
- 接口复用最多只能有一层,切忌多继承
-
创建一个api模块
1.1 在父模块下添加
<modules> <module>backend_api</module> </modules>
<dependencyManagement> <dependencies> <dependency> <groupId>com.mooc.meetingfilm</groupId> <artifactId>backend-api</artifactId> <version>${project.version}</version> </dependency> </dependencies> </dependencyManagement>
-
导入Feign依赖,修改父模块依赖
<parent> <groupId>com.mooc.meetingfilm</groupId> <artifactId>backend-parent</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
客户端Film和hall开启Feign注解 @EnableFeignClients
@ComponentScan(basePackages = {"com.mooc.meetingfilm"}) @MapperScan(basePackages = {"com.mooc.meetingfilm.film.dao"}) @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class BackendFilmApplication { public static void main(String[] args) { SpringApplication.run(BackendFilmApplication.class, args); } }
-
在服务film和hall添加Api依赖
<dependencies> <dependency> <groupId>com.mooc.meetingfilm</groupId> <artifactId>backend-api</artifactId> </dependency> </dependencies>
-
在Api模块添加对外暴露的接口,如果需要返回的Vo对象,可以在Api模块编写,删除之前的VO,统一用API模块的
import com.mooc.meetingfilm.apis.film.vo.DescribeFilmRespVO; import com.mooc.meetingfilm.utils.common.vo.BaseResponseVO; import com.mooc.meetingfilm.utils.exception.CommonServiceException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * @author : * @program : com.mooc.meetingfilm.apis.film * @description : Film提供的公共接口服务 **/ public interface FilmFeignApis { /** * @Description: 对外暴露的接口服务 * @Param: [filmId] * @return: com.mooc.meetingfilm.utils.common.vo.BaseResponseVO * @Author: jiangzh */ @RequestMapping(value = "/films/{filmId}", method = RequestMethod.GET) BaseResponseVO<DescribeFilmRespVO> describeFilmById(@PathVariable("filmId") String filmId) throws CommonServiceException; }
package com.mooc.meetingfilm.apis.film.vo; import lombok.Data; /** * @author : * @program : com.mooc.meetingfilm.film.controller.vo * @description : 根据主键获取影片信息对象 **/ @Data public class DescribeFilmRespVO { private String filmId; private String filmName; private String filmLength; private String filmCats; private String actors; private String imgAddress; private String subAddress; }
-
在需要调用的模块添加Client包,这里是hall调用film查询电影信息,所以在hall模块下建一个包为client
在client包下新建一个FilmFeignApi来继承Api模块下的FilmFeignApis
有的公司是新建一个client去继承,也可以跳过此步,在第七步直接引入FilmFeignApis也可以
import com.mooc.meetingfilm.apis.film.FilmFeignApis; import org.springframework.cloud.openfeign.FeignClient; /** * @author : * @program : com.mooc.meetingfilm.hall.apis * @description : film提供的接口服务 **/ @FeignClient(name = "film-service") public interface FilmFeignApi extends FilmFeignApis { }
-
在业务逻辑层引入FilmFeignApi即可,就可以调用该方法
@Resource private FilmFeignApi filmFeignApi;
7 zuul网关
7.1 网关介绍
- 由于微服务“各自为政”的特性,使微服务的使用非常麻烦
- 通常我们会雇佣一个“传达室大爷”,作为统一入口,这就是网关
- 网关的主要作用是请求转发和请求拦截
7.2 Zuul介绍
-
Zuul是网关大军中的一员,目前市场使用的规律比较高
-
Zuul除了实现请求转发和过滤,一般还作为鉴权和容错使用
-
Zuul可以无缝衔接Ribbon和Hystrix
7.3 Zuul的配置
-
新增一个模块Zuul,在父模块添加依赖,不需要在业务dependencyManagement里面添加依赖
<modules> <module>backend_apigw_zuul</module> </modules>
-
在模块zuul中pom文件添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
在Zuul模块的启动类添加注解,开启zuul代理@EnableZuulProxy
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @EnableZuulProxy @SpringBootApplication public class BackendApigwZuulApplication { public static void main(String[] args) { SpringApplication.run(BackendApigwZuulApplication.class, args); } }
-
在模块的yml中配置zuul
server: port: 8080 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ zuul: routes: film-service: path: /filmapi/** logging: config: classpath:logback.xml
访问http://localhost:8080/film-service/films即可访问,或者http://localhost:8080/filmapi/films
7.4 Zuul的使用
7.4.1 请求路由
-
Zuul可以通过配置完成请求的路由配置
-
Zuul服务器由默认支持的serviceId作为上下文
-
Ignored-services可以禁用serviceId
7.4.2 请求路由表达式
- ?–>匹配任意单个字符
- *–>匹配任意数量的字符
- ** -->匹配任意数量的字符,支持多级目录
7.4.3 Zuul的高层架构图
7.4.4 Zuul Filter的生命周期
7.4.5 自定义Filter
-
继承ZuulFilter并实现相应的方法
-
设置Filter类型、级别和是否启用
-
开发具体的业务逻辑
-
在zuul模块添加filter文件夹,并在里面创建自定义的filter类,需要继承ZuulFilter
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; /** * @author : * @program : com.mooc.meetingfilm.apigwzuul.filters * @description : **/ @Slf4j public class MyFilter extends ZuulFilter { /** * @Description: Filter类型 * @Param: [] * @return: java.lang.String * @Author: jiangzh */ @Override public String filterType() { return "pre"; } /** * @Description: filter的执行顺序 * @Param: [] 数字越大越靠后 * @return: int * @Author: jiangzh */ @Override public int filterOrder() { return 0; } /** * @Description: 是否要拦截 * @Param: [] * @return: boolean * @Author: jiangzh */ @Override public boolean shouldFilter() { return true; } /** * @Description: Filter的具体业务逻辑 * @Param: [] * @return: java.lang.Object * @Author: jiangzh */ @Override public Object run() throws ZuulException { // ThreadLocal RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String headName = headerNames.nextElement(); log.info("headName:{}, headValue:{}", headName, request.getHeader(headName)); } return null; } }
-
在zuul模块下新建一个config的文件夹,编写ZuulConfig配置类,设置返回值类型为自定义Filter类
import com.mooc.meetingfilm.apigwzuul.filters.JWTFilter; import com.mooc.meetingfilm.apigwzuul.filters.MyFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author : * @program : com.mooc.meetingfilm.apigwzuul.config * @description : **/ @Configuration public class ZuulConfig { @Bean public MyFilter initMyFilter(){ return new MyFilter(); } }
7.4.6 核心PreFilter
7.4.7 核心RoutingFilter
7.4.8 核心PostFilter
7.5 Zuul面试点
7.5.1 Zuul和Zuul2
- Zuul使用的是阻塞式县城完成业务调用,占用资源较多,并发量不高
- Zuul使用的是异步线程完成业务调用
7.5.2 Zuul与Hystrix整合
- Zuul可以使用FallbackProvider完成降级开发
- Zuul默认是使用HystrixCommand进行包装的
- Zuul默认情况下隔离使用的SEMAPHORE
屏蔽头信息:
-
sensitive-headers: “imooc” ## zuul默认会过滤一些头信息,需要进行过滤设置-----乐观锁
-
ignored-headers: