关于Blocking RPC的思考

前言

近日,考虑到重构之前的系统,涉及到如何设计一个阻塞型RPC调用,场景如下:

Web Server A 想要call 在Web Server B的一个方法F,方法F执行操作为CURD虚拟机,其中启动VM与克隆VM需要花费数分钟级别的时间。

解决思路与分析

1. 同步

实现方式:声明B的方法F为同步方法:A起一个Thread调用F,当成功执行后,执行对应业务逻辑。

Pros:
B的业务逻辑简单。
Cons:
A中维持一大堆的TCP连接,消耗大量TCP资源;同时,导致在不稳定的Network环境下,错误处理逻辑麻烦。

2. 简单异步回调

实现方式:B的方法F为异步方法,A在调用的时候提前约定,或者传入CallBack URL接口以参数形式传入。类似于third party login的解决方案。

Pros:
1. A在调用后立即返回,无需维持大量的TCP连接。
Cons:
1. 增加API的复杂层度,须为每个阻塞型API都创建一个CallBack接口,增大代码的维护难度。

3. 异步回调与MQ解耦

实现方式:B的方法F为异步方法,A调用后返回。另开一个Thread监听

Pros:
1.  通过MQ的方式,解耦了Server A与Server B,同时,接口数量大大减小。
2. 同时,可以打开Log功能已备查验。

Cons:
2. 增加了Deploy成本,当然可以采取Brokerless的MQ如ZeroMQ等来实现。

4. 异步与轮询

实现方式:B的方法F为异步方法,A的调用后,A周期性poll B的查询接口,查询是否完成。其实有点RESTFul的思想。

Pros:
1. 增加与查询接口相分离,减少了A与B的依赖。

Cons:
1. 无中心化的Audit系统,出问题后不易被查验。
2. 增加部分poll的逻辑,增加代码的复杂性。

总结:

引用Guo Yu的那句话,还是看应用场景的复杂度。简言之,
2与3是属于Callback大类的,系统之间通过消息传递来实现业务驱动。
4是属于Poll大类的,系统之间具有自己的高度主动权。


KeepassHTTP:安全的API设计

前言

最近俺迫于找工作压力,好久没有更新Blog,被迫走上了刷Leetcode的苦逼生涯。找工作的间隙,探索了俺一直使用的Keepass密码管理软件,以及其提供HTTP服务的插件KeepassHTTP。无奈,该插件无法与Alfred保持很好的协作,因此,看能否弄一个Python的小脚本,集成到Alfred中,提高生产力。

KeepassHTTP协议

KeepassHTTP协议一句话描述为:通过URL获取密码的HTTP API。

首先,对于密码服务这种要求High Privacy的软件而言,为其设计一个API则首要考虑的必需为安全性的问题。目前,就俺知道的加密的两大类无非为Symmetric与Asymmetric两种机制,其中又以Symmetic最为常用。简言之,加密双方Offline协商好一个共同的Key。以后都用这把Key对所有的Message加密。而KeepassHTTP就是基于这种机制实现Key Exchange。

那简而言之,意味着,Client与Server(KeepassHTTP)都知道一个相同的Key。为了方便MultiUser,我们对于每把Key,都用一个Key Id区分。
{
    "Test Key":"1c3a2......",
    "Another Key":"2d84c......"
}


客户端REQUEST


在KeepassHTTP中,客户端的请求为如下的格式。


该API为HTTP POST请求,因此并非业内所常见的RESTFul设计。但是其在安全性方面还是做了不少功夫。
Nonce字段:设计Nonce字段的根本目的在于防止重放攻击。
俺曾经调研过锅内某信的Web API,其实也是有该字段的,不过叫做Random,其实是一个道理。本质上,做到一次请求一个Nonce。而在KeepassHTTP中,该字段是由一个随机字符串,Base64编码而成。

Verifier字段:用于签名(识别合法客户端)。
在KeepassHTTP协议中,采用了AES-CBC加密模式。一般来说,AES-CBC的算法通常由,Init Vector(IV),AES-KEY,与Data组成。因此,实现中利用Nonce做Init Vector,Nonce做Data,同时TEST Key做AES-KEY来实现对于Nonce的签名后,BASE64编码。
服务器收到Verifier字段后,通过同样的操作比对解密后的Verifier字段与Nonce字段是否匹配来验证客户端的请求。

URL字段:真正的Data
直接利用Nonce,AES-KEY,与真正的Url加密得到,再利用Base64进行编码。

服务器RESPONSE

当服务器通过Verifier字段验证了客户端的合法性后,便在数据库中查询与URL匹配的Data返回了,其返回值如下图。
对于上面的返回值,客户端需要做的首先对Nonce与Verifier做验证,证明该请求来自于正确的服务器。值得注意的是,Response的Nonce与Request的Nonce并不相同,这里还是为了防止重放所做的设计。
其次,对于每条Entries的所有值,利用AES-KEY做解密操作,拿出对应的密码。

关于traceroute的新姿势

前言


话说,为何对trace route产生了兴趣?还是来源于学校的渣渣网络。贵校的渣渣网络其实挺好,人手一个Public IP,只是说,出口处做了防火规则,禁止进入而言罢了。中间一个大的10.0.0.0/8的网络打通全网。

Traceroute

似乎教科书会提及到traceroute利用了ICMP的TTL字段来进行跟踪路由。然后就一笔带过了。因此,俺当年也天真的以为traceroute只能通过ICMP来追踪路由。但是近几日的深入研究后,也是发现,too young, too simple. 为啥俺能发现这个问题捏?其实在于俺交的网管喜欢乱玩firewall喽,老是disable掉icmp。而当网管disable掉ICMP后,俺喜欢用的mtr与traceroute的表现出了差异,不是mtr不能用就是traceroute不能用。
卧槽,不是都用的ICMP嘛,为啥会有差异。好吧,求人不如求已,RTFM。 细细扫了一遍traceroute,俺在最后发现了作者良心的写下了
LIST OF AVAILABLE METHODS 俺总结下:
1. default:
也是最古老的方法,用udp包加unlikely-port来探测。如何做呢,思路为,每一次发送的ttl+1,dst_ip填target ip。沿途的router在ttl发现为0后,回送icmp time exceed。当数据包到达dst_ip后,dst_host回送icmp unreach port,因此trace route通过抓取这两种类型的icmp packet即可。
2. icmp: 
目前最通用的方法,其实也是mtr的默认探测方法。利用icmp的echo request方式来发送packet。
 3. tcp: 
也是一种现在常用的测量方法,主要用来穿越防火墙使用。假设当你去traceroute一台主机时,有可能中间的router会直接filter掉的你的icmp echo request和一些不知名的udp port,因此,利用udp和icmp方式的探测就会立即失效。这时候,tcp方式直接利用tcp的syn包来穿越防火墙。譬如,当traceroute baidu.com时,利用-T -p 80指定tcp port=80,ttl=1,2,3,4,5....
4. udp: 
利用诸如udp port=53一类的正常端口。但是呢,这种方法有问题,就是说,可能最后一跳不认识数据包就不回复了,不过值得庆幸的是,DNS还是要回复的。233.
5. udp-lite: 
咦,这还是门新的协议,简单地说,利用
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);来创建,而传统的udp方式是以socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
6. dccp:
socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)
7. raw:
这种方式就直截了当,直接payload里面啥都不装,朝网络里面扔过去。

<硝烟中的Scrum和XP>

C1:前言
最近接触了个新词儿叫做PMP(Project Management Professional),于是乎追本溯源,追着追着就到了Scrum这边。似乎俺前几周参加个DevOps Meetup,似乎还跟Scrum/Agile有点联系,就认真的找了本书仔细研读起来。

下文是<硝烟中的Scrum和XP-我们如何实施Scrum>的一些摘要。

C2:我们怎么编写Product backlog





Track(类别),Component(组件),Requstor(请求者),Bug Track ID
backlog停留在业务层面:给数据库添加索引->提供查询速度

C3:如何准备Sprint计划

确保product backlog都有importance的打分。

C4:我们怎么制定sprint计划

Output:
sprint goal,
team member,
sprint backlog,
sprint demo data,
everyday scrum meeting.

为什么产品负责人(Product Owner)必须参加
为什么不能在质量上让步
无休止的sprint计划会议.....NO
Sprint计划会议日程
13:00-13:30 po对sprint目标进行总体介绍,介绍产品backlog。确定演示的时间和地点。
13:30-15:00 团队估算时间,必要的情况下拆分backlog条目,PO必要时修改重要性评分理清每个条目的含义。所有重要性高的backlog条目都要填写如何演示。
15:00-16:00 团队选择要放入sprint的故事,计算生产率,用作核查工作安排的基础。
16:00-17:00 为每日scrum会议安排固定的时间和地点。
确定sprint长度:3周
确定sprint目标:我们为什么要进行这个sprint,为什么不直接放假算了。
决定sprint要包含的故事
拷贝product backlog至sprint backlog。估算的生产率
PO如何对sprint放哪些故事产生影响
1.重设impor 2.缩小范围 3.拆分优先级
团队决定把哪些故事放在sprint里面
用本能反映来估算+用生产率计算来估算
我们为何使用索引卡
定义“完成”:故事上加一个字段
使用计划扑克做时间估算:几轮
明确故事内容
把故事拆分成更小的故事:User Mgmnt->Add/Edit User, Query User
把故事拆分成任务QueryUser->Clarify Req, write test case, design UI…..
定下每日例会的时间地点: 9am
最后界限在哪里:
1.sprint目标和演示日期。2.经团队许可,添加到当前sprint的story。3. Sprint中每个故事的估算值。4.sprint每个故事如何演示。5.生产率和资源计算 6.明确每日例会的时间地点。7.把故事拆分成任务。
技术故事
Bug跟踪系统 vs. 产品backlog
Sprint计划会议终于结束了

C5:我们怎样让别人了解我们的sprint

C6:我们怎样编写Sprint backlog

Sprint backlog的形式



C7:如何布置房间

C8:我们怎样进行每日例会

我们怎样更新任务板
每个人描述已经做的事情和今天要做的事情,移动对应的任务帖。
处理迟到的家伙
处理“我不知道今天干什么”的情况

C9:我们怎样进行Sprint演示

坚持所有的工作都结束于演示
Sprint演示检查列表
确保清晰阐述了sprint目标
不要花太多时间演示,不要做演讲
节奏要快,关注于业务,不管技术细节,不要演示bug和微不足道的特性。
处理无法演示的工作:提供报告

C10:我们怎样做sprint回顾

如何组织回顾
根据要讨论的内容范围,设定时间为1-3小时。
参与者包括产品负责人,整个团队
ScrumMaster向大家展示sprint backlog,在团队的帮助下做总结。
轮流发言。每个人都不被打断的情况下讲出自己的想法,什么是好的,哪些可以更好,哪些需要在下一个sprint做改变。
对预估生产力和实际生产率进行比较。如果差异大,分析原因。
快结束时,ScrumMaster对具体建议进行总结,得出下一个sprint需要改进的地方。
在团队之间传播经验
变,还是不变
回顾中发现问题示例

C11:Sprint之间的休整时刻

C12:怎样制定发布计划,处理固定价格的合同

定义你的验收标准
对最重要的条目进行时间估算
估算生产率
统计一切因素,生成发布计划
调整发布计划

C13:我们怎样结合使用Scrum和XP

结对编程
TDD
在新代码上TDD
在旧代码上TDD
增量设计
持续集成
代码集体所有权
充满信息的工作空间
代码标准
可持续的开发速度

C14:我们怎样做测试

你大概没法取消验收测试阶段
把验收测试阶段缩到最短
把测试人员放倒Scrum团队来提高质量
在每个Sprint中少做工作来提高质量
回到现实

邮件礼仪(Netiquette)

前言:

作为互联网从业人员,如果经常混社区的话,一定会注意到有邮件列表(mailing list)这一个概念。

在mailing list中发邮件询问抑或是回答别人的问题,提交PATCH都必须遵守一定的邮件礼仪。在这里,俺就来科普一下所谓的邮件列表的礼仪(Mailing List Etiquette)。

先大概科普下netiquette源自于net与etiquette的组合词,net不必解释,etiquette源自于法语的étiquette(prescribed behavior),更进一步,étiquette源自于古法语的estiquette("label, ticket")。所以,读者可以在听听etiqutte的发音,其实很像e-ticket。那为啥ticket跟礼仪扯上关系了呢?因为,中世纪贵族参与聚会时,通常有ticket作为一个入场券,而普通未受过良好礼仪的阶级,是没有机会的。因此,ticket便跟礼仪产生了联系。

故作正经地瞎扯淡后,我们继续。

本文基本源自于OpenStack Mailing List的相关礼仪和俺当年混mailing-list被喷的一些惨痛教训。想想当年也是too young呵。。

主题(Subject)

关于邮件的主题(subject),应该仔细阅读每个mailing list的相关规定。例如,某些社区可能会规定,在不同的topic前面加上修饰符,发PATCH时,加上[PATCH]的修饰符,等等。例如:

格式化(Formatting)

这一章节,主要是介绍,如何格式化邮件的正文,以及回复邮件正文的一般形式。

纯文本(Plain Text)

在俺混过的几个社区的邮件列表中,几乎都明确地提及到,请发送纯文本格式(Plain Text)的邮件。这一点对于俺也是经常犯的错误。

举个例子,当利用Gmail回复邮件时,不要使用上方的任何格式化工具,且将模式设置为Plain Text Mode。否则,Gmail会自动主张地修改你的引用为HTML格式。

因此,如果使用Gmail的话,我推荐如下的做法。并注意不要使用任何的加粗之类的标记,否则,除了引用外,你的正文会被HTML化。



换行(Line Wrapping)

这个倒是没太多要求是了,只是有的社区可能有要求,每行不超过72个字符。至于为啥是72,历史遗留问题。

回复(Replies)

关于回复,社区通常采用interleaved(交织回复)的风格。这样的回复风格,可以直接在引文的后面回复相应的内容。俺将一个稍微标准的回复的original message和gmail-formatted的对比下,也许读者能够看到,Gmail还是支持“>”的自动识别功能。

多重引用的标注(Reply Level Indication)

关于多重引用,即多次回复的情况下,应该在每次回复将引文标注>符号,最终形成>>..>这样的格式,方便他人参考。见例子。

裁剪(Trimming)

每次引用的时候,不必引用完全文,对于一些没有必要的话,可以适当删除与修改,这样也能保持文章的干净。

实际上,俺在Gmail中,也经常删除引用中一些不必要的话再发送出去。

保持文章线索(Threading)

每个现代邮件系统,均为为每封邮件增加Message-Id字段用来保持追踪每封邮件。因此,也请在邮箱中增加一个说In-Reply-To字段用以标注回复的邮件Message-ID。

在实际使用中,Gmail已经默认为俺创建了这一字段。

网络礼仪(netiquette)

网络礼仪放在这里主要是一些非技术性的问题,需要人为地去保持网络社区的干净与整洁。

避免Cross-Posting

Cross-Posting一听还是跟interleaved有点像,但实际上指的是,别在几个mailing-list同时发相同的内容的PO文,正确的做法应该是,将将另外一个mailing-list的话题,指向一个之中。

保持CC到mailing-list

这一点是说,别讨论讨论着,就单独私信给一个人了(闷声FDC),请保持CC到mailing-list,让更多的听众关注到这件事情。

当然,如果有什么不方便说的,譬如说,要“负责任的话”,还是别CC了,当然这是例外。

其他

包括打广告,发PR啦等,Anyway,按照Mail-List的规定即可啦。

参考

[1] OpenStack Mailing List Etiquette: https://wiki.openstack.org/wiki/MailingListEtiquette

对于OSI的考据癖(二)


  1. 前言

    俺在前篇,由一次无线的Ping时延不稳定引出了好多追溯。同时,陆陆续续考据了好些东西。这篇紧接着之前的内容,继续对考据网络底层的实现标准。

    PHY/DLL/MAC/LLC/...

    我们还是先有OSI参考模型谈起,话说92年,ISO/IEC组织为了装个B,发布了个OSI参考模型,ISO/IEC 7498-1:1994,全称为Information technology -- Open Systems Interconnection -- Basic Reference Model: The Basic Model。这个模型主要提出的贡献就是将开放互联系统的交互来分为7层嘛。这个OSI的模型中提出了PHY与DLL两个分层。但是,这只是个参考,任意实现可以违背这个准则,譬如说我们大名鼎鼎的802就自己另立门户了。

    之后呢,这个IEEE 802响应了光荣号召,开始写自己的标准啦。反正ISO提出的是一个Ref M odel嘛,所以呢,802可以参考下,将Data Link Layer(DDL)分为了两层分别是MAC与LLC,中间分别以Service Access Port(SAP)进行通信。如下图。


    这里强调一点,MAC这个东西,是802提出来的,802网路中才有MAC这一说法。而其他的非802网络就不一定了。譬如说PPP这个点对点的链路是没有MAC地址这一概念的。因为,点对点也本无需寻找路径。

    当然喽,上图这也只是802的参考模型(RM),到具体实现上,就千差万别了,譬如下图的802.3的实现方式。


    看到了,802.3在PHY的实现上面又自己定义了这么多层次。何其复杂。因此,这有点像Java内部的继承关系。

    除此以外802还为了这个station的管理,安全等因素又定义了一些新的内容,譬如802.1x或者802.1q VLAN就是在这里面被定义的。如下图所示。
     

对于OSI的考据癖(一)

前言

起因于今日,无线网络出了些故障。俺的小本本ping网关竟然ping值在1-100ms之间波动,这种波动对于802.11ac来说,是极为不合理的。

话说,俺多天前购入了一台小米路由器Mini版,雷厂长的东西还是蛮不错的。到手后,刷了OpenWRT,用于扶墙服务,两个无线接口,5G频段另一个用于工作,且与两个内网口作了bridge,2.4G用于他人蹭网,并与俺的办公网用iptables做了隔离。

抓包引发的血案

网络民工啥不会,出了问题就只能抓包了,首先连在wifi底下抓个包。
接着在直接通过双绞线直接相连的主机抓个包。
我们都知道,双绞线直连的网络必然是Ethernet(802.3)网络,而Wi-Fi网络必然是802.11ac啦。但是两种网络抓包的结果竟然惊人的相似,只有三个字段,源地址,目的地址以及类型Type字段。

那么问题来了,当年我渣渣学校的老板们,不是说,Ethernet还有前导码,CRC之类的东西嘛?这些东西为啥都没有,好吧,802.11ac没有,那至少802.3应该有吧?不过似乎我没有看到,切到Linux底下,依然没有。

重拾以太网

啥叫以太网,原创的帖子就不再跟随民科们学习所谓的世界一流大学的教材了。维基百科里面有说

Ethernet /ˈθərnɛt/ is a family of computer networking technologies for local area networks (LANs) and metropolitan area networks (MANs). It was commercially introduced in 1980 and first standardized in 1983 as IEEE 802.3,[1] and has since been refined to support higher bit rates and longer link distances. Over time, Ethernet has largely replaced competing wired LAN technologies such as token ring, FDDI, and ARCNET. The primary alternative for contemporary LANs is Wi-Fi, a wireless LAN standardized as IEEE 802.11.

 几个关键点,第一个Ethernet是一种LAN和MAN技术,在IEEE802.3中被标准化。Ethernet已经取代了token ring等技术成为了有线LAN的主力军。无线LAN技术叫做Wi-Fi,即802.11。

看这个定义的话,Token-Ring应该与Ethernet等价喽。

过时的Token Ring

等等,那MAC又跟Ethernet和Token-Ring啥关系。带着两个问题继续追维基百科。
Token ring local area network (LAN) technology is a communications protocol for local area networks. It uses a special three-byte frame called a "token" that travels around a logical "ring" of workstations or servers. This token passing is a channel access method providing fair access for all stations, and eliminating the collisions of contention-based access methods.
Introduced by IBM in 1984, it was then standardized with protocol IEEE 802.5 and was fairly successful, particularly in corporate environments, but gradually eclipsed by the later versions of Ethernet.
解读一下,几个关键点,这个令牌环也是一种LAN技术。其在IEEE802.5中被定义。不过后来被Ethernet挖了墙角。

等等,802.5是token ring,802.3是Ethernet,802.11是Wi-Fi,那为啥都是802多少?那MAC又是什么?那Data Link Layer又是什么?

关于802.XYZ

那么这个802
IEEE 802 refers to a family of IEEE standards dealing with local area networks and metropolitan area networks. More specifically, the IEEE 802 standards are restricted to networks carrying variable-size packets. By contrast, in cell relay networks data are transmitted in short, uniformly sized units called cells.
 解读下:802是一组IEEE的标准罢了,这个标准主要定义了LAN与MAN。这些802标准们都有一个共同点,可以携带变长数据包。而与之相反的叫做cell relay network,这种网络用来传输短和固定大小的数据包,成为cells。

那这个意思很简单啦,简单来说,802.1---N都叫做LAN/MAN标准,因此,Ehthernet,Wi-Fi, Token Ring都能被称为LAN/MAN啦。

那么802下面还有些啥,可以来这里(Chs)这里(Eng)瞅瞅先。其实802家族是个庞大的分支,大概了解就好。下图也能看出一些大概,摘自IEEE 802 规范2014版本。



802.1 Higher Layer LAN Protocols
        802.1d STP
        802.1q VLAN
        802.1x  Port Based Network Access Control(锐捷就是基于这家伙的变种)
802.3 Ethernet(CSMA/CD)
        802.3u 100BASE-TX, 100BASE-T4, 100BASE-FX Fast Ethernet at 100 Mbit/s
        802.3ab 1000BASE-T Gbit/s Ethernet over twisted pair at 1 Gbit/s
        802.ae 10 Gigabit Ethernet over fiber; 10GBASE-SR, 10GBASE-LR, 10GBASE-ER, 10GBASE-SW, 10GBASE-LW, 10GBASE-EW
802.4 Token Bus
802.11 Wireless LAN (WLAN) & Mesh (Wi-Fi certification)

实际上,在讲深几句,我们所谓的快速以太网或者百兆以太网都是很不准确的。

来个例子,802.3u定义了快速以太网的几种实现机制100BASE-TX, 100BASE-T4, 100BASE-FX,当然,不止这些,实际上还有100BASE-FX,100BASE-SX等,前面几种都是基于双绞线的实现方式,而后面几种是用光纤进行传递的。因此,100兆网既可以跑电(铜线),又可以跑光,取决于采用802.3u的哪种实现。

再啰嗦一句,100BASE-TX,一般100指传输速率,BASE指用baseline频率传输,T指twisted coax,X一般用作placeholder或者用来指明编码方式。

在来个例子,假设说,千兆网的叫法,仅仅指传输速率,其实非常不准确。802.3ae定义了1000BASE-T,802.3z定义了1000BASE-CX, 1000BASE‑LX等。其中1000BASE-CX可以用DE-9接口,也可以用8P8C(RJ45)口。而1000BASE-LX跑在单模或者多模光纤的某光参数下。

再举个例子,我们最常用的百兆网的组网100BASE-TX,也是类似的定义了许多规范。譬如连接线必须cat5,8P8C用哪四根传数据,每个数据包最长传输距离100meter,全双工下,单向数据实现100Mbps速率。

With 100BASE-TX hardware, the raw bits (4 bits wide clocked at 25 MHz at the MII) go through 4B5B binary encoding to generate a series of 0 and 1 symbols clocked at 125 MHz symbol rate. The 4B5B encoding provides DC equalization and spectrum shaping (see the standard for details). Just as in the 100BASE-FX case, the bits are then transferred to the physical medium attachment layer using NRZI encoding. However, 100BASE-TX introduces an additional, medium dependent sublayer, which employs MLT-3 as a final encoding of the data stream before transmission, resulting in a maximum "fundamental frequency" of 31.25 MHz. The procedure is borrowed from the ANSI X3.263 FDDI specifications, with minor discrepancies。
懒得翻译了,讲了如何编码等。

当然,作为网路民工,再看看802.3这个重要得不能再重要的规范吧。

IEEE 802.3 is a working group and a collection of IEEE standards produced by the working group defining the physical layer and data link layer's media access control(MAC) of wired Ethernet. This is generally a local area network technology with some wide area network applications. Physical connections are made between nodes and/or infrastructure devices 

大概就是说,802.3这样的工作组和规范,定义了Physical Layer和Data Link Layer的MAC。方才分析100BASE-TX这个东西的时候,想必大家已经看到了Physical Layer实际上也是由802.3来约束的。因此以太网是一个并非一个Data Link Layer的协议,而是一个Physical Layer和Data Link Layer的协议。

所以,哎,不得不批判这个大学的教育呀。又有多少老师,能分析到这个层面呢?其实,在俺们系是有的,我也从他那里学到了不少的知识,极大地丰富了我对于网络的理解。在这里也是十分感谢!