1 微服务
1.1 微服务是什么
1 将一个单体应用拆分成了一组微小的服务组件。这些组件以业务为中心
2 每个组件运行在自己的进程上。组件之间通过轻量级机制进行交互。
3 独立部署:使用自动化部署机制部署这些组件。
4 独立语言or数据库:采用不同的语言及存储
1.2 为什么使用微服务
伸缩性(scalable)
- 每个服务都可以在横向和纵向上扩展
X轴扩展:负载均衡后,运行多份拷贝,来分摊负载(每个拷贝运行1/n的负载)。
Y: 业务扩展:划分成了多个不同的服务,一个服务负责一个或者一部分的功能。
Z: 数据集的划分
- 每个服务都可按硬件资源的需求进行独立扩容
IO密集型模块和CPU密集型模块无法独立升级和扩容。业务模块对资源的需求不一样,由于所有模块部署到一起,单体架构IO密集型模块和CPU密集型模块无法独立升级和扩容的,比如图片压缩,加解密这些 都是cpu资源密集的应该升级CPU,而IO密集型的模块比如日志收集服务IO操作比较多需要更大的内存,使用比如SSD性能更好的磁盘。
扩容的方式:api网关
服务集群暴露在公网的统一入口。
作用:
- 统一对外接口:减少提供接口的差异。
- 统一鉴权:不用每个应用单独对调用方进行鉴权,应用可专注于服务。
- 服务注册和授权:用来控制调用方能使用和不能使用哪些服务。
- 服务限流:限制调用房调用每个接口的每日次数
- 全链路跟踪:方便通过api网关提供的请求ID来监控调用流程。
可靠性
- 一个bug有可能引起整个应用的崩溃
由于所有模块都是部署在一个实例中,一个bug会引起整个应用的崩溃,比如一个不重要的模块的内存泄露就将会导致所有应用实例一个个crash掉
1.3 微服务的优缺点
1.4 springcloud技术选型
- springcloud的服务发现
eureka
:
Eureka
就是一个服务发现框架: 实现模块定位服务,从而进行负载均衡和中间层服务器的故障转移。
Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务
docker
服务发现的业务流程:
- 服务注册:Eureka 客户端向 Eureka Server 注册时,提供元数据:ip地址、端口等
- 服务续约 Renew:Eureka 客户会每隔30秒(默认情况下)发送一次心跳来续约(注册表中)
- 获取注册列表信息 Fetch Registries:客户端用得到的注册信息表获取其他服务,从而远程调用
- 服务剔除:当Eureka客户端连续90秒(3个续约周期)没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除
- springboot 的负载均衡
Ribbon
RestTemplate是Spring提供的一个访问Http服务的客户端类
Ribbon 是运行在消费者端的负载均衡器:
Consumer 端获取到了所有的服务列表之后,在其内部使用负载均衡算法,进行对多个系统的调用。
负载均衡选型: Nginx 和 Ribbon 的对比
nginx: 将所有请求都集中起来,然后再进行负载均衡。而ribbon是在消费者端进行负载均衡的。 request位置不同。在 Nginx 中请求是先进入负载均衡器,而在 Ribbon 中是先在客户端进行负载均衡才进行请求的。
其次,负载均衡算法不同:nginx: 轮询和加权轮询。而ribbon是默认是使用的 RoundRobinRule 轮询策略: 若经过一轮轮询没有找到可用的 provider,其最多轮询 10 轮。若最终还没有找到,则返回 null。
- 熔断降级用的Hystrix
作用:熔断、降级
熔断:当指定时间窗内的请求失败率达到设定阈值时,系统将通过 断路器 直接将此请求链路断开。
- 解决办法:设置超时时间
降级:执行后备处理:一个方法调用异常时,通过执行另一种代码逻辑
// 指定了后备方法调用
@HystrixCommand(fallbackMethod = "getHystrixNews")
@GetMapping("/get/news")
public News getNews(@PathVariable("id") int id) {
// 调用新闻系统的获取新闻api 代码逻辑省略
}
//
public News getHystrixNews(@PathVariable("id") int id) {
// 做服务降级
// 返回当前人数太多,请稍后查看
}
- 网关Zuul
网关:鉴权、限流、 路由、监控等功能。
zuul:网关和过滤器
zuul的功能:
- 路由映射,统一前缀
- 过滤:从而实现 限流,灰度发布,权限控制
- openFeign
为了方便服务间方法的调用:
之前的方法:
每次都需要url、请求、返回类型 `return restTemplate.postForObject(url, request, Boolean.class);`
修改之后的方法:
新建一个interface,用FeighClient实现方法的映射:
@FeignClient(value = "eureka-client-provider")
public interface TestClient {
// 这里一定要注意需要使用的是提供者那端的请求相对路径,这里就相当于映射了
@RequestMapping(value = "/provider/xxx",
method = RequestMethod.POST)
CommonResponse<List<Plan>> getPlans(@RequestBody planGetRequest request);
}
then, 在 Controller 就可以像原来调用 Service 层代码一样调用它了