python实现Hangman游戏

python实现Hangman游戏

游戏过程解读

计算机从一个文本中随机抽取一个单词,字符串表示,并显示出字符串长度用户一个字符一个字符去猜,手动输入猜测结果,总共有6次猜测次数,每次猜测完之后,计算机告知玩家已经猜测的结果和剩余可以猜测的字母范围玩家猜测过程是有一定的规则的:

不能猜测已经猜测过的字符,每重复猜测一次,计算机发出警告一次,总共有3次警告机会;不能猜测除了大小写字母之外的字符,如‘#’ 等一些特殊字符,每猜测一次,警告次数减 1;当警告次数没有了,玩家如果继续犯规(猜测重复,或者猜测除大小写字母之外的字符)则猜测次数减去 1;玩家猜测的字母不在目标串 secret_letters中,如果猜测的字母是元音字母,则猜测次数减 2 作为惩罚,如果是辅音字母,则猜测次数减 1这样猜对于玩家来说实在太有难度了,是否可以实现一个游戏的改进版本,每当玩家输入星号 * 时,计算机自动输出文本中所有与当前玩家已猜测结果相符合的单词呢?这样就可以大大缩小玩家的猜测范围了,降低游戏难度。

模块化编程思想

怎么实现这个游戏呢? 我们首先要实现一些功能,这些功能如下:

计算机如何获取文本 如何从文本中随机获取单词 判断玩家猜测字符是否正确 获取玩家已猜对字母 获取剩余可猜测字母范围 计算机玩家交互过程 相关函数的代码就不分别给出了,下面直接给出整个项目的代码

游戏代码

Hangman Game
import random

import string

WORDLIST_FILENAME = “words.txt”

def load_words():#读取文件

print(“Loading word list from file…”)

# inFile: file

inFile = open(WORDLIST_FILENAME, ‘r’)

# line: string

line = inFile.readline()

# wordlist: list of strings

wordlist = line.split() #空格分开的单词

print(” “, len(wordlist), “words loaded.”)

return wordlist #返回的是一个列表

wordlist=load_words()

def choose_word(wordlist):

return random.choice(wordlist) #随机获取一个单词
1
#判断玩家猜测是否正确

def is_word_guessed(secret_word, letters_guessed):

list_secret=list(secret_word)

#只要目标中出现一个字母不在玩家猜测结果中,返回False,都在的话,返回True

for i in list_secret:

if i not in letters_guessed:

return False

return True

#获取玩家已猜对字母

def get_guessed_word(secret_word, letters_guessed):

length=len(secret_word)

list_secret=[‘_ ‘]*length #列表元素先全部初始化为’_’

for i in letters_guessed:

for j in range(length):

if i==secret_word[j]: #用猜对的字母替换掉对应位置的’_’

list_secret[j]=secret_word[j]

string=””.join(map(lambda x:str(x),list_secret)) #列表转字符串

return string

#获取剩余可猜测字母范围

def get_available_letters(letters_guessed):

#初始化可猜字母为全部小写字母

letters_all=”abcdefghijklmnopqrstuvwxyz”

for i in letters_all:

if i in letters_guessed: #如果玩家已经猜过 i 则将其替换为’_ ‘

letters_all=letters_all.replace(i,”)

return letters_all

def hangman(secret_word):

list_unique=[] #用于secret_word去重

for i in secret_word:

if i not in list_unique:

list_unique.append(i)

unique_numbers=len(list_unique) #目标单词中不同字符的数量,用于计算玩家分数

vowels=”aeiou” #元音字母

print(“Welcome to the game hangman!”)

length=len(secret_word) #目标单词长度

print(“I’m thinking of a word that is {} letters long!”.format(length))

times_left=6 #玩家剩余猜测次数

warning_left=3 #玩家剩余警告次数

print(“You have {} warnings left.”.format(warning_left))

print(“————- “)

list_guessed=[]

while times_left>0: #玩家猜测次数没用完

print(“You have {} guesses left.”.format(times_left))

print(“Available letters:”,get_available_letters(list_guessed))

char=input(“Please guess a letter:”)

x=str.lower(char)

if x in list_guessed:#玩家已经猜过这个字母

if warning_left>0: #警告次数没用完

warning_left-=1

print(“Oops! You’ve already guessed that letter.You have {} warnings left:”.format(warning_left),get_guessed_word(secret_word,list_guessed))

else: #警告次数为0了 减少猜测次数

times_left-=1

print(“Oops! You’ve already guessed that letter.You have no warnings left,so you lose one guess:”,get_guessed_word(secret_word,list_guessed))

else: #玩家尚未猜测过这个字母

list_guessed.append(x) #先存储玩家猜测结果

if not str.isalpha(x): #玩家输入不是是字母

if warning_left>0:

warning_left-=1

print(“Oops!That is not a valid letter.You have {} warnings left:”.format(warning_left),get_guessed_word(secret_word,list_guessed))

else:

times_left-=1

print(” Oops! That is not a valid letter. You have no warnings left,so you lose one guess:”,get_guessed_word(secret_word,list_guessed))

#玩家输入是字母时

elif x in secret_word:#玩家猜测字母在目标中

print(“Good guess:”,get_guessed_word(secret_word,list_guessed))

# 玩家猜出全部字母

if secret_word==get_guessed_word(secret_word,list_guessed):

print(“————- “)

print(“Congratulations, you won!”)

total_score=times_left*unique_numbers

print(“Your total score for this game is:”,total_score)

return

else: #玩家猜测字母不在目标中

print(“Oops! That letter is not in my word.”,get_guessed_word(secret_word,list_guessed))

if x in vowels: #没有猜中,且是元音字母

times_left-=2

else:

times_left-=1

print(“————- “)

print(“Sorry, you ran out of guesses.The word was {}”.format(secret_word)) #玩家失败,游戏结束

return

“””

def del_space(string): #将字符串去除空格后转化成列表

lst=[]

for i in string:

if i!=’ ‘:

lst.append(i)

return lst

#检验两个单词是否按规则匹配

def match_with_gaps(my_word, other_word):

#先将字符串转换成列表,方便操作

list_my_word=del_space(my_word)

list_other_word=list(other_word)

if len(list_my_word)!=len(list_other_word): #长度不一致

return False

else:

length=len(list_my_word)

for i in range(length): #对应位置均是字母且不相等

if list_my_word[i]!=’_’ and list_my_word[i]!=list_other_word[i]:

return False

#list_my_word[i]==’_’时

for i in range(length):

j=i+1

for j in range(length):

if list_other_word[i]==list_other_word[j] and list_my_word[i]!=list_my_word[j]:

return False

return True

def show_possible_matches(my_word):

flag=0 #用于标记是否存在可能匹配的单词

possible_word=[] #存储可能匹配的单词

for i in wordlist:

if match_with_gaps(my_word,i):

flag=1

possible_word.append(i)

if flag==0:#不存在可能匹配的单词

print(“No matches found.”)

else:

print(“Possible word matches are:”)

for i in possible_word:

print(i,end=’ ‘)

print(“”)

def hangman_with_hints(secret_word):

list_unique=[] #用于secret_word去重

for i in secret_word:

if i not in list_unique:

list_unique.append(i)

unique_numbers=len(list_unique) #目标单词中不同字符的数量,用于计算玩家分数

vowels=”aeiou” #元音字母

print(“Welcome to the game hangman!”)

length=len(secret_word) #目标单词长度

print(“I’m thinking of a word that is {} letters long!”.format(length))

times_left=6 #玩家剩余猜测次数

warning_left=3 #玩家剩余警告次数

print(“You have {} warnings left.”.format(warning_left))

print(“————- “)

list_guessed=[]

while times_left>0: #猜测次数没没有用完时

print(“You have {} guesses left.”.format(times_left))

print(“Available letters:”,get_available_letters(list_guessed))

char=input(“Please guess a letter:”)

x=str.lower(char)

if x in list_guessed:#玩家已经猜过这个字母

if warning_left>0:

warning_left-=1

print(“Oops!You’ve already guessed that letter.You have {} warnings left:”.format(warning_left),get_guessed_word(secret_word,list_guessed))

else: #警告次数为0了 减少猜测次数

times_left-=1

print(“Oops! You’ve already guessed that letter.You have no warnings eft,so you lose one guess:”,get_guessed_word(secret_word,list_guessed))

else: #玩家尚未猜测过这个字母

list_guessed.append(x) #先存储玩家猜测结果

if x==’*’: #是 ‘*’ 的情况

my_word=get_guessed_word(secret_word,list_guessed)

show_possible_matches(my_word)

elif not str.isalpha(x): #玩家输入不是是字母且不是 * 号

if warning_left>0:

warning_left-=1

print(“Oops! That is not a valid letter.You have {} warnings left:”.format(warning_left),get_guessed_word(secret_word,list_guessed))

else:

times_left-=1

print(” Oops! That is not a valid letter.You have no warnings left,so you lose one guess:”,get_guessed_word(secret_word,list_guessed))

#玩家输入是字母时

elif x in secret_word:#玩家猜测字母在目标中

print(“Good guess:”,get_guessed_word(secret_word,list_guessed))

# 玩家猜出全部字母

if secret_word==get_guessed_word(secret_word,list_guessed):

print(“————- “)

print(“Congratulations,you won!”)

total_score=times_left*unique_numbers

print(“Your total score for this game is:”,total_score)

return

else: #玩家猜测字母不在目标中

print(“Oops! That letter is not in my word.”,get_guessed_word(secret_word,list_guessed))

if x in vowels: #没有猜中,且是元音字母

times_left-=2

else:

times_left-=1

print(“————- “)

print(“Sorry,you ran out of guesses.The word was {}”.format(secret_word)) #玩家失败,游戏结束

return

“””

if name == “main”:

secret_word=choose_word(wordlist)

hangman(secret_word)

#secret_word = choose_word(wordlist)

#hangman_with_hints(“apple”)

注意,注释掉的部分是为了实现 hangman 游戏的 改进版本,一个具有计算机提示功能的版本,每当用户输入 * 时,计算机就将文本中所有与当前已猜测结果相匹配的单词输出,以缩小玩家猜测范围作为提示。游戏效果如下: 项目代码如果有任何问题,欢迎随时指出,万分感谢!

云计算的过去、现在和未来

云计算从 AWS 初创时的牛刀小试到如今巨大的行业和生态,从新鲜词汇变为流行语,它的十年发展经历了哪些阶段?每个阶段分别创造和发展了什么?未来云计算又将朝着哪些方向继续前行?此文旨在通过回顾技术发展总结*佳实践、为开发者启发技术新思路。

科学技术的革新始终在推动时代巨轮轰鸣向前。

云计算,已经走过十余年的风雨历程,从 AWS 初创立时的牛刀小试,到如今成长为一个巨大的行业和生态,堪称是新世纪以来*伟大的技术进步之一。“云计算”这个术语,也早已从一个新鲜词汇,成为了妇孺皆知的流行语。十年荏苒,风云变幻,我们可以忆昔抚今,一起回顾和感受云计算领域的发展与变革。

任何事物的诞生和发展一定有其前提条件和土壤,云计算亦是如此。记得在世纪初的大学课堂上,教授们颇为推崇网格计算理论,该理论事实上已经充分体现了计算资源分布式协作和统一管理的先进思想。可惜网格计算过于学术化,*终是更接地气也更宏大的云计算横空出世,震动了整个 IT 业界。

那么,云计算诞生及蓬勃发展的原因是什么呢?在笔者看来,主要有三大因素,分别是相关软硬件技术的成熟、巨大的社会价值和伟大的商业模式。

01 所谓软硬件技术的成熟

指的是在技术和工程层面,构建云计算平台的条件开始陆续具备,主要包括超大规模数据中心建设、高速互联网络,以及计算资源虚拟化 (Hypervisor) 和软件定义网络 (SDN) 技术的不断发展和成熟——这些基础能力构成了云计算发展的技术前提

02 所谓巨大的社会价值

指的是从用户角度出发,云计算的采用使任意组织和个人得以站在巨人的肩膀上开展业务,避免重复造轮,*大提高了软件与服务构建各环节效率,加速了各类应用的架构和落地,而云端按需启用和随意扩展的资源弹性,也能够为企业节省巨大成本

03 所谓伟大的商业模式

指的是云计算的产品和服务形态非常适合新时代的 B 端需要,订阅制和 Pay-as-you-go 的计费方式大幅降低了客户的进入门槛,而技术基础设施架构方面的稳定性需要又带来了较高的客户粘性,再加上多租户高密度数据中心所能带来的规模效应,这些因素使得云计算能够成为一门好的生意,对应着一个*佳的 B 端商业模式

这三者缺一不可,共同促成了云计算的兴起与繁荣,也吸引了不计其数的业界精英投入其中,是为云计算取之不竭的源动力。

当然,同任何新生事物一样,云计算行业的发展也并非一帆风顺。从早期被指责为“新瓶装旧酒” 的概念炒作,到对云上数据隐私问题的担忧,再到对各类公有云线上偶发事故的讥讽和嘲笑,云计算的成长亦伴随着各种挑战和质疑。其中部分负面反馈实质上还是由于使用不当或偏离*佳实践造成,也让云计算背负了不少 “冤屈” 和骂名。所幸瑕不掩瑜,云计算的先进性终究让发展的主旋律盖过了干扰与杂音,配合其本身持续的改进,越来越多地得到客户的认可,市场规模也不断扩大。

本文会试图从普通开发者及实践者的视角来回顾云计算的伟大历程。因篇幅所限,所讨论的范畴将聚焦于公有云,以 IaaS 和 PaaS 层面的技术演进为主。

萌芽时代 2008—2011

事实上,云计算行业的开端较难精准定义。一般认为,亚马逊 AWS 在 2006 年公开发布 S3 存储服务、SQS 消息队列及 EC2 虚拟机服务,正式宣告了现代云计算的到来。

而如果从行业视角来看,我们也不妨视 2008 年为另一个意义上的云计算元年。因为在这一年,当 AWS 证明了云是可行业务之后,越来越多的行业巨头和玩家注意到这块市场并开始入局:

◆微软在 PDC2008 上宣布 Windows Azure 的技术社区预览版,正式开始微软众多技术与服务托管化和线上化的尝试;

◆Google 恰好也在 2008 年推出了 Google App Engine 预览版本,通过专有 Web 框架允许开发者开发 Web 应用并部署在 Google 的基础设施之上,这是一种更偏向 PaaS 层面的云计算进入方式;

◆而众所周知,国内的云计算标杆阿里云也是从 2008 年开始筹办和起步——可见是从 2008 年起,云计算的时代大幕逐步拉开,开始形成一个真正的多元化市场,并随着众多巨头的加入开始良性竞争。

在云计算兴起之前,对于大多数企业而言,硬件的自行采购和 IDC 机房租用是主流的 IT 基础设施构建方式。除了服务器本身,机柜、带宽、交换机、网络配置、软件安装、虚拟化等底层诸多事项总体上需要相当专业的人士来负责,作调整时的反应周期也比较长——相信许多研发负责人都有过等待服务器到位的经历。

云的到来,突然给出了另一种高效许多的方式:只需轻点指尖或通过脚本即可让需求方自助搭建应用所需的软硬件环境,并且根据业务变化可随时按需扩展和按量计费,再加上云上许多开箱即用的组件级服务,这对许多企业来说有着莫大的吸引力。Netflix 就是早期云计算的拥抱者和受益者,该公司在 2010 年成功地全面迁移到 AWS,堪称是云计算史上*著名的案例之一。

技术产品上看,早期的云上产品组合虽然还比较单薄,也存在一些限制,但计算和存储分离的核心理念已经得到初步确立,并深刻影响了基于云上应用程序的架构模式。具体来说,该理念一方面体现在云厂商纷纷将存储服务开辟为独立的产品类别,通过如 AWS S3、Azure Storage、框架户阿里云 OSS 等服务清晰剥离了二进制对象与文件的负载与管理,并且提供了丰富的接口和 API 以供应用程序进行集成;另一方面,在虚拟机层面基于网络存储的托管磁盘服务也得到了大力发展和推广,如AWS的EBS、Azure的Page Blob(后封装为 Managed Disk)以及阿里云的块存储(云盘),此类托管磁盘既很好地保障了数据可靠性,又提供了丰富的容量和性能级别选择,使得云上虚拟机的计算和存储充分解耦,在这两方面都能够独立扩展和调节。

在云计算的萌芽时期,另一个有趣现象是当时“云计算”和“大数据”纠缠不清的关系。可能是由于发展历程上几乎同期兴起,以及在大规模数据存储与计算上的确存在能力交集,两者的概念和定义一度容易互相混淆。早年笔者曾购买过一本很不错的 Hadoop 技术书籍,其副标题却是“开启通向云计算的捷径”,可见早期云计算的定义曾有较为模糊的阶段。

当然,随着后续的时代发展,这样的歧义越来越少见了,云计算已多特指提供各类云端服务与组件的软硬一体化技术资源平台,是一个带有明确商业模式的综合性载体,而大数据则是技术上处理大体量数据的方法论和实现,主要是一种技术体系——所以两者各自独立又可互相依存,比如各云计算厂商都陆续推出了云上大数据分析服务,如 AWS 的 EMR、Azure 的 HDInsight、阿里云的 E-MapReduce,本质上正是开源大数据技术在云上的实现和适配。

探索时代 2011—2014

当云计算玩家们纷纷入场并确认大举投入的战略后,行业进入了精彩的探索时代。这一时期的各朵云在产品技术层面进行了许多有益尝试,虽然免不了在个别方向上走些弯路甚至经受挫折,但总体而言云端服务的能力与质量取得了相当大的进步和提升,也为云计算赢得了越来越多的关注和喝彩。

首先,IaaS 层面继续围绕虚拟机为核心得到稳扎稳打的推进和增强。更强更新的 CPU 带来了云上虚拟机计算能力的提升和换代自不必说,早期机型内存相对偏小的问题也随着新机型的推出逐步得到解决,新上云端的 SSD 磁盘更是让机器性能如虎添翼。厂商们不约而同地形成了通用型、计算型、内存型等多个虚拟机系列,通过将不同 CPU/ 内存比例搭配的机型摆上货架,给予不同应用程序负载以更多选择。当笔者浏览这些琳琅满目的机型列表和参数时,恍惚间犹如来到了当年热闹的中关村 IT 卖场,颇有在云端“攒机”的奇妙感觉。

同属基础设施的存储类服务在初期得到了市场欢迎和认可之后,也同样迎来了大发展。原有功能得以细化,通过引入冷、热乃至存档的各级分层,进一步凸显成本优势。为了弥补托管磁盘在跨机器文件共享方面的不足,类似 NFS 的文件存储类服务也逐步成为了云上标配,如 AWS 的 EFS、Azure 的 Azure Files 及阿里云 NAS 等,进一步把计算存储分离架构发挥到*致,大大方便了某些场景下的架构与实现。

与 IaaS 的高歌猛进相比,PaaS 的发展在这一时期则显得多少有些落寞,尤其是从希望从顶层应用框架入手来推广云的方式一直不温不火,没有得到太多的关注。例如前面我们提到的 Google App Engine,经过几年发展后技术上其实颇为扎实,除 Python 外也添加了 Java/Go 等更多语言的支持,亦可与 Google 其他基础设施无缝集成,但就市场而言总是处于叫好不叫座的状态;国内率先实践此模式的厂商是新浪 SAE,上线之初颇受关注,亮点在于支持 PHP 语言及集成 MySQL,可惜多年运营亦未达到火爆状态;阿里云也曾推出相似的 ACE 服务对此类产品形态进行尝试,后也于 2016 年下线——究其原因,恐怕还是因为 App Engine 类服务本质上是相对受限的环境,平台的技术约束和绑定较强,出问题时也较难进行深入排查所致。

这揭示了云的用户固然喜欢技术实现上的便利和平台抽象,但也同样渴求技术灵活性、可移植性和自我掌控。在后来的 Web 类 PaaS 服务中,我们看到业界调整了策略:更多地定位为通用运行平台并着力于自动扩容和负载监控等专业增值服务,尽可能解除技术、语言和框架层面的限制,如 AWS 的 Elastic Beanstalk、Azure 的 App Service 等;另一种发展思路则彻底轻量化,主要面向中小客户推广较为经济的建站服务,以满足入门级托管需求。

所幸,PaaS 中的另一重要分支——泛数据库类服务(亦称 DBaaS)则得到了快速的发展。由于数据库服务较为标准化,又是应用程序中不可或缺的重要组成,因此很快得到了广泛支持和采用。这里的典型代表有 Amazon RDS、阿里云 RDS、Azure SQL Databases 和 Azure Database for MySQL/PostgreSQL 等。

与自行使用虚拟机搭建相比,云上数据库一键式的创建过程,自带的高可用性和自动备份,可谓省心省力;丰富的性能等级选项更是可根据实际负载选择和调节,实现了成本的*优化控制。除此之外,由于云上数据库按时间及容量计费的轻量特点,也给予了一些优质企业级数据库以重要的展示和售卖渠道,降低了企业的评估和尝试成本:例如,原本只能以昂贵的一体机方式售卖的 MPP 数据库 SQL Server Parallel Data Warehouse (PDW),微软将其彻底云化之后以 Azure SQL Data Warehouse 的产品形态发布,改变了其养在深闺人未识的状态,使得相关技术让普通开发人员触手可及,也无疑大大增加了在整体数据架构中的采用可能。

让我们回到宏观视角。在这一百家争鸣的探索时期,令人可喜的是中国云计算真正如火如荼地发展了起来。除了早期入场的阿里云和盛大云,腾讯、百度及三大运营商等各路巨头也都先后布局试水,并纷纷把“云”的品牌从一度红火的个人网盘服务让位于企业级云计算;微软 Azure 也于 2014 年在中国正式商用,标志着外资厂商开始参与国内市场竞争。值得一提的是,这段时期独立云计算企业 UCloud、七牛云、青云等都相继创立,分别以*具特色的产品服务和强大的自主研发能力,为中国云计算发展书写了浓墨重彩的篇章,使得国内云计算市场更加精彩纷呈。

发展时代 2014—2018

当整个云计算行业一定程度走过蹒跚探索时期之后,开创者们积累了越来越多的经验,对市场反馈和客户需求有了更清晰的了解与洞察,业务模式与商业运营也驾轻就熟起来——云计算行业终于进入高速发展时代。在这一时期,不论是总体市场规模,还是云计算的产品与服务,都得到了*大的增长和丰富。

首先,IaaS 方面的继续进步体现在服务的特异性和多样性上,不断推出细分领域和特定场景下适用的虚拟机实例:如基于物理隔离的专属实例、可运行 SAP HANA 等大型负载的超高配实例、采用 CPU 积分制的性能突增实例、适用于机器学习与 AI 的 GPU 实例等。此类细分服务在云端出现的背后,是不少厂商针对性地研发和定制了适用于云的专属配套硬件。这些新一代的服务器不仅是简单的配置升级,而且在设计之初就是为云端负载而生,与云计算产品理念及底层软件技术栈高度融合匹配。另外在虚拟机计费规则方面,除了经典的按使用时长计费方式,各厂商也相继推出更为灵活的计费模式:包年包月、预留实例、竞价实例等,可有效帮助降低使用成本。这些 IaaS 方面的诸多进展,进一步满足了各场景的细分需求,减少了许多客户的上云阻碍。

在存储服务方面,云计算的步伐也在加快,除立足于核心对象存储服务的增强外,开始以一体化方式进攻部分垂直市场,挤占传统厂商的市场空间。*典型的例子莫过于 CDN:阿里云自 2015 年起在 CDN 领域大举扩张,多次主动大幅降价抢占市场,其他云计算厂商也纷纷跟进,这是原本波澜不惊的 CDN 领域的标志性事件。面对云计算厂商的竞争,网宿、蓝汛等老牌 CDN 厂商顿时立感压力,市场份额也开始发生相应变化。在国外,AWS CloudFront 和 Azure CDN 等云服务同样与 Akamai 这样的传统巨头进行着激烈角逐。当然,得益于 CDN 的架构融合特性,传统厂商也可选择与云厂商合作,从上游的基础设施方面作为云的供应商存在,不失为合作共赢之道。但无论如何,事实说明云的参与和挤压是相关市场发展的必然趋势。而且从技术层面来看,服务之间的一体化协同效应是云计算厂商进入 CDN 这样的单一领域的独到优势。例如,云厂商的 CDN 可以与自家的对象存储服务联动,用户只需要轻点鼠标,即可将对象存储中的文件映射至云 CDN 的边缘节点网络来对外服务,免去了搭建传统回源站点的麻烦。

PaaS 方面在这个大发展时代也找到了崛起之道,不再寻求大一统的应用程序框架,而是更多提供标准的可复用中间件,并与其他 IaaS/PaaS 设施进行组合与联动——这一思路迅速得到开发者和架构师们的欢迎,随着特性完善其采用率开始稳步上升。典型的例子包括 API 网关、负载均衡器、消息队列等。

更进一步地,这一阶段的 PaaS 服务在与 IaaS 虚拟网络集成方面有了较大进步——这是以往 PaaS 服务常为人诟病的短板,即只提供面向互联网的公开端口,游离于 IaaS 的虚拟网络和架构体系之外——这一现状也通过两种方式得到了相当程度的解决:一种可称之为 PaaS-in-VNet,即允许 PaaS 服务直接部署到现成的虚拟网络之上,例如 Azure 上的 App Service Environment (ASE),就是将 App Service 部署到私有虚拟网络的服务形式;另一种方式则是所谓 PaaS-to-VNet,为 PaaS 与虚拟网络之间提供私有通道,在不开放公开访问的情况下允许双向网络互通,同样以 Azure App Service 为例,用户可通过开启服务内置的 VNet Integration 特性来配置与私有网络的安全通信。可以看到,无论是 PaaS-in-VNet 还是 PaaS-to-VNet,其本质都是试图解决 PaaS 和 IaaS 的融合问题,这无疑增加了 PaaS 在整体架构中被采纳的可能。

势头颇佳的 DBaaS 方面的进展,主要体现为从经典关系型数据库拓展到新兴的各类 NoSQL 数据库及大数据领域的云服务。MongoDB、Redis、Kafka、ElasticSearch——这些耳熟能详的开源数据库,我们几乎能在每一个云上找到其对应的托管服务,轻松地一键搭建所需集群。云上数据库的开箱即用是如此的便捷与诱人,不断获得市场,甚至引起了开源厂商的不满:MongoDB 近期将开源协议从 AGPL 调整为新推出的 SSPL,是一时关注度颇高的社区新闻,其矛头正是直指开源软件被直接包装为云服务牟利的状况(部分网友戏称此为“插管吸血”)。

在此,我们对各方抱有商业目的的行为不做倾向性评价,但从产品技术层面来讲,实力雄厚的云厂商并不乏应对之道:一是基于开源版本作分叉并开始自行维护和迭代,二是完全自行实现数据内核和引擎,仅在客户端协议方面与主流数据库进行兼容。目前,后者这类自研云数据库越来越成为一种新趋势:厂商可以放开手脚,充分利用云的特点进行重新设计,同时又兼容流行协议,这一方式迅速取得了市场和开发者认同,此类数据库的杰出代表是 AWS Aurora 和 Azure CosmosDB。其中 AWS Aurora 完全兼容*流行的 MySQL,同时实现了计算存储的高度分离和近乎无限的扩展,而 Azure CosmosDB 则是一款多模式数据库服务,提供 SQL、MongoDB、Cassandra、Gremlin 等多种开放协议或查询语言的兼容,同时实现了全球分布、按需扩容、一致性保障等特性。

所以,无论是 Aurora 还是 CosmosDB,一经推出都迅速攻城略地,取得了不俗的战果。在国内,以阿里、腾讯为代表的大厂商也同样在自研数据库方面不断加码,陆续推出了阿里云 PolarDB、腾讯云 CynosDB 等重磅服务。

在这样的局面下,也许开源软件厂商可以参考同为创业公司的 DataBricks 的做法。DataBricks 作为大数据处理领域事实标准 Spark 框架的实际掌舵者,一方面主导把控 Spark 开源版本不断进步,另一方面也开始推出性能更佳、交付节奏更快的商业版本 Databricks Runtime。更重要的是,Databricks 积*地同云厂商合作,将自身的解决方案融入到公有云平台之中,成为了平台上原生的 PaaS 服务,例如与微软深度合作推出的 Azure Databricks。尽管云上已有基于纯开源方案的大数据服务如 AWS EMR 和 Azure HDInsight,但基于 Databricks 商业级解决方案的 PaaS 服务有自己独到的优势和特点,已初步获得了不错的发展势头。

我们由衷希望,出色的开源软件公司能够像 DataBricks 这样寻找到一种与云合作共赢的商业模式,毕竟业界既需要一站式的整合平台,又应当保护开源与创新的良性环境。

容器与微服务,可以说是近年*重要的技术趋势之一。作为新技术的拥抱者和试验田,公有云自然不会袖手旁观。事实上各大云在容器服务方面的尝试由来已久,在早期厂商们就各自推出了如 AWS ECS、Azure ACS、阿里云容器服务等基础设施,提供基于底层 IaaS 的容器运行环境,同时包含开源或自研的编排引擎。而当 Kubernetes 在编排大战中逐步胜出并成为事实标准后,各大厂商又不约而同地对 Kubernetes 提供更加定向和深度的支持,纷纷推出 AWS Elastic Kubernetes Service (EKS)、Azure Kubernetes Service (AKS) 等新一代容器服务。另一类容器托管服务则进一步屏蔽了底层细节和调度麻烦,让容器作为独立计算单元直接在共享基础设施上运行,如 AWS Fargate、Azure Container Instance、阿里云 ECI 等,颇得无服务器计算思想之精髓。当然,经典的无服务器计算一般指 FaaS,例如也在同步发展的 AWS Lambda 和 Azure Functions,它们虽在编程框架和范式方面有所限制,但得益于更高层抽象,可让开发者聚焦业务逻辑,在合适的场景中使用得当可大幅提高研发效率。

让我们的视角再从技术回到商业。随着云计算行业体量越来越巨大,市场竞争也愈发激烈,价格战屡见不鲜。虽然说云计算有着相当不错的商业模式,但这毕竟是一个重投入长周期的行业,因此陆续有中小玩家力不从心、陷入困境。例如国外的 Rackspace,原本入场颇早也有相当积累,一度曾在市场中占先,但当巨头纷纷入场后就显得后劲不足,现已跌出市场占有率前五名。也许当云计算进入巨头角力时代后,中小玩家还是需要着力发展自己特色,深耕细分市场,或是寻求联姻以共享能力与资源。

客户方面,云计算在这一时期开始明显地从互联网企业向传统行业进行渗透。为了拿下更多传统行业客户,组织架构和流程的匹配也是必做的功课。走在前面的云厂商相应地完善了云上的多账号管理、组织架构映射、资源分组、细粒度权限管控等企业级功能。例如阿里云就在不断地更新升级其企业控制台,帮助用户更好地管理人员、资源、权限及互相之间的关系。

至此,经历了大发展的云计算已然成长为几乎承载一切、包容一切的巨大平台,是一艘提供企业信息化和数字化整体解决方案的航空母舰。云计算无疑已经全面走向成熟,成为了参与和推动 IT 业界向前发展的重要力量。

繁荣时代 2019—?

时间终于进入 2019 年。基于过去十年发展的良好态势,我们没有理由不相信云计算将进入繁荣热潮。来自 Gartner 的分析报告显示,2019 年的全球公有云市场规模将超越 2 千亿美元,并将继续保持稳定增速。而国内由于起步相对较晚,市场渗透率还不高,将拥有更高的增速。“上云”将成为各类企业加快数字化转型、鼓励技术创新和促进业务增长的*选择甚至前提条件。对于企业而言,更多的不会是上不上云的问题,而是要考虑上哪家云、怎么上云的问题,是如何迁移重构以适配云端的问题,是如何让云更好地服务生产的问题。因此,我们对云的未来理应充满信心,同时也抱有更高的期待。接下来,我们不妨结合企业的需求和云厂商的投入方向,大胆预测未来云计算发展的若干趋势。

01 趋势之一:

云计算将进一步成为创新技术和*佳工程实践的重要载体和试验场,走在时代进步的前沿。这是得益于云产品本身的 SaaS 属性,非常适合快速交付与迭代,能够较快地把新产品、新技术推向业界。可以看到,当下的热点技术,从 AI 与机器学习、IoT 与边缘计算、区块链到工程实践领域的 DevOps、云原生和 Service Mesh,甚至未来感十足的量子计算,都有云计算厂商积*参与、投入和推广的身影。以人工智能为例,不论是前面提到的 IaaS 中 GPU 计算资源的提供,还是面向特定领域成熟模型能力开放(如各类自然语言处理、图像识别、语音合成的 API),再到帮助打造定制化 AI 模型的机器学习平台(如 AWS SageMaker、Azure Machine Learning Service、阿里云 PAI 等),云事实上从各个层面都有力地支持和参与了 AI 相关技术的发展。就*终效果而言,云上的资源和产品让人工智能等新兴技术变得触手可及,大大降低了客户的探索成本,也加快了新技术的验证和实际交付,具有*高的社会价值。

另外值得一提的是,云在新技术的发展过程中还保持了某种程度的中立性,对于技术趋势持有普遍包容和适应的态度——*典型的例子莫过于容器化和开源框架(如 Spring Cloud)支持下的云原生架构,它们事实上同部分云端 PaaS 服务存在竞争关系,甚至有助于用户解除厂商锁定,但云厂商并不会厚此薄彼,而是进行不遗余力的支持与适配,更多地把选择权留给客户。

02 趋势之二:

云计算将顺应产业互联网大潮,下沉行业场景,向垂直化产业化纵深发展。随着通用类架构与功能的不断完善和对行业客户的不断深耕,云计算自然地渗透进入更多垂直领域,提供更贴近行业业务与典型场景的基础能力。典型的垂直云代表有视频云、金融云、游戏云、政务云、工业云等。以视频云为例,它是将视频采集、存储、编码转换、推流、视频识别等一系列以视频为核心的技术能力整合为一站式垂直云服务,不仅适用于消费互联网视频类应用的构建,更重要的是配合摄像头硬件和边缘计算节点进军广阔的线下安防监控市场。再如金融云,可针对金融保险机构特殊的合规和安全需要,提供物理隔离的基础设施,还可提供支付、结算、风控、审计等业务组件。可以预计,随着消费互联网红利耗尽,产业互联网将逐步受到重视并兴起,其规模之大、场景之多,将给予云计算厂商*大的发展空间;而云计算作为赋能业务的技术平台和引擎,也非常适合承载产业互联网的愿景,加快其落地与实现。

03 趋势之三:

多云与混合云将成为大中企业刚需,得到更多重视与发展。当企业大量的工作负载部署在云端、对于云的应用进入深水区之后,新的问题则会显现:虽然云端已经能提供相当高的可用性,但为了避免单一供应商出现故障时的风险,关键应用仍须架设必要的技术冗余;另一方面,当业务规模较大时,从商业策略上说也需要避免过于紧密的厂商绑定,以寻求某种层面的商业制衡和主动权。因此,越来越多的企业会考虑同时采购多个云厂商的服务并将它们结合起来使用——这将催生多云架构和解决方案的兴起,以帮助企业集中管理协调多个异构环境,实现跨云容灾和统一监控运维等需要。

例如华为云不久前发布了商用级的多云容器平台 MCP,可对跨云跨区域的多个容器集群进行统一资源与应用管理,提供一站式的接入、管控和调度能力;在网络基础设施层面,也有如犀思云这样专注于云交换服务的企业,提供云与云、网与网之间的快速互联,帮助多云互联在稳定性延迟等方面达到生产要求。除同时使用多个公有云之外,合规和隔离性要求更高时的另一选择是私有部署云基础设施,并与相应公有云专线连接形成混合云架构。从目前市场态势看,主要有公有云厂商主导的混合云方案和私有云厂商主导的方案两类。笔者个人更看好前者的发展,是因为公有云厂商方案让混合云的私有部分成为了公有云在自有数据中心的自然延伸,提供了与公有云端高度一致的能力和使用体验。此类服务的代表有微软的 Azure Stack,以及阿里云 Apsara Stack,包括之前只专注公有云的 AWS 终于在 re:Invent 2018 大会上推出了 AWS Outposts,也加入了混合架构的行列。

04 趋势之四:

云的生态建设重要性不断凸显,成为影响云间竞争的关键因素。当某个云发展到了一定规模和阶段后,恐怕不能仅仅考虑技术和产品,同样重要的是建立和培育具有生命力的繁荣生态和社区,此为长久发展之道。因为一朵云再大再丰富,也必有覆盖不了的场景和完成不了的事情。这就需要大量的第三方服务提供商,以合作伙伴的身份基于云平台提供各类解决方案。此举既方便了用户,又增加了云的粘性,也可保证应用提供商的市场空间,可谓三方共赢。所以在当下各大云平台上,我们都能够找到应用市场和合作伙伴计划,这正是厂商们着力建设的第三方解决方案平台。例如,国内大数据领域的明星创业公司 Kyligence 拥有以 Apache Kylin 为核心的企业级大数据 OLAP 解决方案,通过其 Kyligence Cloud 套件深度适配了多个云端,先后登陆了包括 Azure、AWS 和阿里云在内的多个云市场与平台。

云生态的另一个重要方面是面向广大开发者、架构师和运维工程师的持续输出、培养和影响。只有赢得广大技术人员的关注和喜爱,才能赢得未来的云计算之仗。我们之所以敢下这个判断,是因为以下几点原因:

●其一,云的采购具有弹性特征,不论是 Pay-as-you-go 还是年单方式,都可随实际项目效果和生产运行情况进行调整,此时一线研发人员和架构师会颇具发言权,可将使用端的实际情况反推至商务决策层面并影响续约;

●其二,从历史上来看,部分较为失败的云上功能,往往是失之于理想化和简单化,或是过多地从管理或宣传视角考虑而忽略了落地细节,*终导致了实操效果受限、口碑下滑甚至无人问津,因此从开发者的角度思考产品设计对于云而言至关重要;

●其三,如若能培养庞大的技术爱好者和粉丝群体,形成传播效应,相信对应的云服务自然不愁业务的增长,还能进一步收集到更多产品反馈,形成良性循环。

由上种种,所以当下各大厂商,都开始空前重视开发者关系,并视之为核心竞争力。云厂商们不但努力地建设丰富的文档体系和在专业媒体频繁发声,还会积*举办各类论坛和参与业界开发者会议,并新增如 Developer Advocate 这样的布道师职位,专注于在开发者群体中扩大影响力。这里我们不妨简单分析一个例子:IBM Cloud(原 BlueMix)。总体上 IBM 云历来相当注重和依赖企业端的庞大销售体系和客户资源,但在赢得开发者和社区方面投入相对不足,所以在大多数人的印象中,IBM 云总显得有些遥远和陌生。一旦“脱离了人民群众”,久而久之就难免在市场竞争中处于颓势。恐怕这也能够解释为什么 IBM 去年斥 340 亿美元巨资收购红帽:IBM 不仅仅是看重 Red Hat 深厚的开源技术积累及其 OpenShift 云平台,也一定包含了对于其开发者人气和社区基因的考量,可以很好地弥补自身短板。

综上所述,“创新、垂直、混合、生态”这四大趋势,将伴随云计算走向繁荣。对于云计算的美好未来,我们已迫不及待。*后再作一个小小的预判:随着云的高度复杂化和差异化,企业会愈发需要面向云端各个层面的解读、判断与帮助,除了*方厂商支持团队的助力之外,独立的云计算咨询与托管服务会成为新的需求热点——这将催生一个不小的云增值业务市场,即 Cloud MSP (Managed Service Provider)。在国外,Cloud MSP 已有一定的关注度,Gartner 也开始为此领域发布观察报告及绘制魔力象限,目前埃森哲处于行业*地位;在国内,我们也欣喜地看到如云角信息(已被神州数码收购)、云宿科技、新钛云服等厂商中立的云 MSP 不断出现,且发展势头颇为良好。能否为客户持续创造价值,并形成合理的商业模式,将是决定 Cloud MSP 这一云衍生行业未来的关键。

写在*后

云计算走过了激荡十年,可谓势不可挡,风雨兼程。它如此巨大和丰富,虽万字不足以道其一二。限于篇幅,本文无法覆盖云计算的更多角度与话题,还请读者见谅。另外,本文对于云计算发展的分代方式仅供参考,事实上技术的发展更迭总是互相交织和持续进行的。

云计算历史性地对 IT 硬件资源与软件组件进行了标准化、抽象化和规模化,某种意义上颠覆和重构了 IT 业界的供应链,这是一个巨大的革新与进步。如今,云计算正迎来*好的时代,在中国这片广阔热土更是如此。我们由衷希望,云计算行业不仅取得商业上的成功,更能扎实服务各行各业,为社会经济发展提供数字化引擎和强大动力。让我们继续与云计算同行,与伟大的数字时代同行。

Python实现自定义队列

Python实现自定义队列

项目中需要读取一个pickle数据文件,pickle文件存储的是按行保存的文本,需要调用编码模型分别对每一条文本进行编码,然后使用numpy数组保存编码后的向量。由于模型每次只能输入一定batch size大小的数据,所以需要对数据继续分割后再输入模型编码向量,可以直接加载整个文件,通过batch size来分割整个文件数组。但是如果文件很大,这样的加载方式可能会导致内存溢出。因此考虑对文件进行单行读取,使用一个队列来接受读取的数据,当读取的行数等于batch size时,就将这个批次的数据送入模型进行编码。

于是自己写了一个简单的队列实现批量数据进入队列,队列满了之后清空队列,但与一般的队列不同,只使用一个index的指针来表示队尾巴,并实时返回队列是否已满(此处可以优化)。

队列代码

class data_queue(object):

def __init__(self,size,init_v = “”):
self.init_v = init_v
self.size = size
self.data = [init_v]*size
self.index = -1
self.isFull = False
self.over_v = init_v

def put(self,value):
self.index += 1
self.data[self.index] = value

if self.index == self.size-1:
self.isFull = True
self.over_v = value

return self.isFull

def pop(self):
if self.index < 0:
raise ValueError(“队列为空不能删除”)

# 从队首删除元素,后面的数据往前移
remain_data = self.data[1:self.index+1]
self.data[self.index] = self.init_v
self.data[:self.index] = remain_data

self.index -= 1

def pop_all(self):
if self.index < 0:
raise ValueError(“队列为空不能删除”)

for i in range(self.index,-1,-1):
self.data[i] = self.init_v
self.index -=1

self.isFull = False

def show_data(self):
print(self.index,self.data)

调用测试代码
#测试队列
data_list = [x*3 for x in “abcdefghijklmn”]
# 初始化队列
ds_q = data_queue(10)
tmp = []
for ele in data_list:
is_full = ds_q.put(ele)
ds_q.show_data()

if ele == “f”*3 or ele == “l”*3:
ds_q.pop()
print(“出队一个元素”)

if is_full:
print(“队列已满,返回队列中的值并清空队列”)
tmp = ds_q.data
ds_q.pop_all()

从调用结果来看,实现了队列的功能,在队列满队时就触发pop_all的操作,将所有的队列元素全部弹出去消费,将整个队列清空。因为我在实际使用时不使用pop单个元素的情况,所以编写出队操作的代码比较简单,直接将队头元素出队,整体移动后面的队列元素。但如果需要频繁出队就需要考虑性能,一般会设置队头指针,出队时对头指针往后移动,只有当队尾指针满队时再整体移动队列元素,或者使用循环队列,这个需要根据自己的需求来编写代码。

#测试结果
0 [‘aaa’, ”, ”, ”, ”, ”, ”, ”, ”, ”]
1 [‘aaa’, ‘bbb’, ”, ”, ”, ”, ”, ”, ”, ”]
2 [‘aaa’, ‘bbb’, ‘ccc’, ”, ”, ”, ”, ”, ”, ”]
3 [‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’, ”, ”, ”, ”, ”, ”]
4 [‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ”, ”, ”, ”, ”]
5 [‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ”, ”, ”, ”]
出队一个元素
5 [‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ‘ggg’, ”, ”, ”, ”]
6 [‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ‘ggg’, ‘hhh’, ”, ”, ”]
7 [‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ‘ggg’, ‘hhh’, ‘iii’, ”, ”]
8 [‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ‘ggg’, ‘hhh’, ‘iii’, ‘jjj’, ”]
9 [‘bbb’, ‘ccc’, ‘ddd’, ‘eee’, ‘fff’, ‘ggg’, ‘hhh’, ‘iii’, ‘jjj’, ‘kkk’]
队列已满,返回队列中的值并清空队列
0 [‘lll’, ”, ”, ”, ”, ”, ”, ”, ”, ”]
出队一个元素
0 [‘mmm’, ”, ”, ”, ”, ”, ”, ”, ”, ”]
1 [‘mmm’, ‘nnn’, ”, ”, ”, ”, ”, ”, ”, ”]

关于 Gen8 使用 esxi 磁盘性能的问题

我使用是 esxi6.5b ,驱动已经降级到 88,但是测速只有 20-30MB/S,想问下这样正常吗?

f2f2f 1
f2f2f 2018-07-19 10:41:43 +08:00
问题描述要清楚……啥硬件环境
dbw9580 2
dbw9580 2018-07-19 11:22:02 +08:00 via Android
正常,在我换到 proxmox 之前也是这样:
/t/414166
shinko 3
shinko 2018-07-19 11:30:14 +08:00
@dbw9580 我用 proxmox 同一个千兆口也过不了 40。。。。。。
shinko 4
shinko 2018-07-19 11:31:43 +08:00
@dbw9580 补充,是用 scp,虚拟机之间传输都不超过 40~~~
shinko 5
shinko 2018-07-19 11:42:32 +08:00
哦,测试传输到主机的速度可以去到 70,虚拟机之间是 30 多,估计是机械硬盘的瓶颈。主机是固态
局域网传输还没测试,因为无线路由接的是 100M 口
ys0290 6
ys0290 2018-07-19 12:09:21 +08:00 via iPhone
6.5.0 (Build 5146846) gen8
没改过任何驱动,从 linux A 系统的 nfs 磁盘 scp 传送 200MB 文件给 linux B 系统的系统盘,速度 88MB/s,nfs 盘是 1T HDD,目的磁盘是一块使用率五成以下的小磁盘。网络是 gen8 万兆网络

所以我在考虑要不要升级到 esxi6.7
xpfd 7
xpfd 2018-07-19 13:35:19 +08:00
买个硬盘卡做直通吧,其他的都不行
BOYPT 8
BOYPT 2018-07-19 13:36:43 +08:00
6.5.0 Update 2 (Build 8935087)

升级一下吧
DamonLee 9
DamonLee 2018-07-19 13:59:48 +08:00
@f2f2f gen8 e3 1230 v2 16G 内存
@dbw9580 是不是有点太慢了
@shinko 我也是千兆口
@ys0290 我也是觉得 100Mb/s 是正常的,但是为什么你会有这么快的速度?
@xpfd 直通卡好像不便宜啊
@BOYPT 我的版本是 VMware-ESXi-6.5.0-5310538-HPE-650.10.1.5.20-Oct2017
BOYPT 10
BOYPT 2018-07-19 14:08:47 +08:00
@DamonLee 直接在线升就可以

DamonLee 11
DamonLee 2018-07-19 14:56:29 +08:00
@BOYPT 我说错了,我用的是 VMware-ESXi-6.5.0-Update2-8294253-HPE-Gen9plus-650.U2.10.3.0.24-Jun2018.iso ,这个就是 6.7 之前的*新的 6.5 版本了?
BOYPT 12
BOYPT 2018-07-19 15:46:40 +08:00
@DamonLee emmm ios 是发布版,但是还有在线补丁的方式升级的;不过你这个情况应该就不是升级能解决的了
DamonLee 13
DamonLee 2018-07-19 17:16:35 +08:00
@BOYPT 哎,只是很诧异为什么磁盘性能会这么低
abchehehe32 14
abchehehe32 2018-07-19 17:46:15 +08:00 via iPhone
gen8 一直有这个问题,没记错的话 6.7 也没解决。chiphell 有大量替换驱动文件的教程,楼主可以参考下。我是另外买 p420 用 raid 的,原生的软 raid 已放弃,直接用 ahci 模式直通给虚拟机用。
linooy 15
linooy 2018-07-19 17:49:10 +08:00
请检查网卡,E1000 的虚拟网卡性能并不好.
ucanuup 16
ucanuup 2018-07-19 17:56:07 +08:00
我的 gen8 esxi 6.7 机械硬盘速度没有问题,但是 SSD 速度很慢,大概就 40M。我用的直通硬盘方式。
DamonLee 17
DamonLee 2018-07-19 21:26:02 +08:00 via iPhone
@abchehehe32 我替换了驱动
@linooy 虚拟网卡有解吗?
@ucanuup 直通还是 rdm ?
linooy 18
linooy 2018-07-19 22:03:59 +08:00 via Android
@DamonLee 在虚拟机配置里可以改啊
DamonLee 19
DamonLee 2018-07-20 08:38:21 +08:00
@linooy 我看了下我的事 VMXNET3
royal007 20
royal007 2018-07-24 20:49:37 +08:00
我的版本是 VMware-ESXi-6.5.0-5310538-HPE-650.10.1.5.20-Oct2017,和你相同。我从 PC 通过 ftp 软件 上传到 esxi 虚拟机里速度是 60MB/s,相当于千兆网络跑了 60%。 从 esxi 虚拟机 下载文件到 PC 里,速度只有 11MB/s,,感觉只达到了百兆的速率。我切换了网络适配器,从 E1000 换到 VMXNET3,测试结果都是一样的。我的 PC 环境是 SSD。目前也不知道是哪里出了问题,但是从虚拟机 cpu 使用情况来看,从虚拟机下载东西到 pc 端,cpu 占用率达到 30-40%不等。
DamonLee 21
DamonLee 2018-07-25 08:43:52 +08:00
@royal007 对,以前用 hyper-v 的时候可以跑满,看来真的跟网友说的一样,HP 不打算更新 GEN8 的驱动了,但是有的基友用 exsi 也能跑满,所以很凌乱
keramist 22
keramist 2018-08-05 22:34:38 +08:00 via Android
彻底转投 proxmox
jacoz126 23
jacoz126 2018-08-16 22:50:24 +08:00 via iPhone
用 ahci 模式,记得一定把 write cache 打开,硬盘 rdm,建议装个黑裙,把所有 rdm 的数据盘分配给黑裙,黑裙 nfs 服务提供给其他虚拟机用。
DamonLee 24
DamonLee 2018-08-17 08:30:29 +08:00
@jacoz126 已经装了黑裙,不过我是通过 ubuntu 开 nfs 给黑裙,因为之前的硬盘跑在 win server 上,是 ntfs 格式?,已经打开 write cache,现在速度稳定在 60-70mb/s,可能是买的*便宜的千兆线吧,不过已经达到要求了

远程玩我的世界,需要 vps 还是云服务器、云主机?

需要用到多高的配置?

已试过 Vultr 1c2g 装 win2008 带不动 MC。

已知有大佬用向日葵远程好几台玩 mc,win7 系统。无奈大佬不肯透露。特来求解。

ps:是玩 MC,不是搭服务器。

night98 1
night98 2018-08-31 15:13:10 +08:00 via Android
mc 很吃配置。。。
baicheng10 2
baicheng10 2018-08-31 15:17:45 +08:00
不理解这种操作……
sydeEvans 3
sydeEvans 2018-08-31 15:18:43 +08:00
为什么要远程玩
annoy1309 4
annoy1309 2018-08-31 15:27:31 +08:00 via Android
你这得选带 GPU 的机器了呀
liuxu 5
liuxu 2018-08-31 16:44:27 +08:00
云玩家
realkenshinji 6
realkenshinji 2018-08-31 17:03:41 +08:00 via iPhone
RPi 的配置都可以跑 MC,仅供参考。所以你可以考虑家里的 RPi 跑个 MC,然后通过 vnc 连过去玩
on1ydai 7
on1ydai 2018-08-31 17:37:30 +08:00
感谢各位大佬,刚刚发现有一个叫远程电脑出租的东西 0.0
b821025551b 8
b821025551b 2018-08-31 17:38:19 +08:00
树莓派都可以流畅跑 MC 啊
on1ydai 9
on1ydai 2018-08-31 21:58:38 +08:00
现在又有新的问题 T T。远程桌面玩 mc 的时候 3d 画面卡顿严重,试过 TV,向日葵,Anydesk 都差不多,不知道向日葵的付费服务是否能解决这个问题?
mostkia 10
mostkia 2018-09-17 15:55:40 +08:00
为什么要远程玩。。其实就算是搭建服务器,一般的小鸡 VPS 其实也挺吃力的,主要是内存不够(尤其是安装了 win 服务器后),U 的算力的话还是可以的。
但如果你是想在服务器上玩 3D 游戏,情况就完全不一样了,首先考虑延时的问题,国内主机还好,国外主机首先网络卡出翔,然后操作延时+显示返回延时叠加,然后是远程桌面低发色数+集成显卡卡顿。。。总之各种辣眼睛。还是放弃这个念头吧。

mostkia 2018-09-17 15:59:36 +08:00
@on1ydai 你说的卡顿,很大可能是网络问题。因为你的操作需要双倍的延时,比如,键盘操作 MC 里的人物,发送到服务器需要 100 毫秒,就算服务器没有处理时间,直接反映到画面上,这个画面,也得传送回你当前操作的客户端,那就又是 100 毫秒(这还是远程桌面完全不掉帧的理想状态下),总之那么无论如何,你操作都不会有流畅感可言,因为远程桌面不是用来玩游戏的。
dejavuwind 12
dejavuwind 2018-10-19 16:46:53 +08:00
不明白为什么会有这种奇葩需求

香港韩国日本美国站群服务器如果选择

%title插图%num

VPS值不值评测:https://www.vpszbz.com/

一提到做泛站群,蜘蛛池等专用的多IP服务器,大家首先想到的都是美国站群服务器。这主要是因为做SEO的人们*开始就是利用美国站群服务器来给网站做优化的。香港站群服务器后来也赶了上来,成为站群服务器当中使用数量第二多的地区。而韩国站群服务器到今天为止,还是问的人多,而用的人少。那么,这3个地区的站群服务器各有什么优缺点呢?为什么大家选择美国站群服务器的比较多呢?接下来,我就为大家一一分析,希望能帮助大家找到适合自己的站群服务器。
美国站群服务器作为站群服务器市场上的销量王,与其自身的诸多优点是离不开的。我认为美国站群服务器*大的优点就是价格便宜。就拿大家用的*多的美国加州地区的站群服务器来说,258个IP的服务器价格平均只有800元左右,这是香港258IP站群服务器价格的一半,是韩国258IP站群服务器的将近三分之一。做站群网站一般上都要用到很多台服务器,少则三五台,多则一二十台。如果站群服务器的价格太高,那么无形中就会为站群SEO的前期准备工作中增添一部分的压力。而选择美国站群服务器反而是*省钱的。当然价格便宜不是美国站群服务器的唯一优点。IP数量多,也是美国站群服务器熠熠生辉的原因之一。
美国站群服务器不但有一个C段258个IP的套餐可选,更有4个C段244个IP,8个C段232个IP,16个C段208个IP。甚至还有2个C段511个IP,4个C段1012个IP可选。这种分配到多个C段,甚至多个A段多达一千多个IP的优势,除了美国机房能做到之外,恐怕再也没有其他国家或者地区能做到了。这主要是因为美国是世界互联网的发源地,这里拥有世界上*多的IP资源,所以他们的IP就跟白菜一样廉价。美国站群服务器还有一个优点就是带宽大。美国站群服务器一般上都是100M以上的带宽,而香港服务器的带宽一般只有5M,韩国服务器的带宽也不过10M。更大的带宽就意味着能支持更大的访问量。如果你的网站网站访问量比较大,还是建议你用美国站群服务器。美国站群服务器还有一个优点就是服务器配置高。美国的互联网行业发达,带宽大,需要处理的数据量也比较大,所以它们机房里面的服务器硬件配置也很高。普遍都是至强E3和至强E5系列的处理器。这种处理器处理速度特别快。并且美国机房里面服务器硬件更新换代的频率也很高。基本三到五年就要换一批新的服务器硬件。这就保证了服务器能安全稳定的运行,不会出现超期服役的情况。说了美国站群服务器这么多的优点,*后,就该说说它的缺点了。美国服务器只有一个缺点,那就是从中国大陆访问的时候,打开网站的速度比较慢。在美国,香港,韩国这三个海外机房中,香港站群机房是距离中国大陆*近的,所以从大陆访问的时候,香港服务器的速度*快。只是香港站群因为带宽比较小,所以只适合访问量小,而且对网络速度要求比较高的站群服务器使用。其实说了这么多,可能你也发现了,韩国站群服务器加在中间显得特别鸡肋。论访问速度它没有香港机房快,论服务器带宽它没有美国机房大,而且韩国站群服务器的价格也是相当昂贵。其实这就是韩国站群服务器一直很少有人用的原因。
美国站群服务器,香港站群服务器,韩国站群服务器的各自特点
下面,让我再用几句话来概括一下各个机房的优缺点。
美国站群服务器的优点是价格便宜,IP数量多,带宽大。缺点是中国大陆访问的速度慢。美国站群服务器适合用来做泛站群,蜘蛛池,耐克抢购,亚马逊刷单等。综合看来,美国站群服务器是这3个机房中使用范围*广,性价比*高的。
香港站群服务器的优点是中国大陆访问的速度快。其实就凭香港站群服务器这无与伦比的网速,它就获得了诸多站群SEO站长的青睐。毕竟,网络速度好,丢包率低的香港服务器,对于站群SEO来说也是大有裨益的。香港站群服务器适合用来做泛站群,蜘蛛池,耐克抢购,亚马逊刷单等。
韩国站群服务器主要是作为一种候补,如果你认准韩国站群服务器,比如要用来挂游戏等。那么不失为一种不错的选择。不过需要说明的是,市场上很多韩国站群服务器的IP是从香港广播过去的,也就是虽然是韩国站群服务器,但是IP显示的却是香港地区。所以对于用来挂韩国游戏账号的人来说还是提前问清楚比较好。
希望已上建议对你帮助,有不懂欢迎留言一起交流。

Android 之 Window、WindowManager 与窗口管理

其实在android中真正展示给用户的是window和view,activity在android中所其的作用主要是处理一些逻辑问题,比如生命周期的管理、建立窗口等。在android中,窗口的管理还是比较重要的一块,因为他直接负责把内容展示给用户,并和用户进行交互。响应用户的输入等。

在讲窗口管理时,有必要先说下ViewManager这个接口,这个接口主要有以下的实现子接口和实现类,分别是:WindowManager和ViewGroup里面还有三个重要的方法:

      * addView(); 

     * updateViewLayout();

     * removeView();

在WindowManager中,addView方法表示的是将主窗口中的顶级view(也就是DecorView)添加到WindowManager中,并建立会话。接下来会详细介绍。我们先来看看Window

Window:

Window是android中的窗口,表示顶级窗口的意思,也就是主窗口,它有两个实现类,PhoneWindow和MidWindow,我们一般的activity对应的主要是PhoneWindow,在activity中经常使用的setContentView等方法也是在这个里面实现的。

    @Override

    public void setContentView(View view,ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();

        } else {
            mContentParent.removeAllViews();

        }

        mContentParent.addView(view, params);

        final Callback cb = getCallback();

        if (cb != null) {
           cb.onContentChanged();  //窗口类容发生变化时更新

        }

    }

 每个主窗口中都有一个View,称之为DecorView,是主窗口中的顶级view(实际上就是ViewGroup),在View中有两个成员变量叫做mParent、mChildren,它是用来管理view的上下级关系的。而ViewGroup是对一组View的管理。因此,在ViewGroup中建立了所有view的关系网。而*终ViewGroup附属在主窗口上。这样就很容易在窗口中通过findViewById找到具体的View了。view中的事件处理也是根据这个路径来处理的。

我们再来看看ActivityThead中的两个重要的方法(至于ActivityThead将在一篇中详细介绍):

           performLaunchActivity( );

            handleResumeActivity( );

在performLaunchActivity中,会调用activity.attach方法建立一个window, 在handleResumeActivity方法中启动activity的时候,会将主窗口加入到WindowManager中

             View decor =r.window.getDecorView();  //获得窗口的顶级View

      decor.setVisibility(View.INVISIBLE);

      ViewManager wm= a.getWindowManager();    //WindowManager继承自ViewManager

      WindowManager.LayoutParams l =r.window.getAttributes();

      a.mDecor = decor;

      l.type =WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

     l.softInputMode |= forwardBit;

     if (a.mVisibleFromClient) {
         a.mWindowAdded = true;

         wm.addView(decor, l);  //实际上是把主窗口的顶级view加入到WindowMangaer

      }

我们再来看看WindowManager。

WindowManager:

WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。

通过Context.getSystemService(Context.WINDOW_SERVICE)的方式可以获得WindowManager的实例.

WindowManager继承自ViewManager,里面涉及到窗口管理的三个重要方法,分别是:

     * addView(); 

     * updateViewLayout();

     * removeView();  

在WindowManager中还有一个重要的静态类LayoutParams.通过它可以设置和获得当前窗口的一些属性。

我们先来看看addView()方法,在addView中,会利用LayoutParams获得window的View属性,并为每个window创建ViewRoot,ViewRoot是View和WindowManager之间的桥梁,真正把View传递给WindowManager的是通过ViewRoot的setView()方法,ViewRoot实现了View和WindowManager之间的消息传递。在将主窗口添加到WindowManger时,它首先会建立一个代理对象:

              wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE)

并且打开会话(IWindowSession),之后Window将通过该会话与WindowManager建立联系,

来看下setView方法:

         try {

        res =sWindowSession.add(mWindow, mWindowAttributes,

         getHostVisibility(), mAttachInfo.mContentInsets);

     } catch (RemoteException e) {
         mAdded = false;

        mView = null;

         mAttachInfo.mRootView =null;

         unscheduleTraversals();

         throw newRuntimeException(“Adding window failed”, e);

      } finally {
         if (restore) {
            attrs.restore();

         }

      }

在这段代码中,ViewRoot通过IWindowSession把窗口添加到WindowManager中。ViewRoot继承了Handler,实际上它的本质就是一个Handler,窗口中View的事件处理、消息发送、回调等将通过ViewRoot来处理。

这样就完成了把窗口添加到WindowManager中,并交由WindowManager来管理窗口的view、事件、消息收集处理等。

iOS开发-NSNotification源码原理学习

文章目录
问题
1. 实现原理
对于addObserver方法,为什么需要object参数?
都传入null对象会怎么样
addObserver源码逻辑
2.通知的发送时同步的,还是异步的
3. NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
4.NSNotificationQueue和runloop的关系
5.如何保证通知接收的线程在主线程
6.多次添加同一个通知会是什么结果?多次移除通知呢?
7.下面的方式能接收到通知吗?为什么
问题
苹果并没有开源相关代码,但是可以读下 GNUStep 的源码

或者这里下载
链接:https://pan.baidu.com/s/1F25GgeLxqKjeo10Zgfr2OQ
密码:qpka

从下列问题触发,探索下NSNotification的实现原理

实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等)
通知的发送时同步的,还是异步的
NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
NSNotificationQueue是异步还是同步发送?在哪个线程响应
NSNotificationQueue和runloop的关系
如何保证通知接收的线程在主线程
页面销毁时不移除通知会崩溃吗
多次添加同一个通知会是什么结果?多次移除通知呢
下面的方式能接收到通知吗?为什么

1 // 发送通知
2 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@”TestNotification” object:@1];
3 // 接收通知
4 [NSNotificationCenter.defaultCenter postNotificationName:@”TestNotification” object:nil];

1. 实现原理
对于addObserver:selector:name:object:会创建一个observation

1 typedef
2   id
3   SEL
4   struct Obs
5   int
6   struct NCTbl
7 } Observation;

对于Observation持有observer

在iOS SDK 8之前:不是一个类似OC中的weak类型,持有的相当与一个__unsafe_unretain指针对象,当对象释放时,会访问已经释放的对象,造成BAD_ACCESS。
在iOS SDK 8之后:持有的是weak类型指针,对nil对象performSelector不再会崩溃
name和Observation是映射关系,observer和sel包含在Observation结构体中。

此外,NSNotification维护了GSIMapTable表的结构,用于存储Observation,分别是nameless,name,cache,nameless存储没有传入名字的通知,named存储传入了名字的通知,cache用于快速缓存.

1 #define CHUNKSIZE 128
2 #define CACHESIZE 16
3 typedef struct NCTbl {
4   Observation *wildcard; /* Get ALL messages. */
5   GSIMapTable nameless; /* Get messages for any name. */
6   GSIMapTable named; /* Getting named messages only. */
7   unsigned lockCount; /* Count recursive operations. */
8   NSRecursiveLock *_lock; /* Lock out other threads. */
9   Observation *freeList;
10   Observation **chunks;
11   unsigned numChunks;
12   GSIMapTable cache[CACHESIZE];
13   unsigned short chunkIndex;
14   unsigned short cacheIndex;
15 } NCTable;

这里值得注意nameless和named的结构,虽然同为hash表

1
2 在nameless表中:
3 GSIMapTable的结构如下
4 object : Observation
5 object : Observation
6 object : Observation
7
8 —————————-
9 在named表中:
10 GSIMapTable结构如下:
11 name : maptable
12 name : maptable
13 name : maptable
14
15 maptable的结构如下
16 object : Observation
17 object : Observation
18 object : Observation

关于GSIMap结构

对于addObserver方法,为什么需要object参数?
– (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

就是addObserver其实不用传入name也可以,传入object,当postNotification方法同样发出这个object时,就会触发通知方法。

例如这样的写法:

1 – (void)viewDidLoad {
2     [super viewDidLoad];
3     // Do any additional setup after loading the view.
4     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5     MyObject *obj = [MyObject new];
6     [center addObserver:self selector:@selector(doAction:) name:nil object:obj];
7     dispatch_async(dispatch_get_global_queue(0, 0), ^{
8         [center postNotificationName:@”TEST” object:obj];
9     });
10 }
11
12 – (void)doAction:(NSNotification*)sender {
13     NSLog(@”%s %@ %@”,__FUNCTION__,sender,[NSThread currentThread]);
14 }

参考 https://www.jianshu.com/p/83770200d476

都传入null对象会怎么样
你可能也注意到了,addObserver方法name和object都可以为空,这表示将会把observer赋值为 wildcard,他将会监听所有的通知。

addObserver源码逻辑
addObserver的逻辑如下

1. 根据传入的selector和observer创建Observation,并存入maptable中,如果已存在,则是从cache中取。
2. 如果name存在,则向named的maptable表中插入元素,key为name,value为GSIMapTable,GSIMapTable中存key为object,value为Observation,转入5。如果不存在则进入3
3. 如果object存在,则向nameless的maptable表中插入元素,key为object,value为Observation。如果不存在,则进入4,否则转入5
4. name和object都为空,则Observation->next=wildcard,将老的wildcard赋值为next指针,然Observation对象置为wildcard,wildcard = Observation
5.结束

2.通知的发送时同步的,还是异步的
postNotificationName的底层实现是

– (void) _postAndRelease: (NSNotification*)notification
由于内部会读取TABLE

lockNCTable(TABLE);
… //找到对应的observer对象
unlockNCTable(TABLE);

…//执行performSelector方法

1 lockNCTable(TABLE);
2 … //找到对应的observer对象
3 unlockNCTable(TABLE);
4
5 …//执行performSelector方法
6
7 lockNCTable(TABLE);
8 GSIArrayEmpty(a); //释放临时创建的数组对象 – 用于存储observer的
9 unlockNCTable(TABLE);

同步异步这个问题,由于TABLE资源的问题,同一个线程会按顺序执行,自然是同步的。

3. NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
由于是使用的performSelector方法,没有进行转线程,默认是postNotification方法的线程。

1
2 [o->observer performSelector: o->selector
3                                 withObject: notification];
4

对于异步发送消息,可以使用NSNotificationQueue,queue顾明意思,我们是需要将NSNotification放入queue中执行的。

有三种状态

1 typedef NS_ENUM(NSUInteger, NSPostingStyle) {
2     NSPostWhenIdle = 1,      // 当runloop处于空闲状态时post
3     NSPostASAP = 2,    // 当当前runloop完成之后立即post
4     NSPostNow = 3    // 立即post
5 };

 

1 NSNotification *noti = [NSNotification notificationWithName:@”111″ object:nil];
2 [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostASAP];

参考文章 https://www.jianshu.com/p/356f7af4f2ee

4.NSNotificationQueue和runloop的关系
NSNotificationQueue的执行是依赖于runloop的,它的三种模式各自的执行时机不一样。

1 typedef NS_ENUM(NSUInteger, NSPostingStyle) {
2     NSPostWhenIdle = 1,      // 当runloop处于空闲状态时post
3     NSPostASAP = 2,    // 当当前runloop完成之后立即post
4     NSPostNow = 3    // 立即post
5 };

例如

1 void asyncQueueNotiInRunloop() {
2     dispatch_async(dispatch_get_global_queue(0, 0), ^{
3         NSLog(@”1″);
4         NSLog(@”%@”, [NSThread currentThread]);
5
6         //NSPostWhenIdle
7         //NSPostASAP
8         //NSPostNow
9         NSNotification *notification = [NSNotification notificationWithName:@”TEST” object:nil];
10         [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
11         // run runloop
12         [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSRunLoopCommonModes];
13         CFRunLoopRun();
14         NSLog(@”3″);
15     });
16 }

如果去掉run runloop部分的代码,则无法触发通知

5.如何保证通知接收的线程在主线程
由于通知的发出使用performSeletor实现,如果需要保证接收的线程在主线程,可以:

保证主线程发出
接收到通知后跳转到主线程,苹果建议使用NSMachPort进行消息转发到主线程。
https://blog.csdn.net/shengpeng3344/article/details/90206265

使用block接口addObserverForName:object:queue:usingBlock:

1 – (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
2     // The return value is retained by the system, and should be held onto by the caller in
3     // order to remove the observer with removeObserver: later, to stop observation.

页面销毁时不移除通知会崩溃吗?
对于Observation持有observer

在iOS SDK 8之前:不是一个类似OC中的weak类型,持有的相当与一个__unsafe_unretain指针对象,当对象释放时,会访问已经释放的对象,造成BAD_ACCESS。
在iOS SDK 8之后:持有的是weak类型指针,对nil对象performSelector不再会崩溃
所以说不一定会崩溃,但是根据代码严谨是需要remove的

6.多次添加同一个通知会是什么结果?多次移除通知呢?
由于源码中并不会进行重复过滤,所以添加同一个通知,等于就是添加了2次,回调也会触发两次。

为什么会触发两次呢,因为- (void) postNotificationName: (NSString*)name object: (id)object的逻辑是这样的:

1. 查找所有是wildcard类型的Observation,加入数组array,即既不监听name也不监听object
2. 查找指定object但未指定name的Observation,加入数组array
3. 查找指定了相同name和object的Observation,加入数组array,当name一致但object不一致时,也不会加入到数组array,但如果传入的object!=nil,则会将name对应的maptable中,所有key为nil的Observation也加入数组。
4. 遍历array,执行performSelector
5. 清空array

第三步的意思是:如果发出一个通知,方法中传入了对象object,那么那些只监听通知name,object设置为nil的当然也可以收到,object匹配了的也可以收到,object不匹配的则收不到。

关于多次移除,并没有问题,因为会去map中查找,找到才会删除。当name和object都为nil时,会移除所有关于该observer的WILDCARD

7.下面的方式能接收到通知吗?为什么

1
2 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@”TestNotification” object:@1];
3
4 [NSNotificationCenter.defaultCenter postNotificationName:@”TestNotification” object:nil];

根据postNotification的实现,会找到key为TestNotification的maptable,再从中选择key为nil的observation,所以是找不到以@1为key的observation的

iOS开发-ViewController的生命周期相关

文章目录
ViewController生命周期
加载流程
didReceiveMemoryWarning
View的layoutSubviews
Runloop相关
view的drawRect:方法
ViewController生命周期
加载流程

%title插图%num

1 1.init或者initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
2 2.loadView:加载view
3 3.viewDidLoad:view加载完毕
4 4.viewWillAppear:控制器的view将要显示
5 5.viewWillLayoutSubviews:控制器的view将要布局子控件
6 6.viewDidLayoutSubviews:控制器的view布局子控件完成
7 这期间系统可能会多次调用viewWillLayoutSubviews、viewDidLayoutSubviews俩个方法
8
9 7.viewDidAppear:控制器的view完全显示
10 8.viewWillDisappear:控制器的view即将消失的时候
11 这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
12
13 9.viewDidDisappear:控制器的view完全消失的时候

didReceiveMemoryWarning

Discussion Your app never calls this method directly. Instead, this
method is called when the system determines that the amount of
available memory is low.

You can override this method to release any additional memory used by
your view controller. If you do, your implementation of this method
must call the super implementation at some point.

当app收到内存警告的时候会发消息给视图控制器。
app从来不会直接调用这个方法,而是当系统确定可用内存不足的时候采取调用。
如果你想覆写这个方法来释放一些控制器使用的额外内存,你应该在你的实现方法中调用父类的实现方法。

View的layoutSubviews
init初始化不会触发layoutSubviews。
addSubview会触发layoutSubviews。
改变一个UIView的frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
滚动一个UIScrollView引发UIView的重新布局会触发layoutSubviews。
旋转Screen会触发父UIView上的layoutSubviews事件。
直接调用setNeedsLayout 或者 layoutIfNeeded。

Runloop相关
在非主页面加载时

1 * thread #1, queue = ‘com.apple.main-thread’, stop reason = breakpoint 1.1
2   * frame #0: 0x0000000100529264 GSWatermarkView`-[GSWaterMarkView layoutSubviews](self=0x000000011d801410, _cmd=”layoutSubviews”) at GSWaterMarkView.m:110
3     frame #1: 0x00000001a77db5b0 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2156
4     frame #2: 0x00000001a2fe7af0 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 68
5     frame #3: 0x00000001a9d81c0c QuartzCore`-[CALayer layoutSublayers] + 292
6     frame #4: 0x00000001a9d81f14 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 484
7     frame #5: 0x00000001a9d953fc QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 140
8     frame #6: 0x00000001a9cda184 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double) + 296
9     frame #7: 0x00000001a9d05228 QuartzCore`CA::Transaction::commit() + 684
10     frame #8: 0x00000001a7362d6c UIKitCore`_afterCACommitHandler + 144
11     frame #9: 0x00000001a324bf5c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
12     frame #10: 0x00000001a3246bfc CoreFoundation`__CFRunLoopDoObservers + 420
13     frame #11: 0x00000001a32471ac CoreFoundation`__CFRunLoopRun + 1292
14     frame #12: 0x00000001a3246978 CoreFoundation`CFRunLoopRunSpecific + 480
15     frame #13: 0x00000001ad376534 GraphicsServices`GSEventRunModal + 108
16     frame #14: 0x00000001a7338f0c UIKitCore`UIApplicationMain + 1940
17     frame #15: 0x000000010052814c GSWatermarkView`main(argc=1, argv=0x000000016f8df940) at main.m:14
18     frame #16: 0x00000001a30c6f04 libdyld.dylib`start + 4

在初始界面加载时

1 * thread #1, queue = ‘com.apple.main-thread’, stop reason = breakpoint 1.1
2   * frame #0: 0x0000000100b05264 GSWatermarkView`-[GSWaterMarkView layoutSubviews](self=0x000000010130f210, _cmd=”layoutSubviews”) at GSWaterMarkView.m:110
3     frame #1: 0x00000001a77db5b0 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2156
4     frame #2: 0x00000001a2fe7af0 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 68
5     frame #3: 0x00000001a9d81c0c QuartzCore`-[CALayer layoutSublayers] + 292
6     frame #4: 0x00000001a9d81f14 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 484
7     frame #5: 0x00000001a9d953fc QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 140
8     frame #6: 0x00000001a9cda184 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double) + 296
9     frame #7: 0x00000001a9d05228 QuartzCore`CA::Transaction::commit() + 684
10     frame #8: 0x00000001a7350d20 UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 84
11     frame #9: 0x00000001a324c95c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
12     frame #10: 0x00000001a324c0e0 CoreFoundation`__CFRunLoopDoBlocks + 268
13     frame #11: 0x00000001a32470e0 CoreFoundation`__CFRunLoopRun + 1088
14     frame #12: 0x00000001a3246978 CoreFoundation`CFRunLoopRunSpecific + 480
15     frame #13: 0x00000001ad376534 GraphicsServices`GSEventRunModal + 108
16     frame #14: 0x00000001a7338f0c UIKitCore`UIApplicationMain + 1940
17     frame #15: 0x0000000100b0414c GSWatermarkView`main(argc=1, argv=0x000000016f303940) at main.m:14
18     frame #16: 0x00000001a30c6f04 libdyld.dylib`start + 4

可以看出在app启动时,初始界面View的layoutSubviews由__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__触发,后续的界面由__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__触发

view的drawRect:方法
直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect大小不能为0。
drawRect的调用时机是在viewWillAppear和viewDidAppear之间。且在View的layoutSubviews之后

1 2020-03-03 14:05:58.384185+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController loadView]]
2 2020-03-03 14:05:58.384252+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidLoad]]
3 2020-03-03 14:05:58.385593+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView didMoveToSuperview]]
4 2020-03-03 14:05:58.385686+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewWillAppear:]]
5 2020-03-03 14:05:58.387915+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewWillLayoutSubviews]]
6 2020-03-03 14:05:58.387956+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidLayoutSubviews]]
7 2020-03-03 14:05:58.387975+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView layoutSubviews]]
8 2020-03-03 14:05:58.388046+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView drawRect:]]
9 2020-03-03 14:05:58.427785+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidAppear:]]

调用sizeToFit,会触发drawRect的调用。
通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
app启动时,添加到初始界面的堆栈,还是CALayer触发

iOS开发-逆向注入SDK之MonkeyDev注入打包

文章目录
MonkeyDev
使用
注入SDK
MonkeyDev
强大的工具集,MonkeyDev ,使用它行了!

安装教程见 Wiki

使用
拿着之前通过 frida砸壳 的 ipa包,先创建一个 MonkeyDev 工程。这里以 qqmusic 为例

%title插图%num
将砸壳的 ipa 放到 qqmusic/TargetApp/ 下,然后拖拽至工程文件中。

设置主工程的证书,dylib的不用设置

%title插图%num

提示 Showing All Messages Signing for “qqmusicDylib” requires a development team. Select a development team in the Signing & Capabilities editor. ,此时选择 qqmusicDylib->Build Settings->Add User-Defined Setting

%title插图%num

添加 CODE_SIGNING_ALLOWED 为 NO,再运行即可。

file not found: /usr/lib/libstdc++.dylib 问题

原因是新版本xcode去掉了libstdc++这个库,从老版本复制过来即可,这里直接使用别人的 https://github.com/devdawei/libstdc-

注入SDK
可以运行之后,我们在工程中初始化pod,

1 修改pod文件,注释use_frameworks!
2 # platform :ios, ‘9.0’
3
4 target ‘qqmusic’ do
5   # Comment the next line if you don’t want to use dynamic frameworks
6   # use_frameworks!
7   pod ‘GrowingAnalytics-cdp/Autotracker’
8   # Pods for qqmusic
9
10 end
11
12 target ‘qqmusicDylib’ do
13   # Comment the next line if you don’t want to use dynamic frameworks
14   # use_frameworks!
15   pod ‘GrowingAnalytics-cdp/Autotracker’
16   # Pods for qqmusicDylib
17
18 end

查找对应的appdelegate类,使用class-dump命令
class-dump -H xxx.app -o yourDir/Headers
1
发现其 AppDelegate 类叫 XXXXAppDelegate

然后使用logos注入SDK初始化代码,使用文档查看官网 http://iphonedevwiki.net/index.php/Logos

%title插图%num

1 // See http://iphonedevwiki.net/index.php/Logos
2
3 #import <UIKit/UIKit.h>
4 #import “GrowingAutotracker.h”
5
6 static NSString *const kGrowingProjectId = @”91eaf9b283361032″;
7
8 %hook XXXXAppDelegate
9
10 – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
11         BOOL result = %orig;
12     GrowingTrackConfiguration *configuration = [GrowingTrackConfiguration configurationWithProjectId:kGrowingProjectId];
13     configuration.debugEnabled = YES;
14     configuration.impressionScale = 1.0;
15     configuration.dataCollectionServerHost = @”https://run.mocky.io/v3/08999138-a180-431d-a136-051f3c6bd306″;
16     [GrowingAutotracker startWithConfiguration:configuration launchOptions:launchOptions];
17         return result;
18 }
19
20 %end

然后再编译运行,至此,已经可以在App中调试SDK,并有相关日志输出了。