• 服务端接口测试之JSON、Session、Cookie、Token以及mitmproxy


    接口八问

    1. 接口的请求地址是什么?
    2. 接口的功能描述是什么?
    3. 请求接口是GET还是POST?
    4. 接口需要在登录情况下才有用吗?
    5. 接口有上送数据吗?上送的数据是什么?
    6. 接口返回的状态码是多少?
    7. 接口返回报文体的格式和编码是什么?
    8. 接口返回的内容是什么?

    1.接口测试相关概念

    接口测试在分层测试中的价值与质量关注点

    可以参考博文:https://www.cnblogs.com/String-song/p/12964406.html

    接口:定义了标准的规则(输入参数和输出结果)。接口,两个不同系统(或子程序)交接并通过它彼此作用的部分。

    协议:两个或两个以上实体为了开展某项活动,经过协商后双方达成的一致意见。

    通信协议:双方实体完成通信或服务所必须遵循的规则和约定。简单说,通信协议就是一种语言,两个实体共同约定的一种双方都懂的语言,完全互相通信。

    接口测试:测试特定接口在给定输入下的行为与预期行为之间的符合性。

    接口协议:需要通过接口进行信息交换的通信双方之间需要遵从的通信方式和要求。

    HTTP协议:超文本传输协议,浏览器和web服务器网站之间为了完成互相通信而共同约定的一种语言。HTTP基于TCP/IP传输数据,默认端口是80,GET/POST都是HTTP协议中最常用的2中方法。

    参考博文:https://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html

    接口定义的基础:对接口的功能、调用的前提条件、调用的方法、接口返回内容的描述。

    web架构的基础:组件、连接器、数据。

    Web动静分离的结构:就是前后台分离。动静分离:动态内容的实现逻辑和静态内容的展示逻辑的分离。彼此交互方式采用ajax接口方式。

    Ajax:多种技术的联合使用,包括2种技术。Ajax接口就是数据交换接口,实际上就是一个普通的HTTP协议接口。

    1. 通过浏览器的XMLHttpRequst对象,驱动浏览器和web后台服务器实现异步的数据交换。
    2. 通过JS的DOM操作功能,实现浏览器中已经加载的网页的动态更新,而不需要重新加载整个页面。

    接口测试分层模型:

    1. 接口文档测试:明确测试类型(json串?)、必填项、非必填项、特殊字段的测试、有效数字保留、枚举值、特殊定义类型。
    2. 内部业务逻辑测试:测试接口的内部逻辑。
    3. 数据库存储测试:插入值是否正确、更新哪些字段是否正确。
    4. 其他异常流测试:数据库事务性测试。

    2.常见接口协议解析

    TCP/UDP/HTTP/RESTFul/Dubbo等协议区别与解读

    HTTP、UDP:无状态协议;TCP、FTP:有状态协议。

    无状态协议:在下一次链接不记住这一次链接信息。即一种把每个请求作为之前任何请求都无关的独立事务的服务器。

    Rest架构风格:REST是一种互联网软件架构原则。REST风格的架构就是一种将要操作的业务数据作为资源、分配给一个固定的URL地址,然后通过HTTP的四个请求方式:POST、DELETE、PUT、GET,分别对应业务数据的增删改查。

    RESTful架构:(Representational[表现层指的是资源的表现层] State Transfer[状态转化]),简称REST,REST具有跨语言、跨平台的特点,称为REST架构。所有状态保存在服务器端。

    1. 每一个URI代表一种资源。
    2. 客户端和服务器之间,传递这种资源的某种表现层。
    3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

    Restful接口:REST架构组件之间的通信接口,采用HTTP作为通信接口协议。一个Restful接口与一个普通的HTTP请求一致。

    Dubbo协议:与HTTP、FTP、SMTP等应用层协议并列的概念。Dubbo协议是阿里巴巴实现的一种应用层协议,传输层还是TCP。

    Web Services相关概念与技术:

    3 初始HTTP

    3.0 知识储备:urlencoded格式[表单格式]

    【urlencoded格式】:form格式,或者是x-www-form-urlencoded格式。

    1. 表达方式:表单格式由键值对组成。键和值之间用=,多个键值对之间用&。比如:name=wendy&age=20

    3.1 知识储备:JSON数据格式

    【json格式】--重点

    1. json有对象、数组两种数据结构,有字符串、数字、空值(NULL)、逻辑值四种数据类型。
    2. 用大括号{}表示对象。对象由属性组成的,属性由键值对组成,键和值之间用冒号隔开,属性之间用逗号隔开,键必须用双引号引起来。
    3. 用中括号[]表示数值,数值由单独的值组成。
    4. json可以任意嵌套。

    1.JSON是什么?

    • JSON:JavaScript Object Notation (JavaScript 对象表示法)
    • JSON类似于XML,存储和交换文本信息的语法;同时JSON比XML更小、更快、更易解析
    • JSON具有层级结构(值中存在值)、纯文本、具有自我描述性、独立于编程语言、轻量级的文本数据交换格式

    2.JSON语法

    • JSON语法规则:是JS对象表示法语法的子集。数据在"名称/值对"中、由逗号分隔、{}保存对象、[]保存数组。

    JSON值:数字(整数或浮点数)、字符串(在双引号中)、逻辑值(true或false)、数组(在[]中)、对象(在{}中)、null。

    #JSON数字
    { "age":30 }
    #JSON对象,在{}中书写,对象可以包含多个名称/值对
    {key1 : value1, key2 : value2, ... keyN : valueN }
    #JSON数组,在[]中书写,数组可以包含多个对象
    {
        "sites": 
        [
            { "name":"菜鸟教程" , "url":"www.runoob.com" }, 
            { "name":"google" , "url":"www.google.com" }, 
            { "name":"微博" , "url":"www.weibo.com" }
        ]
    }
    #JSON布尔值,可以设置为true或false
    { "flag":true }
    #JSON null
    { "runoob":null }
    #JS使用json
    var sites = [
        { "name":"runoob" , "url":"www.runoob.com" }, 
        { "name":"google" , "url":"www.google.com" }, 
        { "name":"微博" , "url":"www.weibo.com" }
    ];
    document.getElementById("name1").innerHTML=sites[0].name;
    sites[0].name="课程";
    document.getElementById("name2").innerHTML=sites[0].name;

    3.JSON对象或数组

      对象语法:{}中书写。对象可以包含多个key/value(键/值)对,key必须是字符串,value可以是合法的JSON数据类型(字符串、数字、对象、数组、布尔值、null)。

      访问对象值:使用点号(.)来访问对象的值。或者使用([])来访问对象的值。

    #JSON实例,来自字符串的对象,可以通过eval()函数,且必须把文本包围在括号中,可以通过点号(.)或([])调用数据。
    var txt= '{"employees":['+
    '{"firstName":"John" , "lastName":"Doe" },' +
    '{"firstName":"Anna" , "lastName":"Smith" },'+
    '{"firstName":"Peter" , "lastName":"Jones" }]}';
    
    var obj = eval ("(" + txt + ")"); 
    document.getElementById("fname").innerHTML = obj.employees[1].firstName
    document.getElementById("lname").innerHTML = obj.employees[1].lastName
    document.getElementById("fname").innerHTML =obj.employees[1]["lastName"]
    document.getElementById("lname").innerHTML =obj.employees[2]["lastName"]
    #JSON实例,来自字符串的对象,可以通过eval()函数,且必须把文本包围在括号中,可以通过点号(.)或([])调用数据。
    var txt= '{"employees":['+
    '{"firstName":"John" , "lastName":"Doe" },' +
    '{"firstName":"Anna" , "lastName":"Smith" },'+
    '{"firstName":"Peter" , "lastName":"Jones" }]}';
    
    var obj = eval ("(" + txt + ")"); 
    document.getElementById("fname").innerHTML = obj.employees[1].firstName
    document.getElementById("lname").innerHTML = obj.employees[1].lastName
    document.getElementById("fname").innerHTML =obj.employees[1]["lastName"]
    document.getElementById("lname").innerHTML =obj.employees[2]["lastName"]
    #JSON数组
    var obj={
    "name":"网站",
    "num":3,
    "sites":[ "Google", "Runoob", "Taobao" ]
    }
    var x="" ;
    for (i in obj.sites){
    x+=obj.sites[i] +"<br>";
    }
    document.getElementById("demo").innerHTML =x;

    4.为什么使用JSON

    对于AJAX应用程序来说,JSON比XML更快更易使用。

    使用XML:

    1. 读取XML文档
    2. 使用XML DOM来循环遍历文档
    3. 读取值并存储在变量中

    使用JSON:

    1. 读取JSON字符串
    2. 用eval()处理JSON字符串

    5.JSON.parse() 和JSON.stringify()

    JSON通常用于与服务端交换数据;在接收服务器数据时一般都是字符串。

    1. JSON.parse()方法将数据(字符串)转换为JS对象。
    2. JSON.stringify()方法将JS对象转换为字符串。

    语法:

    JSON.parse(text[,reviver])

    text必需,一个有效的JSON字符串。reviver:可选,一个转换结果的函数,将为对象的每个成员调用此函数

    JSON.stringify(value[,replacer[,space]]])

    value必需,要转换的JS值(通常为对象或数组)。

    3.2 HTTP协议

      HTTP协议讲解:常用状态码、Header、请求与响应的格式分析。

    HTTP协议中有请求和响应两种报文,两种报文又分为报文头和报文体。

    1. 报文头:传递一些通用的信息或者指定某种行为。
    2. 报文体:实际上就是请求或响应中传递的数据内容。

    3.2.1 HTTP状态码:HTTP Status Code,是网页服务器返回给浏览器用于表示响应状态的代码。

    1XX信息,服务器收到请求,需要请求者继续执行操作
    2XX成功,操作被成功接收并处理
    3XX重定向,需要进一步的操作以完成请求
    4XX客户端错误,请求包含语法错误或无法完成请求
    5XX服务器错误,服务器在处理请求的过程中发生错误
    状态码范围:100-599
    #常用状态码
    200-请求成功
    301-资源(网页等)被永久转移到其他URL
    404-请求的资源(网页等)不存在
    500-内部服务器错误

    通信协议是一种语言,是两个实体之间为了完成互相通信,共同约定的一种双方都懂,都遵守的语言。

    HTTP协议,就是浏览器和web服务器网站之间为了完成互相通信而共同约定的一种语言。

    HTTP协议,基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接(使用统一资源标识符URI来传输数据和建立连接)来交换信息,是一个无状态的请求/响应协议。

    HTTP协议概念:

    1. 目前常用协议HTTP1.1,有8种请求方法,其中最常见的POST和GET
    2. HTTP协议中有请求和响应两种报文,两种报文又分为报文头和报文体
    3. 报文头:用于传递一些通用的信息或者指定某种行为
    4. 报文体:请求或响应中传递的数据内容

    报文头中字段名:

    1. Cookie:请求报文中,用于存放Cookies内容。cookie是指某些网站为了辨别用户身份或存储用户相关信息而储存在用户本地终端上的数据(加密)。
    2. Content-Type:表示后面的文档属于什么MIME类型,Servlet默认为text/plain,但通常需要显式地指定为text/html.
    3. Location,表示客户应当到哪里去提取文档。
    4. Set-Cookie,设置和页面关联的Cookie。Set-Cookie域用来指示浏览器或其他客户端,在本地保存Cookie信息。

    Content-type详解:MIME(多用途互联网邮件扩展类型),就是为了发送邮件的时候,为方便打开不同的内容而制定的一个标准。

    使用CURL发送请求:从已有的请求中自动生成对应的curl命令

    CURL:一个用于通过URLs传递数据的命令行工具和库。curl最常用的是通过命令行来给web网站发送HTTP请求,并将HTTP报文内容在命令行界面输出。

    #curl命令,-d是通过POST请求向URL地址发送数据
    curl -d "name=aa&location=beijing" http://httpbin.org/post
    
    #curl命令,-b是用于指示后面的string或file需要随HTTP请求一起发送Cookie信息
    #curl命令,-v是用于以对话的方式将请求发送的HTTP报文和服务端的响应HTTP报文的详细信息都显示出来
    curl -v -b "name=aa;location=beijing" http://httpbin.org/cookies

    3.3 python的requests的post()和get()

      1.python的requests.get()或requests.post(),都会返回一个response对象。

    #Response对象常用的属性和方法
    Response.text:文本格式输出的返回报文内容。
    Response.json():用JSON格式编码的返回报文内容,注意这个返回值是字典数据类型。
    Response.status_code:服务器响应的状态码,是一个数字类型。
    Response.headers:服务器响应头,数据类型为requests.sturctures.CaseInsensitiveDict。本质是一个字典数据类型。大小写不敏感
    Response.cookies:服务器响应的cookies
    eg:
    import requests
    r = requests.get("http://httpbin.org/get?name-'akui'&email-'a kui@163.con")
    print(r.text)
    print(r.json())
    print(r.status_code)
    print(r.headers)
    print(r.cookies)

      2.通过GET请求发送带参数的HTTP请求和POST请求带参数的HTTP请求。

    #requests的post()方法
    import requests
    import json
    name = input("请输入您的姓名:")
    email = input("请输入您的邮箱:")
    data ={"name":name ,"email":email}
    result=requests.post( "http://httpbin.org/post",data=data)
    print("返回的状态码:",result.status_code)
    content =json.dumps(result.json(),ensure_ascii=False,indent=4)
    print("返回的内容为:",content)
    
    #requests的get()方法
    import requests
    strIp =input("请输入你要查询的IP地址:")
    param ={"IP":strIp}
    # result = requests.get("http://ip.taobao.com/instructions/outGetIpInfo",params=param)
    result = requests.get("http://ip.taobao.com/service/getIpInfo.php",params=param)
    print(result.content)
    print("你输入的IP地址所在的国家:"+result.json()["data"]["country"])

      3.post()方法提交时,到底是用data还是json,取决于请求头中的content-type字段,是用何种方式进行编码。

    #HTTP协议,Post提交的方式,取决于Content-Type字段。
    #Content-Type字段:获知请求中的消息主体是用何种方式进行编码,在对消息主体进行解析
    application/x-www-form-urlencoded:以form表单形式提交数据
    application/json :以json串提交数据。
    multipart/form-data:上传文件
    
    #eg1:提交form表单,只需要将请求的参数构造成一个字典,然后传给data参数。
    url = 'http://httpbin.org/post'
    d = {'key1': 'value1', 'key2': 'value2'}
    r = requests.post(url, data=d)
    print r.text
    
    #eg2:提交json串,主要用于发送ajax请求,动态加载数据。
    #data进行json编码,在发送
    url = "http://jinbao.pinduoduo.com/network/api/common/goodsList"
    data ={"pageSize":60,"pageNumber":1,"withCoupon":0,"sortType":0}
    r = requests.post(url=url,data =json.dumps(data),headers=headers)
    #或者requests提供了json参数,自动使用json方式发送
    r = requests.post(url=url,json =data,headers=headers)
    
    #eg3:上传文件
    url = 'http://httpbin.org/post'
    files = {'file': open('upload.txt', 'rb')}
    r = requests.post(url, files=files)
    print(r.text)
    requests.post()中报文data参数和json参数
    requests.get()中params参数
    
    eg:
    import json
    <json>= json.dumps(<dict>)  //转换成字典对象,得到一个字符串对象,序列化对象到一个json字符串。
    <dict>= json.loads(<json>)//json.loads()函数用于加载一个JSON编码的字符串,将其进行转换后,返回一个python的字典数据类型的值。
    
    #字典转换成json格式数据,得到字符串对象(unicode),与str类型有区别
    dict = {"a":1,"b":"测试"}
    print(type(dict))
    to_json = json.dumps(dict,ensure_ascii=False)
    print(type(to_json),to_json)
    结果:
    <class 'dict'>
    <class 'str'> {"a": 1, "b": "测试"}

    4  Session、Cookie、Token区别

        为了解决HTTP协议的无状态问题,出现了cookie和session的概念。

      小杨要去一个写字楼, 第一次需要到前台填写登记表格, 登记完成之后前台给了小杨一张通行卡, 这栋大楼内部的安保人员看到通行卡就能确认小杨的身份信息和授权信息.

      这里小杨就是客户端, 通行卡就是Cookie,前台就是服务端, 表格就是Session. 客户端第一次访问服务器, 服务器端保存客户的信息并且给客户端一个Cookie, 客户端携带Cookie去访问服务端, 服务端通过携带的Cookie找出该用户信息. 服务端就能够知道是谁访问了.

      在非Web的情况下,没有Cookie的时候,解决用户身份认证问题:Token。服务端的组件应该类似于提供API的方式,API设计为无状态的,无session、也不依赖Cookie。Token应运而生。

    Token,通常叫令牌,Token=uid(用户唯一身份标识)+time(时间戳)+sign(签名,hash算法压缩成一定长的16进制字符串,可以防止恶意的第三方拼接的token请求服务器)。

    解析:基于真实系统了解session、cookie、token的区别

     4.1 Cookie

    Cookie:服务器发送到用户浏览器并保存在本地的一小块数据。它会在浏览器再次访问时被携带并发送到服务器,用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。

    Cookie作用:会话状态管理(如用户登录状态、购物车或其他需要记录的信息)、个性化设置(如用户自定义设置、主题等)、浏览器行为跟踪(如跟踪分析用户行为等)。

    Cookie类型:会话期Cookie[关闭及删除]、持久性Cookie[Max-Age过期时间限制]、第三方Cookie

    cookie背景:浏览器中的cookie有个数显示,每个站点的cookie不能超过50个;浏览器中的cookie有大小限制,每个站点的cookie大小不能超过4kb.

    Cookie特点:

    1. cookie是分站点的,站点和站点之间的cookie是相互独立的
    2. 浏览器的cookies是保存在浏览器的某个位置的,Cookie保存在客户端,目的是下次访问可以直接调取,上送服务器,一般由浏览器负责存储在本地。
    3. 服务器端可以通过:响应头中的set-Cookie参数,对客户端的Cookie进行管理
    4. 浏览器的每次请求,都会把该站点的Cookie发送给服务器
    5. 加密存储,但保存在本地,数据可靠性不高,不建议保存敏感信息,如密码。
    6. 服务器决定Cookie在客户端本地保存和保存时间。在HTTP协议中通过服务器返回响应报文头中的set-Cookie域来指示浏览器或者其他客户端,在本地保存Cookie。
    7. 在HTTP协议中通过客户端发送给服务器的请求报文头中,有一个cookie域专门用于存放这个信息,方便客户端将Cookie信息发送给服务器。

    4.2 Session

    HTTP是一个无状态的协议。

    session机制

    1. session是一个对象,是服务器产生的,保存在服务器的内存中的。
    2. session有自己的管理机制,包括session产生、销毁、超时等。
    3. sessionID是session对象的一个属性,是全局唯一的,永远都不会重复的。

    Cookie的工作机制是用户识别和状态管理(比如保存用户的用户名、密码、邮箱等)。web网站为了管理用户的状态,通过web浏览器把一些数据,作为Cookies信息临时写入用户的计算机内。当用户再次访问时,会将之前未过期的cookies(假如没有过期的话)取出来,发送给web网站。但存在缺点:

    1. 数据保存在本地客户端,安全性不高。
    2. 每次访问都要发送保存的Cookies数据,当网络访问量大,会浪费网络带宽。

    Session因为Cookie的缺陷产生。Session也是一种管理用户状态和信息的机制。Session的数据保存在服务器端,一般在服务器的内存。客户端和服务端通过一个SessionID(一个较长的随机字符串)来进行沟通。

    sessionID存在的缺点:

    1. 对服务器内存的消化,特别是并发用户量大。
    2. 保存在服务器内存里,web服务器是多个节点的分布式系统,需要进行一些特殊的设计和处理才能实现Session内容在多个节点之间的共享。

     

    比如举例说明:

    用户登录-》服务器产生一个session,同时sessionID响应给客户端

    sessionID通过响应头-》set-Cookie给客户端

    浏览器拿到set-Cookie中的内容,立马保存,存在浏览器的cookie中

    之后每次请求,都会把该站点的cookie传给服务器,包括sessionID

    服务器根据客户端传过来的sessionID进行有效性判断,判断通过则访问,不通过则跳转到登录界面

    当用户发送退出登录请求时,服务器端会把session注销,同时sessionID置空响应给客户端

    浏览器如果拿到set-Cookie中的sessionID为空,则会删除sessionID

     4.3 Token

    可以参考博文:https://network.51cto.com/art/201911/605549.html

    基于Token的验证原理:

    1. 无状态,不将用户信息存在服务器或session中。
    2. token也是由服务器产生的,存在服务器的内存或硬盘中。
    3. token也有一套产生规则,会涉及到加密算法。

    基于Token的身份验证过程:

    1. 用户通过用户名和密码发送请求
    2. 程序验证
    3. 程序返回一个签名的token给客户端
    4. 客户端存储token,并且每次用于发送请求
    5. 服务器验证token并返回数据

    Token实现思路:

    【用token来实现登录】

    开发提供一个获取Token接口,根据用户名+密码,获取一个token值返回一个Token(字符串)

    token值服务器通过什么给客户端的呢:通过响应头给客户端,通过响应消息体传给客户端,通过Cookie传递给客户端

    Token是怎么传递给服务器:通过请求头、通过请求体单独的一个参数

    5 mitmproxy代理工具

    常用的代理工具:fildder、charles、burpsuite、mitmproxy、anyproxy等工具的应用。

    协议层拦截:反向代理、透明代理、普通代理。

    5.1 代理

    代理:一种有转发功能的应用程序,它扮演了位于web服务器和web客户端中间人的角色,接收由客户端发送的请求并转给服务器,同时也接收服务器返回的响应并转发给客户端。

    1. 反向代理:与正向代理对应。反向代理代理的对象是服务端[比如百度,隐藏真实服务端],正向代理代理的对象是客户端[隐藏真实客户端]。
    2. 普通代理:在两端之间来回传送HTTP报文。

    普通代理结构如下:

    代理服务器是网络信息中转站,功能如下:

    1. 共享网络
    2. 提高了访问速度
    3. 突破了访问限制
    4. 隐藏身份

    代理的使用方法有很多,按基类分为两种:一种是否使用缓存,一种是否会修改报文。

    1. 缓存代理:代理转发响应时,会预先将资源的副本保存在代理服务器上。当代理再次接收到对相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
    2. 透明代理:转发请求或响应时,不对报文做任何加工的代理类型。 

    5.2 mitmproxy 使用

    参考博文:https://www.cnblogs.com/fnng/p/13407445.html

    mitmproxy官网:https://docs.mitmproxy.org/stable/howto-transparent/

    1.mitmproxy 安装

    1. 模块安装:使用pip安装,然后在更改代理设置内设置端口为8080。
    2. 证书安装:首次运行mitmdump,会在当前用户生成证书。比如:C:UsersAdministrator.mitmproxy。

    mitmproxy的安装及版本查看:

    #安装
    pip install mitmproxy
    
    #查看版本
    mitmdump --version

     安装成功,结果显示为:

    >mitmdump --version
    Mitmproxy: 6.0.2
    Python:    3.8.5
    OpenSSL:   OpenSSL 1.1.1i  8 Dec 2020

     设置代理:

    成功截取:

     2.mitmproxy 组件

    1. mitmproxy(交互式使用,windows不支持)
    2. mitmdump:提供简单明了的终端输出。
    3. mitmweb(界面):提供基于浏览器的图形界面。

     三者的区别就是所展示的交互面不同。

    参考博文:https://www.cnblogs.com/grandlulu/p/9525417.html

     1.mitmweb介绍

    1. 拦截:修改请求前数据、修改请求后数据
    2. 筛选
    3. 高亮
    4. 置放请求

    3.抓包分析TCP协议:使用TCPdump 与wireshark分析三次握手与四次握手流程

    4.使用Postman发送请求:使用图形化工具构造HTTP请求并发送

    HTTP/HTTPS抓包分析:SSL证书设置与HTTPS抓包

    GetPost区别实战详解:get与post的本质区别与具体抓包解读

    Mock应用:通过代理修改请求与响应、辅助构造更多场景用例

    问题:

    2.接口实现的一个流程

    3.抓包工具

    4.Mock应用

  • 相关阅读:
    「Wallace 笔记」K-D tree 区域查询时间复杂度简易证明
    「LOJ #2980」「THUSCH 2017」大魔法师
    「Wallace 笔记」快速上手回文自动机(PAM)
    「ZJU Summer Training 2020
    「AtCoder AGC002F」Leftmost Ball
    文案高手的18项修炼
    高性能MySQL实战
    300分钟搞懂 Spring Cloud
    腾讯产品启示录
    300分钟吃透分布式缓存
  • 原文地址:https://www.cnblogs.com/wendyw/p/14460512.html
Copyright © 2020-2023  润新知