前言 什么是 API?
什么是 SDK?
两者之间有何关系?
欢迎来到本次的每周一问系列。
既然点进来了,相信你或多或少都听说过这两个名词了,因此,在为你解答之前,让我们先从一个例子出发。
假如你想开发一个 OCR 应用(通俗的说就是文字识别应用),他的功能是识别用户上传的一张图片,然后将图片中的文字识别出来返回给用户。如下图所示:
通常,OCR 应用的后端服务都会部署在云上,那么我们应该如何在移动应用程序与基于云的服务之间进行通信呢?
这就是 API 和 SDK 的用武之地了。
API API 的特点 通信 首先我们要明白的是 API 是和通信有关的,是用于应用(服务)与其他应用(服务)对话所定义的协议。在上述例子中,你可以简单理解为 API 是 OCR 应用和云端服务之间沟通的桥梁。
那么 API 到底是什么?
API 全称 Application Programming Interface,即 「应用程序接口」 。
一般是指一些预先定义的函数,目的是供应用程序与开发人员基于某软件或硬件得以访问一组程序的能力,而又无需访问源码,或理解内部工作机制的细节。
以 Java 为例,当你想要实现一个数组排序的功能时,你是会先手写一个排序算法,还是直接使用Arrays.sort()函数?我想你心里是有答案的。
抽象 其次,我们要理解,API 的另一个重要特点——抽象。
抽象指的又是什么?
还是以这个 OCR 应用为例,当我们在使用云端提供的文字识别能力时(比如百度文字识别),他的背后可能会有成千上万的代码,比如提供识别能力的机器学习的代码、提供 Web 能力的后端代码等等。
但是你作为一个 APP 的开发者,你需要去看这些代码是怎么写的吗?难道不知道背后的源码就不能调用百度提供的文字识别能力了吗?当然不是。
通常服务商已经给你提供了文档,告诉你如何去调用相应服务,只要你按照他的要求来即可。
因此,在你的 APP 和 OCR 服务之间,API 抽象出所有复杂的逻辑,简化了调用过程,这使得你只需要考虑获取所需的数据即可。
标准化 API 是标准化的,这意味着存在有关如何定义 API 的行业标准,比如 SOAP、REST、GraphQL 等。...
IP 地址怎么定位?
我们经常可以在影视作品中见到某某组织通过对某个人的 IP 地址进行监控,定位其位置,甚至精确到某栋大楼的某一层,如此可怕的场景在现实生活中真的有可能会发生吗?
先来说结果,仅通过 IP 地址最精确能够达到街道级别。而且在不通过运营商的用户数据库查询情况下,定位到家庭住址和单元楼的情况难度很高。
ISP 在《互联网是如何工作的》一文中,我们介绍道,IP 地址是类似于现实世界中的地址这样的东西,通过 IP 地址,我们就可以在网络上定位到一台计算机,在现实世界中,IP 地址是由一个叫互联网服务提供商,即 ISP 提供的。
回想一下我们上网的过程:
去运营商(移动、电信、联通)办理宽带业务 -> 业务员上门拉线 -> 电脑连接宽带 -> 访问互联网 在这一过程中,三大运营商就是我们能接触到的 ISP 的,ISP 可以从互联网管理机构申请到很多 IP 地址,然后一些机构和个人从某个 ISP 获取 IP 地址的使用权,并可通过该 ISP 连接到互联网。
那么 ISP 又是如何标记 IP 地址的地理位置的呢?
我们先来说说 ISP 的三层结构。
三层 ISP 结构分为主干 ISP,地区 ISP,本地 ISP。本地 ISP 给用户提供最直接的服务,本地 ISP 可以连接到地区 ISP,也可以连接到主干 ISP。从原理上讲。只要每一个本地 ISP 都安装了路由器连接到某个地区 ISP,而每一个地区 ISP 也有路由器连接到主干 ISP,那么在这些相互连接的 ISP 的共同作用下,就可以完成互联网中的所有的分组转发任务。
这里我们通过计算机网络教材中的一张图来理解三层结构,如图 1 所示 👇
也就是说,当你向好友发了一条微信,这条微信首先会从你所在公司/学校的内网上发到当地的服务器,再从当地服务器发送到地区服务器,之后从地区服务器通过移动/联通/电信的服务器向你好友所在地的地区服务器转发,再通过本地服务器最终转发到好友所在公司/学校的内网。
如此一来,既然你的 IP 地址是由当地的 ISP 分配给你的,自然也就知道了你所在的 IP 地址的大致位置了。...
为什么不在 for 循环里捕获异常?
在回答标题这个问题之前,我们先试想一下,在没有 try…catch 的情况下,如果想要对函数的异常结果进行判断,我们应该怎么做?
异常 第一个想法肯定就是 if…else 了,一般情况下,相关的代码段我们都是放在一起的,如果此时你的程序中有大量的代码段要做这做判断,这就意味着后面执行的逻辑会依赖你前面语句的执行情况,也就意味着你每调用一个可能会出现错误的函数的时候,都要先判断是否成功,然后再继续执行后面的语句。这就会导致你的代码中会充斥着大量的 if…else。 Java 是一门工程性的语言,而工程也是一种艺术,因此采用这样的做法显然是很不优雅的。《Thinking in Java》中提到“badly formed code will not be run.”,意思是结构不优雅的代码不应该被执行,于是一个适用于 Java 的异常处理机制便应运而生了。 Java 的异常处理其目的在于通过使用少于目前数量的代码来简化大型程序,举个简单的例子 🌰 不用 try…catch
FileReader fr = new FileReader("path"); if (fr == null) { System.err.println("Open File Error"); } else { BufferedReader br = new BufferedReader(fr); while (br.ready()) { String line = br.readLine(); if (line == null) { System.err.println("Read Line Error"); } else { System.out.println(line); } } } 用了 try…catch...
为什么我们需要批量操作?
背景 实习的时候被问过一个问题,为什么 redis 会有 pipline,mysql 会有 batch,这些东西都具有批量操作的共性,是什么原因让我们在处理数据时需要批量操作?
这么说可能有些抽象,举一个和 API 调用有关的例子 🌰:
现有三个服务 service A、service B 和 service C。因业务需要,我们需要在 service A 中调用 service B 获取一组 id,然后根据 id 从 service C 中读取最终内容。然后组织成结果返回前端。由于 service C 只提供了单个 id 查询内容的 API,所以如果我们想要获取批量的信息,最先想到的办法是通过 for 循环多次调用 service C。但是这样的办法是极其不优雅的,接下来我们从以下两个方面来分析。
网络通信 鉴于现在的分布式架构,每个 service 都分布在不同的服务、不同的机器中,所以我们每次调用都要通过 RPC 来实现,这就要求我们不得不构造同等数量的请求来获取数据。这样就会导致了一些效率问题。如下图所示:
所以我们通常会通过在 service C 中提供一个批量查询的接口来解决多次通信的问题。如下图所示 👇
我们知道,并不是每一次网络传输都非常稳定,中途可能会遇到丢包等一系列问题,而用批量查询代替 for 循环单个查询,这样做的好处是,我们可以减少网络通信的次数,一定程度上可以增加整个系统的健壮性。
数据查询 解释完多次 rpc 调用可能造成的网络延迟的问题后,我们再往深一点的地方看。
一般情况下,数据都是存放在数据库中的,所以无论是单个查询还是批量查询,我们最终都是要访问到数据库的。
现假设,我们需要从数据库中查询一个 id 为 123 的用户信息,我们可以用类似下面这样的代码。
long id = 123; Person p = serviceA....