K8S下挖空Dubbo的一些思路--可行性

在kubernetes环境下,dubbo显得有些臃肿。我们从用户接口、协议、注册中心三部分来看一下

  • 用户接口:正常情况下开发只会使用三个@Reference@SeviceRpcContext;足够简洁、没问题
  • 协议:默认是单一长连接;首先http2是大势所趋、而且两者实现类似,其次Istio对原始tcp的支持不是很理想
  • 注册中心:无论是ZookeeperNacos都是一个独立的大组件,属于需要单独维护的那种。而且目前的Nacos我没有使用的欲望

那么明确下目的:在保持用户接口的情况下,替换掉dubbo协议和注册中心

当我们说到爬虫时我们在说什么呢(下)

接上文,我们来看下最麻烦的反爬对策

为什么说反爬对策是最麻烦的呢(注意这里的最麻烦不是技术上最困难)?原因有下

  1. 没有明确的目标,你很难确定做对了什么或者做错了什么
  2. 会引入一些外部资源,而这些资源会大幅提高爬取成本
  3. 会显著降低爬取效率,而且随时可能会被封死
  4. 心很累,一旦封死继续去抓包、调试吧

当我们说到爬虫时我们在说什么呢(上)

当我们说到爬虫时我们在说什么呢?Spider?Robots?Python?网络协议?黑产?

上边都可能会说到吧,但是我觉得这些都是表层现象,没有触及现代爬虫技术的核心。我认为爬虫的核心在于

  1. 调度框架,包括但不限于任务发现、去重和重试,延时调度,补偿机制,分布式多队列等等
  2. 逆向工程,包括但不限于抓包分析,反编译,插桩,动态调试等等
  3. 反爬对策,这个就是让你去对抗一个团队(笑

个人认为以上三项的麻烦(e xin)程度依次递增

在Java里实现一套自己的SPI机制

之前我们有使用过dubbo的SPI机制,并且java也有自己的SPI机制

java的SPI不能按需加载,没有优先级,只能空参构造;dubbo的SPI和自己深度结合,无法单独使用。我们需要一个满足以下特性的SPI机制

  1. 和其它SPI机制类似,支持按需加载
  2. 可以将实现类和其优先级定义在一起,可以按优先级加载,也可以全部加载
  3. 在某种程度上支持参数构造,因为可能某一接口的全部实现类都依赖相同的运行时参数

Kubernetes常见高可用方案

首先看下一个高可用集群需要哪些先决条件

  • etcd集群要高可用:节点在3个或以上
  • cp上的服务:kube-apiserver、kube-controller-mansger和kube-scheduler有多个实例

以上两个条件是一个HA集群的基础,无论是通过kubeadm还是其它手段都很容易做到。这里不做过多讨论,官方文档已经很详细了,目前主推Stacked

我们知道在集群内部会生成一个kubernetes服务,集群内部对apiserver的调用通过这个svc实现了高可用,那么是否还有哪里不满足高可用呢?

不仅有,还是一个很重要的、每个节点都需要安装的组件:kubelet

如何打造一个Dubbo网关--模拟网关

前文已经构建了一个完整的将dubbo服务转换为http+json的网关了。但还有一个重要的问题:开发人员如何在本地调试呢?我们假设如下条件

  1. 开发使用vpn接入dev环境,本地项目可以正常启动;但网关无法连接开发机器上的服务
  2. 开发阶段使用单元测试自测;但联调和提测阶段,前端和测试人员只会给接口名和参数,最多给一个curl请求
  3. 此时通过在dev环境里的日志可以解决还好,需要加日志甚至打断点怎么办?不断发布重启甚至开放调试端口吗?

为此我们需要一种可以在开发本地机器,按照网关格式调用本地dubbo服务的方案

本篇内容主要讲述本地调试,以下代码片段全部来自于plume

如何打造一个Dubbo网关--异常处理

在实际业务中,我们通常会自定义一些异常,这些异常不用在业务代码里捕获,通常是全局处理的。全局异常处理不仅能让代码简洁明了、简化开发,更能确保不漏掉异常处理

比如Spring中,通常会使用@ControllerAdvice标注类,在其中处理未在业务代码中直接捕获的异常。@ExceptionHandler(Exception.class)就可以保证异常处理的下限:不给前端非json数据

在分布式系统中,我们也希望能由网关来处理所有的自定义异常

本篇内容主要讲述异常包装和传递,为此需要借助dubbo的consumer-filterprovider-filter,以下代码片段全部来自于plume

如何打造一个Dubbo网关--组合调用

通过泛化调用一文我们知道一个接口会有两个唯一值:pathNameinvokeName,通过这两个值定义了两种调用方式

  • 通过pathName的被称为路径调用,入口是/{system}/{clazz}/{method}很明显一次只能调用一个接口
  • 通过invokeName的被称为组合调用,入口时/api,可以一次性调用多个接口,返回一个结果列表

因为网关的实际调用代码就是按照组合调用来的比较复杂,所以将详细的内容放在了软分组、网关注入和平台之后。本篇内容主要讲述调用示例调用完整流程,以下代码片段全部来自于plume

如何打造一个Dubbo网关--数据源

上篇说到的平台还有另外一个重要的职责:统一数据源管理。为什么统一管理呢?又要怎么统一管理呢?

  1. 这里所说的数据源是指需要通过密码登录的中间件,包括但不限于:mysql、redis、es、s3等
  2. 比如线上库不应该给予开发人员权限,但是代码里总是需要配置一个可以读写的账号,这个账号不可能随时变更,而且是由开发人员掌控的
  3. 即使通过Druid之类的连接池加密,加密后的密码和publickey总要存在于服务器上的某个地方:env、启动参数、配置文件
  4. 假设Druid加密可以解决大部分问题,还有redis、es、s3等等更多不支持加密的sdk
  5. 配置中心也是同理,即使可以在管理界面对开发人员隐藏某一部分配置,但明文总存在于服务器上的某个地方,而且很方便就能找到

本篇内容主要讲述数据源注入。以下代码片段大部分来自于plume

如何打造一个Dubbo网关--平台

文档篇中我们成功生成了接口信息数据,并将其保存在了运行时的jar中;在更早的泛化调用中我们知道所有信息都是通过invokeName从数据库里查出来的

我们把连接在其中桥梁称为平台。平台负责接收业务系统上报的接口信息数据,对比其版本并将有变更的写入数据库。为什么要把网关和平台拆分呢:

  1. 网关就应该只作为一个应答器,只承担流量入口、流量转发、限流等和流量相关的职责
  2. 平台负责其它附加的功能,这样拆分后职责更加清晰、明确,出问题也能更快定位
  3. 网关追求高性能和稳定,可能会部署很多份;平台最多有一个standby即可,甚至因为不对外提供服务可以随意启停,方便开发

本篇内容主要讲述文档上报文档展示,文档存储部分虽然代码繁琐但是没有特别需要注意的地方就跳过了,由于代码量较大只会挑部分说明。以下代码片段全部来自于plume