单体应用
- 所有业务功能都在一个应用程序里面
- 研发人员开发并维护同一个代码库
- 架构简单,典型的三层架构
单体应用横向扩展
单体应用的优势(规模不大)
架构简单,容易上手
部署简单,没有复杂的依赖
测试方便,一旦部署,所有功能就可以测了
单体应用的劣势(规模变大后)
复杂度变高,代码越来越庞大
开发效率低,开发协作越来越麻烦
牵一发而动全身,任何一个功能出故障,全部完蛋
微服务
- “微服务”就是微小的服务或应用,比如linux上各种工具:ls,cat,awk,wc,cp,rm等
- 基本原理:让每个服务专注的做好一件事情
- 每个服务单独开发和部署,服务之间是完全隔离的
优势
迭代周期短,极大的提升研发效率
独立部署,独立开发
可伸缩性好,能够针对指定的服务进行伸缩
故障隔离,不会互相影响
缺点
复杂度增加,一个请求往往要经过多个服务,请求链路比较长
监控和定位问题困难
服务管理比较复杂
服务注册和发现
注册/注销/负载/健康检查
Client side implement
调用需要维护所有调用服务的地址
有一定的技术难度,需要rpc框架支持
Server side implement
架构简单
有单点故障
etcd注册中心
分布式一致性系统
基于raft一致性协议
etcd使用场景
服务注册和发现
共享配置
分布式锁
Leader选举
服务注册和发现的思考
一定需要一致性么?
Eureka 是一个 AP 系统, netflix 开发,用来做高可用的注册中心
rpc调用
数据传输
Thrift协议
Protobuf协议
Json协议
msgpack协议
负载均衡
随机算法
轮询
一致性hash
异常容错
健康检查
熔断
限流
服务监控
log收集 - elk
日志收集端→ kafka集群→数据处理→日志查询和报警
Metrics打点 - promethus
实时采样服务的运行状态
直观的报表展示
span监控 - opentracing
服务网关
什么是服务网关
服务网关 = 路由转发 + 过滤器
1、路由转发:接收一切外界请求,转发到后端的微服务上去;
2、过滤器:在服务网关中可以完成一系列的横切功能,例如
权限校验、
监控等,
限流等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)。
为什么需要服务网关
上述所说的横切功能(以权限校验为例)可以写在三个位置:
每个服务自己实现一遍
写到一个公共的服务中,然后其他所有服务都依赖这个服务
写到服务网关的前置过滤器中,所有请求过来进行权限校验
第一种,缺点太明显,基本不用;
第二种,相较于第一点好很多,代码开发不会冗余,但是有两个缺点:
由于每个服务引入了这个公共服务,那么相当于在每个服务中都引入了相同的权限校验的代码,使得每个服务的jar包大小无故增加了一些,尤其是对于使用docker镜像进行部署的场景,jar越小越好;
由于每个服务都引入了这个公共服务,那么我们后续升级这个服务可能就比较困难,而且公共服务的功能越多,升级就越难,而且假设我们改变了公共服务中的权限校验的方式,想让所有的服务都去使用新的权限校验方式,我们就需要将之前所有的服务都重新引包,编译部署。
而服务网关恰好可以解决这样的问题:
将权限校验的逻辑写在网关的过滤器中,后端服务不需要关注权限校验的代码,所以服务的jar包中也不会引入权限校验的逻辑,不会增加jar包大小;
如果想修改权限校验的逻辑,只需要修改网关中的权限校验过滤器即可,而不需要升级所有已存在的微服务。
所以,需要服务网关!!!
服务网关基本功能
- 智能路由:接收外部一切请求,并转发到后端的对外服务open-service上去;
注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。 - 权限校验:只校验用户向open-service服务的请求,不校验服务内部的请求。服务内部的请求有必要校验吗?
- API监控:只监控经过网关的请求,以及网关本身的一些性能指标(例如,gc等);
- 限流:与监控配合,进行限流操作;
API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志
。。。后续补充
开发语言:java + groovy,groovy的好处是网关服务不需要重启就可以动态的添加filter来实现一些功能;
微服务基础框架:springboot;
网关基础组件:netflix zuul;
服务注册中心:consul;
权限校验:jwt;
API监控:prometheus + grafana;
API统一日志收集:logback + ELK;
压力测试:Jmeter;
// drf的requests初始化流程
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)