from: https://www.ibm.com/developerworks/cn/webservices/1102_mace_restservicePart3/1102_mace_restservicePart3.html?ca=drs-

基于 SOAP 的 Web 服务和 REST 服务的描述

在本系列的前两篇文章中,作者系统的介绍了 REST 服务的核心概念以及 REST 和 SOAP 服务的实现机理。接下来,我们以获取股价的 Web 服务为例,来看看基于 SOAP 的 Web 服务和 REST 服务的描述、发送请求的方式和响应的格式的不同。

清单 1 所示是一个获取股价的基于 SOAP 协议的 Web 服务。如果不熟悉 WSDL 规范的朋友请参考文献,我们这里不再详述。描述文件看起来很复杂,其实就是两个服务端点,在 service 元素里面描述的两个:StockQuoteSoap、StockQuoteHttpGet。StockQuoteSoap 说明这个服务端点接受 SOAP 协议的的请求并在 SOAP body 里面返回服务的结果。StockQuoteHttpGet 是以 SOAP over HTTP 的方式提供服务。另外还有对端口类型、绑定、消息、输入参数、输出参数的描述,有点像对一个函数签名的详细描述。

清单 1.WSDL 描述的获取股价的 Web 服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:tns="http://www.webserviceX.NET/"
 xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
 targetNamespace="http://www.webserviceX.NET/"
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:types>
   <s:schema elementFormDefault="qualified"
   targetNamespace="http://www.webserviceX.NET/">
     <s:element name="GetQuote">
       <s:complexType>
         <s:sequence>
           <s:element minOccurs="0" maxOccurs="1"
           name="symbol" type="s:string" />
         </s:sequence>
       </s:complexType>
     </s:element>
     <s:element name="GetQuoteResponse">
       <s:complexType>
         <s:sequence>
           <s:element minOccurs="0" maxOccurs="1"
            name="GetQuoteResult" type="s:string" />
         </s:sequence>
       </s:complexType>
     </s:element>
     <s:element name="string" nillable="true" type="s:string" />
   </s:schema>
 </wsdl:types>
 <wsdl:message name="GetQuoteSoapIn">
   <wsdl:part name="parameters" element="tns:GetQuote" />
 </wsdl:message>
 <wsdl:message name="GetQuoteSoapOut">
   <wsdl:part name="parameters" element="tns:GetQuoteResponse" />
 </wsdl:message>
 <wsdl:message name="GetQuoteHttpGetIn">
   <wsdl:part name="symbol" type="s:string" />
 </wsdl:message>
 <wsdl:message name="GetQuoteHttpGetOut">
   <wsdl:part name="Body" element="tns:string" />
 </wsdl:message>
 <wsdl:portType name="StockQuoteSoap">
   <wsdl:operation name="GetQuote">
     <documentation xmlns
     ="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol
     </documentation>
     <wsdl:input message="tns:GetQuoteSoapIn" />
     <wsdl:output message="tns:GetQuoteSoapOut" />
   </wsdl:operation>
 </wsdl:portType>
 <wsdl:portType name="StockQuoteHttpGet">
   <wsdl:operation name="GetQuote">
     <documentation xmlns
     ="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol
     </documentation>
     <wsdl:input message="tns:GetQuoteHttpGetIn" />
     <wsdl:output message="tns:GetQuoteHttpGetOut" />
   </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="StockQuoteSoap" type="tns:StockQuoteSoap">
   <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
    style="document" />
   <wsdl:operation name="GetQuote">
     <soap:operation soapAction="http://www.webserviceX.NET/GetQuote"
     style="document" />
     <wsdl:input>
       <soap:body use="literal" />
     </wsdl:input>
     <wsdl:output>
       <soap:body use="literal" />
     </wsdl:output>
   </wsdl:operation>
 </wsdl:binding>
 <wsdl:binding name="StockQuoteHttpGet" type="tns:StockQuoteHttpGet">
   <http:binding verb="GET" />
   <wsdl:operation name="GetQuote">
     <http:operation location="/GetQuote" />
     <wsdl:input>
       <http:urlEncoded />
     </wsdl:input>
     <wsdl:output>
       <mime:mimeXml part="Body" />
     </wsdl:output>
   </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="StockQuote">
   <wsdl:port name="StockQuoteSoap" binding="tns:StockQuoteSoap">
     <soap:address location="http://www.webservicex.net/stockquote.asmx" />
   </wsdl:port>
   <wsdl:port name="StockQuoteHttpGet" binding="tns:StockQuoteHttpGet">
     <http:address location="http://www.webservicex.net/stockquote.asmx" />
   </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

根据这个服务的描述,我们来看一下怎么调用这个服务。清单 2 和清单 3 给出了调用示例和响应示例。根据描述我们知道,SOAPAction 是 GetQuote,HTTP method 是 GET,这个服务的输入参数是一个 String 类型的股票代码,如 IBM,参数名称是 symbol,服务的端点是 www.webservicex.net/stockquote.asmx。首先如清单 2 所示构建 StockQuoteHttpGet 服务的请求。

清单 2. A SOAP Request 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /stockquote.asmx HTTP/1.1
Host: www.webservicex.net
Content-Type: text/xml; charset="utf-8"
Content-Length: nnn
SOAPAction= "http://www.webserviceX.NET/GetQuote"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
   <GetQuote xmlns="http://www.webserviceX.NET/">
     <symbol>IBM</symbol>
   </GetQuote>
 </soap:Body>
</soap:Envelope>

清单 3 返回的是按照 SOAP 协议封装的调用响应,在 SOAP body 里面,GetQuoteResult 里面放置的是调用结果,返回的是 XML 表示的 IBM 在调用时刻的股价信息,

清单 3. A SOAP response 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
HTTP/1.1 200 OK
Content-Type: text/xml; charset='utf-8'
Content-Length: nnn
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
   <GetQuoteResponse xmlns="http://www.webserviceX.NET/">
     <GetQuoteResult>
<StockQuotes>
<Stock>
<Symbol> IBM </Symbol>
<Last> 144.36 </Last>
<Date> 11/18/2010 </Date>
<Time> 4:00pm </Time>
<Change> 0.00 </Change>
<Open> N/A </Open>
<High> N/A </High>
<Low> N/A </Low>
<Volume> 0 </Volume>
<MktCap> 179.3B </MktCap>
<PreviousClose> 144.36 </PreviousClose>
<PercentageChange> 0.00% </PercentageChange>
<AnnRange> 116.00 - 147.53 </AnnRange>
<Earns> 11.001 </Earns>
<P-E> 13.12 </P-E>
<Name> International Bus </Name>
</Stock>
</StockQuotes>
</GetQuoteResult>
   </GetQuoteResponse>
 </soap:Body>
</soap:Envelope>

从清单 2 和清单 3 可以看出,基于 SOAP 的 Web 服务把 SOAP 请求和 SOAP 响应封装在 soap Envelope 中,服务的调用端需要自己构建这个 SOAP 信封,并且需要一定的 code 去做解析工作。一般来说,XML 的解析是一项复杂度比较高的任务,比较耗时,这将会影响整个程序的性能。

下面我们来看一下以 REST 服务的方式怎么提供和清单 1 对应的股票查询的能力。首先我们还是来看一下服务的描述,如清单 4 所示。

清单 4. 获取股价的 REST 服务的描述
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<service xmlns="http://www.ibm.com/rest/description/1.0/">
 <title> Stock quote for a company Symbol </title>
 <template httpMethod="GET"
url=" http://www.webservicex.net/stockquote.asmx/GetQuote?symbol={symbol}"/>
 <parameter name="symbol" required="true"
 defaultValue="IBM" style="template"/>
</service>

和清单 1 比较,清单 4 显得特别简洁明了,语义也特别清楚。这给程序员的处理程序很大的简化的可能性。清单 5 和清单 6 显示了获取股价的 REST 服务的调用。从清单 5 可以看出,请求的发送非常的简单,仅仅是一个 HTTP url,而清单 6 显示的查询结果要清单 3 的查询结果看起来语义要清楚很多。

清单 5. A REST Request over HTTP 示例
1
2
GET /stockquote.asmx/GetQuote?symbol=IBM   HTTP/1.1
Host: www.webservicex.net

清单 6. A REST Response over HTTP 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
HTTP/1.1 200 OK
Date: Fri, 12 Sept 2010 17:15:33 GMT
Last-Modified: Fri, 12 Sept 2010 17:15:33 GMT
ETag: "2b3f6-a4-5b572640"
Accept-Ranges: updated
Content-Type: text/xml; charset="utf-8"
<StockQuotes>
<Stock>
<Symbol> IBM </Symbol>
<Last> 144.36 </Last>
<Date> 11/18/2010 </Date>
<Time> 4:00pm </Time>
<Change> 0.00 </Change>
<Open> N/A </Open>
<High> N/A </High>
<Low> N/A </Low>
<Volume> 0 </Volume>
<MktCap> 179.3B </MktCap>
<PreviousClose> 144.36 </PreviousClose>
<PercentageChange> 0.00% </PercentageChange>
<AnnRange> 116.00 - 147.53 </AnnRange>
<Earns> 11.001 </Earns>
<P-E> 13.12 </P-E>
<Name> International Bus </Name>
</Stock>
</StockQuotes>

分析 SOAP 的 Web 服务和 REST 服务的关系

现在你被认为已经清楚了基于 SOAP 的 Web 服务和 REST 服务的描述,以及已经会调用他们。接下来,我们来看一下这两种服务的逻辑关系。

  1. 面向方法和面向资源。从清单 1 可以看出,SOAP 服务是按照面向方法的方法论来设计的,需要服务提供者清楚的给出每个方法的名称、输入参数、输出详细描述、绑定等等,这些又再次封装在消息 message 中。而从清单 4 中我们可以看出,REST 服务是面向资源的,服务提供者只需要告诉用于定位到服务的 URL template 以及要实例化这个 template 所有的参数描述。为了使这个服务可以工作,所以这里我们用了 http://www.webservicex.net/stockquote.asmx/GetQuote?symbol={symbol},但是更好的 URL 格式应该是 http://www.webservicex.net/stockquote.asmx/Quote?symbol={symbol},也许你已经发现了,两个 URL 只是 GetQuote 和 Quote 的差别。奥妙就在这。GetQuote 看起来像一个方法名称,而 Quote 是一个名词,是一个资源。知道了这个差别,可以把 SOAP 服务的输出作为一种资源,对应提供 REST 服务。
  2. 参数对应。在 SOAP 描述文件中我们看到调用一个 SOAP Action 所需要的输入的详细描述。这些参数是系统提供服务所要求的必须的输入。而在 REST 服务中,用户看到的就是一个 URL,所以,我们可以把 SOAP Action 的输入用 query string 的形式放到 REST 服务的 URL template 中。之所以叫 template,是因为不同的输入会对应不同的 URL 示例,也就是说对应到不同的资源示例。

知道了两种服务间的逻辑关系,接下来,我们开始用程序把 SOAP 服务转化成 REST 服务,当然,如果系统需要,你也可以把 REST 服务转成 SOAP 服务。

SOAP Web 服务和 REST 服务的转换

很多种方式,可以把 SOAP 服务转化成 REST 服务。最直接的方式,程序员可以自己写程序,实现一个 proxy,提供 REST 端点,然后通过 proxy 把 REST 请求转发到 SOAP 端点,然后再实现调用结果的处理。这里我们主要介绍用 IBM 的一些产品来实现转化的方法。IBM WebSphere sMash 和 IBM Mashuphub 都可以实现这种转化。这里着重介绍用 IBM WebSphere sMash 平台实现的方法。使用 IBM Mashuphub 的实现方式请参考 IBM Mashup Center 初探 : 第二部分。

开始之前

  1. 了解 WebSphere sMash

WebSphere sMash 即以前的 Project Zero, 它为快速简便地开发交互式 Web 应用程序提供了开发环境。这个项目的目的是为 Web 开发提供一个完整的基础设施,让应用程序开发人员可以将注意力集中在业务逻辑上。花一些时间浏览和熟悉  Project Zero Web 站点。可以加入 Project Zero 社区、为这个项目做贡献,或参与论坛,在各个开发阶段对项目进行评价。本文只要求您的计算机上安装了合适的 Java™ Development Kit (JDK)。

  1. 创建 WebSphere sMash 开发环境

遵循下面的步骤,创建 WebSphere sMash 开发环境。

Step 1:从 WebSphere sMash 网站下载工具包 zero.zip。

Step 2: 解压 zero.zip 到任意文件夹。如图 1 所示。

图 1. 解压 zero.zip 到任意文件夹

Step3: 设置环境变量 ZERO_HOME 和 Path。如图 2 所示。

图 2. 设置环境变量

图 2. 设置环境变量

Step4: 在命令行运行 zero resolve 命令。如图 3 所示。

图 3. 命令行运行 zero resolve 命令

Step5: 创建 eclipse 开发环境,详细步骤请参考 Plug-in for Java and Groovy。

Step6: 创建一个 WebSphere sMash 新项目,取名 SOAP2REST。如图 4 所示。

图 4. 创建 zero 项目 SOAP2REST

开始转换

sMash 提供了一个组件叫 zero.connection.soap.REST2SOAPHandler,它封装了 SOAP 协议,负责构建 SOAP header,发送 SOAP 请求,调用成功后,它返回 SOAP body。所以程序员只需要做其中很小的一部分工作就可以完成 SOAP 到 REST 的转换。按照下面的步骤完成转化的过程。

Step 1: 在 zero.config 文件里面添加如清单 7 所示的代码片段,配置转换 hanlder 以及转换的对应关系。

清单 7. 在 zero.config 中申明 REST2SOAPHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/config/connection/destinations += {
 "http://localhost:9980/cms/*": {
 "handlers" : [
 { "class" : "zero.core.connection.handlers.logger.SimpleJavaLoggerHandler.class" },
 { "class" : "zero.connection.soap.REST2SOAPHandler",
 "config" : {
   "endpointAddress" : "http://www.webservicex.net/stockquote.asmx",
   "SOAPVersion" : "1.1",
   "r2sMapping" : [
     {
      "RESTOperation" : "POST",
      "SOAPBodyTemplate" : "stockquoterequest.gt",
       "URLMatch" : "/cms/stockquote/{symbol}",
"SOAPAction": "http://www.webserviceX.NET/GetQuote"
        }
             
        ]
          }
        },
{ "class" : "zero.core.connection.handlers.logger.SimpleJavaLoggerHandler.class",
    "config" : {
"request" : { "keys" : ["/connection/request/body", "/connection/request/soapHeaders"] },
"response" : { "keys" : ["/connection/response/body",
 "/connection/response/soapHeaders"] }
        }
      }
    ]
  }
 }

在清单 7 中,配置 REST2SOAPHandler 的各种参数,比如 endpointAddress,SOAPVersion,r2sMapping,实现类等。在 r2sMapping 中,配置 SOAP 和 REST 的对应关系,SOAP 操作由 SOAPAction 属性指定,相对应的 REST 的操作属性由 RESTOperation 指定;另外需要指定的是 SOAPBodyTemplate,用 gt 格式的文件指定;URLMatch 表明了 SOAP 服务端点和 REST 服务端点的对应。在清单 8 给出了 stockquoterequest.gt 的内容。

清单 8. 指明 SOAP 请求 Header 中的内容
1
2
3
4
5
6
<%
// the SOAP request body
%>
<GetQuote xmlns="http://www.webserviceX.NET/">
     <symbol><%=r2s_getParam("symbol")%></symbol>
</GetQuote>

其中 r2s_getParam("symbol") 指的是从 REST 请求的 request 里面取出来参数的值。比如 REST 请求是 http://localhost:9980/stockquote.gt?symbol=IBM,那么 r2s_getParam("symbol") 的值就是 IBM.

指明了 handler 和 SOAP 请求后,我们需要创建一个 public 的 zero resource,在 public 目录下面,我们把这个 resource 叫做 stockquote.gt 吧,清单 9 给出了具体的内容。

清单 9. 声明一个 zero 的 public 的资源 stockquote.gt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<%
import zero.core.connection.*;
def symbol = java.net.URLEncoder.encode(request.params.symbol[])
logger.INFO{symbol}
%>
<%
try {
// 这里的 URL 应该和 zero.config 中的 URL 对应,并指明 REST 的操作为 POST
conn = new Connection("http://localhost:9980/cms/stockquote/${symbol}",
  Connection.Operation.POST);
// 指明 content-type
conn.addRequestHeader("Content-Type", "application/xml");
// 发送请求
resp = conn.getResponse();
body = resp.getResponseBodyAsString();
if (body == null) {
throw new Exception("Response body incorrect: " + body.toString());
}
// 取出服务返回的相应
def respObj = zero.json.Json.decode(body);
request.json.output = respObj;
// 指明一个 gt 用来处理返回的相应,如清单 10
request.view = "stockquote.gt";
render()
} catch (Exception e) {
e.printStackTrace();
print("<p><strong>Test failed!</strong></p><p>
"+zero.util.XMLEncoder.escapeXML(e.toString())+"</p>");
}
%>

声明了 public 的资源后,用户就可以用 http://localhost:9980/stockquote.gt?symbol=IBM 的方式访问资源了。

清单 10. 处理返回的相应的 groovy 模板
1
2
3
4
5
6
<%
headers.out."Content-Type" = "application/xml"
def respObj = request.json.output[]
def stockquote = respObj.GetQuoteResponse.GetQuoteResult
print(stockquote);
%>

结束语

本文作为 REST 服务最佳实践的第三篇,通过一个实际的例子,从两种不同类型的 Web 服务的描述入手,辅助于两种不同技术实现的 Web 服务的调用实例,详细介绍 SOAP Web 服务和 REST 服务的关系,并示例介绍基于 WebSphere sMash 的 SOAP Web 服务和 REST 服务的转换,从而使程序员可以轻松的利用已有系统的功能,快速构建 REST 服务。

把 SOAP 服务转化为 REST 服务(REST Service 的最佳实践,第 3 部分)相关推荐

  1. 公开发布版的Windows Azure 基础结构服务中的 SQL Server – 文档和最佳实践(已更新),还有即将发布的博客...

    一周前,WindowsAzure 刚刚宣布公开发布版的基础结构服务正式推出, 这标志着WindowsAzure从此开始完全支持基础结构即服务,SQL Server是其中的一个主要组件. 预安装的SQL ...

  2. 日期格式化为yyyymmdd_大厂日期时间处理最佳实践

    1 背景 Java8前,处理日期时间时,使用的"三大件" Date Calender SimpleDateFormat 以声明时间戳.使用日历处理日期和格式化解析日期时间.但这些类 ...

  3. [转]在 Azure 云服务上设计大规模服务的最佳实践

    本文转自:http://technet.microsoft.com/zh-cn/magazine/jj717232.aspx 英文版:http://msdn.microsoft.com/library ...

  4. 【微服务架构】微服务与SOA架构(3)

    前文: [微服务架构]微服务与SOA架构(1) [微服务架构]微服务与SOA架构(2) 比较架构特性 组件(component)是软件中的一个单位,具有定义良好的接口.定义良好的角色/责任集合.组件是 ...

  5. RESTful服务最佳实践

    本文主要读者 引言 REST是什么 统一接口 基于资源 通过表征来操作资源 自描述的信息 超媒体即应用状态引擎(HATEOAS) 无状态 可缓存 C-S架构 分层系统 按需编码(可选) REST快速提 ...

  6. 都在说微服务,那么微服务的反模式和陷阱是什么(二)

    译者:程超 译文:http://www.jianshu.com/p/c76f7f234a31 上篇:<都在说微服务,那么微服务的反模式和陷阱是什么(一)> 六.无因的开发者陷阱 名字来自詹 ...

  7. SOA系列文章(二):服务设计原理:服务模式和反模式

    服务设计系列的法则已经发展到最佳通信实践和取样相关编码的程度.本文提供了设计和实现网络服务的基本原理,并且对面向服务的体系结构(SOA)的相关概念做了一个简要的回顾,以及有关于几种模式和反模式的详细讨 ...

  8. Windows Azure 安全最佳实践 - 第 6 部分:Azure 服务如何扩展应用程序安全性

    多种 Windows Azure服务可以帮助您将应用程序安全性扩展到云. 有三种服务可提供多个提供程序之间的身份标识映射.内部部署数据中心间的连接和相互发送消息的应用程序功能(无论应用程序位于何处). ...

  9. 服务的协作:服务间的消息传递——《微服务设计》读书笔记

    在微服务集成--<微服务设计>读书笔记文章中,我们说过服务间的消息传递有几种方式,一种是请求/响应技术,另一种是基于事件的机制. RPC(远程过程调用) RPC是Remote Proced ...

最新文章

  1. 关系型数据库设计要领(值得收藏)
  2. 40年产权的商业地产,个人投资者决不能碰
  3. linux 逆向工具 radare2入门
  4. 常见的网站服务器架构有哪些?
  5. 数据的交换输出【杭电-2016】 附题
  6. 操作系统之文件管理:5、文件物理结构(连续分配、链式(显式、隐式)分配、索引分配(链接、多层索引、混合索引))
  7. Javascript第二章中While/do..while循环第三课
  8. 宏定义处理特殊字符 -_c语言编译与预处理命令
  9. 【韩顺平 零基础30天学会Java】(第一阶段)(自用)
  10. 工程点点app爬虫和sign算法破解
  11. upload上传, 取各类文件的名字
  12. javaee 学习书籍推荐
  13. 基于AD603的AGC电路
  14. Python中用PyPDF2拆分pdf提取页面
  15. linux 文本随机抽样_Linux命令总结
  16. 颜色大全:颜色名称和颜色值。色板、色板对照表1
  17. excel斜线表头的制作
  18. 利用wireShark抓取iphone手机上的网络通信包
  19. 梅特卡夫法则(Metcalfe's law)
  20. 应用-单向链表-数据结构和算法

热门文章

  1. 设备控制接口(ioctl 函数)
  2. Google开源OCR项目Tesseract安装版在Windows下的使用测试记录
  3. 利用Canny边缘检测算子进行边缘检测的原理及OpenCV代码实现
  4. flannel源码分析--newSubnetManager
  5. leetcode算法题--二维区域和检索 - 矩阵不可变
  6. TCP第四次挥手为什么要等待2MSL(最长报文段寿命,Maximum Segment Lifetime)
  7. 定题信息服务是从什么角度_格木教育谢浩浩:事业单位综合应用概念分析题之角度界定技巧...
  8. ValueAnimation 原理分析
  9. 模型评估——ROC、KS
  10. IBM服务器指示灯报警说明