首页 文章 精选 留言 我的

精选列表

搜索[快速],共10006篇文章
优秀的个人博客,低调大师

[Phoenix] 一、快速入门

Phoenix作为应用层和HBASE之间的中间件,以下特性使它在大数据量的简单查询场景有着独有的优势 二级索引支持(global index + local index) 编译SQL成为原生HBASE的可并行执行的scan 在数据层完成计算,server端的coprocessor执行聚合 下推where过滤条件到server端的scan filter上 利用统计信息优化、选择查询计划(5.x版本将支持CBO) skip scan功能提高扫描速度 一般可以使用以下三种方式访问Phoenix JDBC API 使用Python编写的命令行工具(sqlline, sqlline-thin和psql等) SQuirrel 一、命令行工具psql使用示例 1.创建一个建表的sql脚本文件us_population.sql: CREATE TABLE IF NOT E

优秀的个人博客,低调大师

快速搭建 Hadoop 环境

对于Hadoop来说,最主要的是两个方面,一个是分布式文件系统HDFS,另一个是MapReduce计算模型,下面讲解下我在搭建Hadoop 环境过程。 Hadoop 测试环境 共4台测试机,1台namenode3台datanode OS版本:RHEL5.5X86_64 Hadoop:0.20.203.0 Jdk:jdk1.7.0 角色ip地址 namenode 192.168.57.75 datanode1192.168.57.76 datanode2192.168.57.78 datanode3192.168.57.79 一 部署 Hadoop 前的准备工作 1需要知道hadoop依赖Java和SSH Java1.5.x(以上),必须安装。 ssh必须安装并且保证sshd一直运行,以便用Hadoop脚本管理远端Hadoop守护进程。 2建立Hadoop公共帐号 所有的节点应该具有相同的用户名,可以使用如下命令添加: useraddhadoop passwdhadoop 3配置host主机名 tail-n3/etc/hosts 192.168.57.75namenode 192.168.57.76datanode1 192.168.57.78datanode2 192.168.57.79datanode3 4以上几点要求所有节点(namenode|datanode)配置全部相同 二 ssh 配置ssh 详细了解 1生成私匙id_rsa与公匙id_rsa.pub配置文件 [hadoop@hadoop1~]$ssh-keygen-trsa Generatingpublic/privatersakeypair. Enterfileinwhichtosavethekey(/home/hadoop/.ssh/id_rsa): Enterpassphrase(emptyfornopassphrase): Entersamepassphraseagain: Youridentificationhasbeensavedin/home/hadoop/.ssh/id_rsa. Yourpublickeyhasbeensavedin/home/hadoop/.ssh/id_rsa.pub. Thekeyfingerprintis: d6:63:76:43:e2:5b:8e:85:ab:67:a2:7c:a6:8f:23:f9hadoop@hadoop1.test.com 2私匙id_rsa与公匙id_rsa.pub配置文件 [hadoop@hadoop1~]$ls.ssh/ authorized_keysid_rsaid_rsa.pubknown_hosts 3把公匙文件上传到datanode服务器 [hadoop@hadoop1~]$ssh-copy-id-i~/.ssh/id_rsa.pubhadoop@datanode1 28 hadoop@datanode1'spassword: Nowtryloggingintothemachine,with"ssh'hadoop@datanode1'",andcheckin: .ssh/authorized_keys tomakesurewehaven'taddedextrakeysthatyouweren'texpecting. [hadoop@hadoop1~]$ssh-copy-id-i~/.ssh/id_rsa.pubhadoop@datanode2 28 hadoop@datanode2'spassword: Nowtryloggingintothemachine,with"ssh'hadoop@datanode2'",andcheckin: .ssh/authorized_keys tomakesurewehaven'taddedextrakeysthatyouweren'texpecting. [hadoop@hadoop1~]$ssh-copy-id-i~/.ssh/id_rsa.pubhadoop@datanode3 28 hadoop@datanode3'spassword: Nowtryloggingintothemachine,with"ssh'hadoop@datanode3'",andcheckin: .ssh/authorized_keys tomakesurewehaven'taddedextrakeysthatyouweren'texpecting. [hadoop@hadoop1~]$ssh-copy-id-i~/.ssh/id_rsa.pubhadoop@localhost 28 hadoop@localhost'spassword: Nowtryloggingintothemachine,with"ssh'hadoop@localhost'",andcheckin: .ssh/authorized_keys tomakesurewehaven'taddedextrakeysthatyouweren'texpecting. 4验证 [hadoop@hadoop1~]$sshdatanode1 Lastlogin:ThuFeb209:01:162012from192.168.57.71 [hadoop@hadoop2~]$exit logout [hadoop@hadoop1~]$sshdatanode2 Lastlogin:ThuFeb209:01:182012from192.168.57.71 [hadoop@hadoop3~]$exit logout [hadoop@hadoop1~]$sshdatanode3 Lastlogin:ThuFeb209:01:202012from192.168.57.71 [hadoop@hadoop4~]$exit logout [hadoop@hadoop1~]$sshlocalhost Lastlogin:ThuFeb209:01:242012from192.168.57.71 [hadoop@hadoop1~]$exit logout 三 java环境配置 1下载合适的jdk //此文件为64Linux系统使用的RPM包 wgethttp://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.rpm 2安装jdk rpm-ivhjdk-7-linux-x64.rpm 3验证java [root@hadoop1~]#java-version javaversion"1.7.0" Java(TM)SERuntimeEnvironment(build1.7.0-b147) JavaHotSpot(TM)64-BitServerVM(build21.0-b17,mixedmode) [root@hadoop1~]#ls/usr/java/ defaultjdk1.7.0latest 4配置java环境变量 #vim/etc/profile//在profile文件中加入如下信息: #addforhadoop exportJAVA_HOME=/usr/java/jdk1.7.0 exportCLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/ exportPATH=$PATH:$JAVA_HOME/bin //使环境变量生效 source/etc/profile 5拷贝/etc/profile到datanode [root@hadoop1 src]#scp/etc/profileroot@datanode1:/etc/ Theauthenticityofhost'datanode1(192.168.57.86)'can'tbeestablished. RSAkeyfingerprintisb5:00:d1:df:73:4c:94:f1:ea:1f:b5:cd:ed:3a:cc:e1. Areyousureyouwanttocontinueconnecting(yes/no)?yes Warning:Permanentlyadded'datanode1,192.168.57.86'(RSA)tothelistofknownhosts. root@datanode1'spassword: profile100%16241.6KB/s00:00 [root@hadoop1 src]#scp/etc/profileroot@datanode2:/etc/ Theauthenticityofhost'datanode2(192.168.57.87)'can'tbeestablished. RSAkeyfingerprintis57:cf:96:15:78:a3:94:93:30:16:8e:66:47:cd:f9:cd. Areyousureyouwanttocontinueconnecting(yes/no)?yes Warning:Permanentlyadded'datanode2,192.168.57.87'(RSA)tothelistofknownhosts. root@datanode2'spassword: profile100%16241.6KB/s00:00 [root@hadoop1 src]#scp/etc/profileroot@datanode3:/etc/ Theauthenticityofhost'datanode3(192.168.57.88)'can'tbeestablished. RSAkeyfingerprintis31:73:e8:3c:20:0c:1e:b2:59:5c:d1:01:4b:26:41:70. Areyousureyouwanttocontinueconnecting(yes/no)?yes Warning:Permanentlyadded'datanode3,192.168.57.88'(RSA)tothelistofknownhosts. root@datanode3'spassword: profile100%16241.6KB/s00:00 6 拷贝 jdk 安装包,并在每个datanode 节点安装 jdk 包 [root@hadoop1 ~]#scp-r/home/hadoop/src/hadoop@datanode1:/home/hadoop/ hadoop@datanode1'spassword: hadoop-0.20.203.0rc1.tar.gz100%58MB57.8MB/s00:01 jdk-7-linux-x64.rpm100%78MB77.9MB/s00:01 [root@hadoop1 ~]#scp-r/home/hadoop/src/hadoop@datanode2:/home/hadoop/ hadoop@datanode2'spassword: hadoop-0.20.203.0rc1.tar.gz100%58MB57.8MB/s00:01 jdk-7-linux-x64.rpm100%78MB77.9MB/s00:01 [root@hadoop1 ~]#scp-r/home/hadoop/src/hadoop@datanode3:/home/hadoop/ hadoop@datanode3'spassword: hadoop-0.20.203.0rc1.tar.gz100%58MB57.8MB/s00:01 jdk-7-linux-x64.rpm100%78MB77.9MB/s00:01 四 hadoop 配置 //注意使用hadoop 用户 操作 1配置目录 [hadoop@hadoop1~]$pwd /home/hadoop [hadoop@hadoop1~]$ll total59220 lrwxrwxrwx1hadoophadoop17Feb116:59hadoop->hadoop-0.20.203.0 drwxr-xr-x12hadoophadoop4096Feb117:31hadoop-0.20.203.0 -rw-r--r--1hadoophadoop60569605Feb114:24hadoop-0.20.203.0rc1.tar.gz 2配置hadoop-env.sh,指定java位置 vimhadoop/conf/hadoop-env.sh exportJAVA_HOME=/usr/java/jdk1.7.0 3配置core-site.xml//定位文件系统的namenode [hadoop@hadoop1~]$cathadoop/conf/core-site.xml <?xmlversion="1.0"?> <?xml-stylesheettype="text/xsl"href="configuration.xsl"?> <!--Putsite-specificpropertyoverridesinthisfile.--> <configuration> <property> <name>fs.default.name</name> <value>hdfs://namenode:9000</value> </property> </configuration> 4配置mapred-site.xml//定位jobtracker所在的主节点 [hadoop@hadoop1~]$cathadoop/conf/mapred-site.xml <?xmlversion="1.0"?> <?xml-stylesheettype="text/xsl"href="configuration.xsl"?> <!--Putsite-specificpropertyoverridesinthisfile.--> <configuration> <property> <name>mapred.job.tracker</name> <value>namenode:9001</value> </property> </configuration> 5配置hdfs-site.xml//配置HDFS副本数量 [hadoop@hadoop1~]$cathadoop/conf/hdfs-site.xml <?xmlversion="1.0"?> <?xml-stylesheettype="text/xsl"href="configuration.xsl"?> <!--Putsite-specificpropertyoverridesinthisfile.--> <configuration> <property> <name>dfs.replication</name> <value>3</value> </property> </configuration> 6配置master与slave配置文档 [hadoop@hadoop1~]$cathadoop/conf/masters namenode [hadoop@hadoop1~]$cathadoop/conf/slaves datanode1 datanode2 7拷贝hadoop目录到所有节点(datanode) [hadoop@hadoop1~]$scp-rhadoophadoop@datanode1:/home/hadoop/ [hadoop@hadoop1~]$scp-rhadoophadoop@datanode2:/home/hadoop/ [hadoop@hadoop1~]$scp-rhadoophadoop@datanode3:/home/hadoop 8格式化HDFS [hadoop@hadoop1hadoop]$bin/hadoopnamenode-format 12/02/0211:31:15INFOnamenode.NameNode:STARTUP_MSG: /************************************************************ STARTUP_MSG:StartingNameNode STARTUP_MSG:host=hadoop1.test.com/127.0.0.1 STARTUP_MSG:args=[-format] STARTUP_MSG:version=0.20.203.0 STARTUP_MSG:build=http://svn.apache.org/repos/asf/hadoop/common/branches/branch-0.20-security-203-r1099333;compiledby'oom'onWedMay407:57:50PDT2011 ************************************************************/ Re-formatfilesystemin/tmp/hadoop-hadoop/dfs/name?(YorN) Y//这里输入Y 12/02/0211:31:17INFOutil.GSet:VMtype=64-bit 12/02/0211:31:17INFOutil.GSet:2%maxmemory=19.33375MB 12/02/0211:31:17INFOutil.GSet:capacity=2^21=2097152entries 12/02/0211:31:17INFOutil.GSet:recommended=2097152,actual=2097152 12/02/0211:31:17INFOnamenode.FSNamesystem:fsOwner=hadoop 12/02/0211:31:18INFOnamenode.FSNamesystem:supergroupsupergroup=supergroup 12/02/0211:31:18INFOnamenode.FSNamesystem:isPermissionEnabled=true 12/02/0211:31:18INFOnamenode.FSNamesystem:dfs.block.invalidate.limit=100 12/02/0211:31:18INFOnamenode.FSNamesystem:isAccessTokenEnabled=falseaccessKeyUpdateInterval=0min(s),accessTokenLifetime=0min(s) 12/02/0211:31:18INFOnamenode.NameNode:Cachingfilenamesoccuringmorethan10times 12/02/0211:31:18INFOcommon.Storage:Imagefileofsize112savedin0seconds. 12/02/0211:31:18INFOcommon.Storage:Storagedirectory/tmp/hadoop-hadoop/dfs/namehasbeensuccessfullyformatted. 12/02/0211:31:18INFOnamenode.NameNode:SHUTDOWN_MSG: /************************************************************ SHUTDOWN_MSG:ShuttingdownNameNodeathadoop1.test.com/127.0.0.1 ************************************************************/ [hadoop@hadoop1hadoop]$ 9启动hadoop守护进程 [hadoop@hadoop1hadoop]$bin/start-all.sh startingnamenode,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-namenode-hadoop1.test.com.out datanode1:startingdatanode,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-datanode-hadoop2.test.com.out datanode2:startingdatanode,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-datanode-hadoop3.test.com.out datanode3:startingdatanode,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-datanode-hadoop4.test.com.out startingjobtracker,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-jobtracker-hadoop1.test.com.out datanode1:startingtasktracker,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-tasktracker-hadoop2.test.com.out datanode2:startingtasktracker,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-tasktracker-hadoop3.test.com.out datanode3:startingtasktracker,loggingto/home/hadoop/hadoop/bin/../logs/hadoop-hadoop-tasktracker-hadoop4.test.com.out 10验证 //namenode [hadoop@hadoop1logs]$jps 2883JobTracker 3002Jps 2769NameNode //datanode [hadoop@hadoop2~]$jps 2743TaskTracker 2670DataNode 2857Jps [hadoop@hadoop3~]$jps 2742TaskTracker 2856Jps 2669DataNode [hadoop@hadoop4~]$jps 2742TaskTracker 2852Jps 2659DataNode Hadoop监控web页面 http://192.168.57.75:50070/dfshealth.jsp 五 简单验证HDFS hadoop的文件命令格式如下: hadoopfs-cmd<args> //建立目录 [hadoop@hadoop1hadoop]$bin/hadoopfs-mkdir/test-hadoop //査看目录 [hadoop@hadoop1hadoop]$bin/hadoopfs-ls/ Found2items drwxr-xr-x-hadoopsupergroup02012-02-0213:32/test-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp //査看目录包括子目录 [hadoop@hadoop1hadoop]$bin/hadoopfs-lsr/ drwxr-xr-x-hadoopsupergroup02012-02-0213:32/test-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred drwx-------hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred/system -rw-------2hadoopsupergroup42012-02-0211:32/tmp/hadoop-hadoop/mapred/system/jobtracker.info //添加文件 [hadoop@hadoop1hadoop]$bin/hadoopfs-put/home/hadoop/hadoop-0.20.203.0rc1.tar.gz/test-hadoop [hadoop@hadoop1hadoop]$bin/hadoopfs-lsr/ drwxr-xr-x-hadoopsupergroup02012-02-0213:34/test-hadoop -rw-r--r--2hadoopsupergroup605696052012-02-0213:34/test-hadoop/hadoop-0.20.203.0rc1.tar.gz drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred drwx-------hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred/system -rw-------2hadoopsupergroup42012-02-0211:32/tmp/hadoop-hadoop/mapred/system/jobtracker.info //获取文件 [hadoop@hadoop1hadoop]$bin/hadoopfs-get/test-hadoop/hadoop-0.20.203.0rc1.tar.gz/tmp/ [hadoop@hadoop1hadoop]$ls/tmp/*.tar.gz /tmp/1.tar.gz/tmp/hadoop-0.20.203.0rc1.tar.gz //删除文件 [hadoop@hadoop1hadoop]$bin/hadoopfs-rm/test-hadoop/hadoop-0.20.203.0rc1.tar.gz Deletedhdfs://namenode:9000/test-hadoop/hadoop-0.20.203.0rc1.tar.gz [hadoop@hadoop1hadoop]$bin/hadoopfs-lsr/ drwxr-xr-x-hadoopsupergroup02012-02-0213:57/test-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred drwx-------hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred/system -rw-------2hadoopsupergroup42012-02-0211:32/tmp/hadoop-hadoop/mapred/system/jobtracker.info drwxr-xr-x-hadoopsupergroup02012-02-0213:36/user -rw-r--r--2hadoopsupergroup3212012-02-0213:36/user/hadoop //删除目录 [hadoop@hadoop1hadoop]$bin/hadoopfs-rmr/test-hadoop Deletedhdfs://namenode:9000/test-hadoop [hadoop@hadoop1hadoop]$bin/hadoopfs-lsr/ drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop drwxr-xr-x-hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred drwx-------hadoopsupergroup02012-02-0211:32/tmp/hadoop-hadoop/mapred/system -rw-------2hadoopsupergroup42012-02-0211:32/tmp/hadoop-hadoop/mapred/system/jobtracker.info drwxr-xr-x-hadoopsupergroup02012-02-0213:36/user -rw-r--r--2hadoopsupergroup3212012-02-0213:36/user/hadoop //hadoopfs帮助(部分) [hadoop@hadoop1hadoop]$bin/hadoopfs-help hadoopfsisthecommandtoexecutefscommands.Thefullsyntaxis: hadoopfs[-fs<local|filesystemURI>][-conf<configurationfile>] [-D<propertyproperty=value>][-ls<path>][-lsr<path>][-du<path>] [-dus<path>][-mv<src><dst>][-cp<src><dst>][-rm[-skipTrash]<src>] [-rmr[-skipTrash]<src>][-put<localsrc>...<dst>][-copyFromLocal<localsrc>...<dst>] [-moveFromLocal<localsrc>...<dst>][-get[-ignoreCrc][-crc]<src><localdst> [-getmerge<src><localdst>[addnl]][-cat<src>] [-copyToLocal[-ignoreCrc][-crc]<src><localdst>][-moveToLocal<src><localdst>] [-mkdir<path>][-report][-setrep[-R][-w]<rep><path/file>] [-touchz<path>][-test-[ezd]<path>][-stat[format]<path>] [-tail[-f]<path>][-text<path>] [-chmod[-R]<MODE[,MODE]...|OCTALMODE>PATH...] [-chown[-R][OWNER][:[GROUP]]PATH...] [-chgrp[-R]GROUPPATH...] [-count[-q]<path>] [-help[cmd]] 更多Hadoop 相关知识 结束 Hadoop 环境搭建步骤繁琐,需要具备一定的Linux 系统知识,需要注意的是,通过以上步骤搭建的Hadoop 环境只能让你大体了解的hadoop ,如果想将HDFS 用于线上服务,还需对hadoop 配置文档做进一步配置 ,后续文档将继续以博文的形式发布,敬请期待。 本文转自 dongnan 51CTO博客,原文链接:http://blog.51cto.com/dngood/775368

优秀的个人博客,低调大师

Swift语法快速索引

在WWDC的演示中就可以看出来Swift这个更接近于脚本的语言可以用更少的代码量完成和OC同样的功能。但是对于像我一样在战争中学习战争的同学们来说,天天抱着笨Swift Programming Language Reference之类的大部头看不实际。毕竟还是要养家糊口的。而且,那么1000+页内容讲的东西不是什么都要全部在平时工作中用到的。咱们就把平时用到的全部都放在一起,忘记了立马翻开看看,不知不觉的就学会了之后变成习惯。这样多省事。 变量 1 // Variable 2 var int_variable = 1 // 类型推断 3 var message : String 4 var x = 0.0, y = 0.0, z = 0.0 常量 // Constant let const_int = 1 //const_int = 10 ERROR: can not assign to let value 字符串 // String // 1. 定义 var empty_string = "" var another_empty_string = String() // 2. 拼接 var hello_string = "hello" var world_string = " world" hello_string += world_string // hello world let multiplier = 3 //let multiplier_message = "\(mulitplier) times 2.5 is \(Double(multiplier) * 2.5)" // 3. 比较 var hello_world_string = "hello world" hello_string == hello_world_string // all are "hello world", result is true if hello_string == hello_world_string { println("These two are equal") } Tuple // Tuple // 1. Unnamed tuple let http_not_found = (404, "Not Found") println("tuple item 1 \(http_not_found.0), tuple item 2 \(http_not_found.1)") // 2. Named tuple let (statusCode, statusMessage) = (404, "Not Found") statusCode // 404 statusMessage // "Not Found" let http_not_found2 = (statusCode:404, statusMessage:"Not Found") http_not_found2.statusCode // 404 http_not_found2.statusMessage // "Not Found" // 3. return tuple func getHttpStatus() -> (statusCode : Int, statusMessage : String){ // request http return (404, "Not Found") } 数组 // Array // 1. 定义 //var empty_array = [] // 在swift里没事不要这样定义数组。这是NSArray类型的,一般是Array<T>类型的 var empty_array : [Int] var empty_array2 = [Int]() var fire_works = [String]() var colors = ["red", "yellow"] var fires : [String] = ["small fire", "big fire"]; // Xcode6 beta3里数组的类型是放在方括号里的 var red = colors[0] // 2. append & insert colors.append("black") colors += "blue" colors += fires colors.insert("no color", atIndex: 0) // 3. update colors[2] = "light blue" //colors[5...9] = ["pink", "orange", "gray", "limon"] // 4. remove colors.removeAtIndex(5) //colors[0] = nil ERROR! // other colors.isEmpty colors.count 字典 // Dictionary // 1. 定义 var airports : Dictionary<String, String> = ["TYP":"Tokyo", "DUB":"Boublin"] var airports2 = ["TYP":"Tokyo", "DUB":"Boublin"] var empty_dic = Dictionary<String, String>() var empty_dic2 = [:] // 2. update airports.updateValue("Dublin International", forKey: "DUB") airports["DUB"] = "Dublin International" // 3. insert airports["CHN"] = "China International" // 4. check exists if let airportName = airports["DUB"] { println("The name of the airport is \(airportName).") } else{ println("That airport is not in the airports dictionary.") } // 5. iterate for (airportCode, airportName) in airports{ println("\(airportCode):\(airportName)") } // 6. remove airports.removeValueForKey("TYP") airports["DUB"] = nil 枚举 // Enum // 1. defination & usage enum PowerStatus: Int{ case On = 1 case Off = 2 } enum PowerStatus2: Int{ case On = 1, Off, Unknown } var status = PowerStatus.On enum Barcode { case UPCA(Int, Int, Int) case QRCode(String) } var product_barcode = Barcode.UPCA(8, 8679_5449, 9) product_barcode = .QRCode("ABCDEFGHIJKLMN") switch product_barcode{ case .UPCA(let numberSystem, let identifier, let check): println("UPC-A with value of \(numberSystem), \(identifier), \(check)") case .QRCode(let productCode): println("QR code with value of \(productCode)") } 方法 // Function // 1. 定义 func yourFuncName(){ } // 2. 返回值 func yourFuncNameWithReturnType()->String{ return "" } // 3. 参数 func funcWithParameter(parameter1:String, parameter2:String)->String{ return parameter1 + parameter2 } funcWithParameter("1", "2") // 4. 外部参数名 func funcWithExternalParameter(externalParameter p1:String) -> String{ return p1 + " " + p1 } funcWithExternalParameter(externalParameter: "hello world") func joinString(string s1: String, toString s2: String, withJoiner joiner: String) -> String { return s1 + joiner + s2 } joinString(string: "hello", toString: "world", withJoiner: "&") // 外部内部参数同名 func containsCharacter(#string: String, #characterToFind: Character) -> Bool { for character in string { if character == characterToFind { return true } } return false } containsCharacter(string: "aardvark", characterToFind: "v") // 默认参数值 func joinStringWithDefaultValue(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String { return s1 + joiner + s2 } joinStringWithDefaultValue(string: "hello", toString: "world") //joiner的值默认为“ ” // inout参数 func swapTwoInts(inout a: Int, inout b: Int) { let temporaryA = a a = b b = temporaryA } var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // prints "someInt is now 107, and anotherInt is now 3 类 // Class // 1. 定义 class NamedShape { var numberOfSides: Int = 0 var name: String // *这样定义的初始化函数,其参数在调用的时候必须作为外名称使用 init(name: String) { self.name = name } // *这样定义的参数,在初始化的时候可以不使用外名称 init(_ nickName: String){ self.name = nickName } func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } }// 2. 继承 & 函数重载 & 属性getter setter class Square: NamedShape { var sideLength: Double init(sideLength: Double, name: String) { self.sideLength = sideLength super.init(name: name) numberOfSides = 4 } func area() -> Double { return sideLength * sideLength } // 函数重载 override func simpleDescription() -> String { return "A square with sides of length \(sideLength)." } } class EquilateralTriangle: NamedShape { var sideLength: Double = 0.0 init(sideLength: Double, name: String) { self.sideLength = sideLength super.init(name: name) numberOfSides = 3 } // 属性的getter setter var perimeter: Double { get { return 3.0 * sideLength } set { sideLength = newValue / 3.0 } } override func simpleDescription() -> String { return "An equilateral triagle with sides of length \(sideLength)." } } // 3. 使用 var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle") triangle.perimeter triangle.perimeter = 9.9 triangle.sideLength 使用闭包给属性赋初值 struct Checkerboard { let boardColors: Bool[] = { var temporaryBoard = Bool[]() var isBlack = false for i in 1...10 { for j in 1...10 { temporaryBoard.append(isBlack) isBlack = !isBlack } isBlack = !isBlack } return temporaryBoard }() func squareIsBlackAtRow(row: Int, column: Int) -> Bool { return boardColors[(row * 10) + column] } } 类(二) 1. 属性的初始化 2. init函数中修改常量属性 struct Color{ // 在初始化函数中可以修改这些在定义时没有给出初始值的属性 let red, green, blue: Double // stored property(就是下面这样定义的),如果是optional的(结尾时?活着!),可以在初始化函数 // 中不给出初始值,否则必须给出初始值。 var alpha: Double! init(r red: Double, green: Double, blue: Double){ self.red = red self.green = green self.blue = blue } init(white: Double){ red = white green = white blue = white } init(_ aColor: Double){ self.red = aColor self.green = aColor self.blue = aColor } } 3. 构造函数 // 默认init函数 class ShoppingItem { var name: String? // var quantity: Int // 编译错误,要使代码正确需要注释掉这一句 var purchased = false // 除非定义的属性全部都有初始值。optional的属性的默认值是nil。 // 否则的话必须显示定义一个init函数。 init(){ } init(name: String, purchased: Bool){ self.name = name self.purchased = purchased } // 结构体等自定义值类型可以直接在一个init函数中使用self.init(...)的方式调用 // 其他的init函数。但是在class(引用类型)中需要显示制定convenience关键字 // 才可以调用其他的init函数 convenience init(name: String){ self.init(name: "hello", purchased: true) } } var item = ShoppingItem() struct Color{ // 在初始化函数中可以修改这些在定义时没有给出初始值的属性 let red, green, blue: Double // stored property(就是下面这样定义的),如果是optional的(结尾时?活着!),可以在初始化函数 // 中不给出初始值,否则必须给出初始值。 var alpha: Double! init(r red: Double, green: Double, blue: Double){ self.red = red self.green = green self.blue = blue } //在init函数中调用其他init函数 init(white: Double){ // red = white // green = white // blue = white self.init(r: 1.0, green: 2.0, blue:1.0) } } convenience的init函数只能在本类中调用。而一般的init函数(也就是designated init)可以在继承的链中在一个类中调用super类的init函数。 继承 不想什么被继承就在什么的前面放个final关键字(以前有@号,现在木有了)。如果在class前面放final关键字的话,那么整个类不可以被继承。 class ShoppingListItem: ShoppingItem{ final var wishListed: Bool? // 不被继承 override init(){ super.init(name: "what", purchased: false) } } 析构函数 class Vehicle{ var numberOfWheels = 0 var description: String{ return "\(numberOfWheels) wheel(s)" } deinit{ // 析构具体内容 } } 析构函数执行的特点: 1. 系统自动调用,不允许手动调用 2. 执行完本类调执行super类的 3. 执行完析构函数之后实例才释放,所以可以析构函数里可以访问全部属性的值 WEAK和UNOWNED关键字 class Customer{ let name: String var card: CreditCard? init(name: String) { self.name = name } } class CreditCard{ let number: Int /* * weak和owned关键字都用来修饰属性的,为了防止循环引用造成的内存无法释放。 * 区别就在于unowned是一定要有值的。所以unowned只可以用在非可选类型上(non-optional type) */ unowned let customer: Customer init(number: Int, customer: Customer){ self.number = number self.customer = customer } } 在闭包中也会出现这样的问题。如果在闭包中使用了self.xxxProperty也会出现对类实例本身的一个强引用,从而出现了循环引用。PS:在闭包中使用类成员的时候必须要用self.的写法。 } } class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { [unowned self] in if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { println("\(name) is being deinitialized") } } 其他稍后补充 欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处! 本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/3851717.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

Themosis —— WordPress 快速开发框架

Themosis 是一种框架,可帮助开发者使用WordPress更快地开发网站和 Web 应用程序。Themosis 框架使用优雅而简单的代码语法,可帮助开发者构建和组织代码,并允许开发者更好地管理和扩展 WordPress 网站和应用程序。 Themosis 框架是一款面向任何级别的 WordPress 开发人员的工具。但是,您对 WordPress 和 PHP 的了解越多,使用起来就越容易。 要求: 服务器要求与WordPress基本相同,但额外增加了一些: PHP >= 7.1 IntlPHP 扩展 我们建议至少使用 PHP 7.2+ 安装 Composer Themosis 框架使用Composer管理其依赖项并轻松加载其文件。请按照此处的说明在计算机上本地或全局安装 Composer。 在 Windows 上,您可以使用 ComposerWindows 安装程序。 我们建议在系统上全局安装 Composer。 安装 Themosis 框架 打开Terminal或Console执行以下命令: composer create-project themosis/themosis my-project-name 这将在计算机上调用的目录创建一个my-project-name,并自动下载最新的 WordPress 版本以及最新的 Themosis 框架版本及其依赖项。 安装Themosis 默认情况下,不再使用 Composer 安装默认主题。 相反,CLI 工具现在与框架捆绑在一起并安装在项目根目录下。要安装新主题,请从终端运行以下命令: php console theme:install 该命令将要求用户输入主题名称,然后将主题下载并解压缩到htdocs/content/themes目录中。

优秀的个人博客,低调大师

如何快速定位程序Core?

导读:程序core是指应用程序无法保持正常running状态而发生的崩溃行为。程序core时会生成相关的core-dump文件,是程序崩溃时程序状态的数据备份。core-dump文件中包含内存、处理器、寄存器、程序计数器、栈指针等状态信息。本文将介绍一些利用core-dump文件定位程序core原因的方法和技巧。 全文7023字,预计阅读时间 13分钟。 一、程序Core定义及分类 程序core是指应用程序无法保持正常running状态而发生的崩溃行为。程序core时会生成相关的core-dump文件,core-dump文件是程序崩溃时程序状态的状态数据备份。core-dump文件包含内存、处理器、寄存器、程序计数器、栈指针等状态信息。我们可以借助core-dump文件来分析定位程序Core的原因。 这里我们从三个方面对程序Core进行分类:机器、资源、程序Bug。下表对常见的Core原因进行了分类: 二、函数栈介绍 当我们打开core文件时,首先关注的是程序崩溃时的函数调用栈状态,为了方便理解后续定位core的一些技巧,这里先简单介绍一下函数栈。 2.1 寄存器介绍 目前生产环境都为64位机,这里只介绍64位机的寄存器,如下: 对于x86-64架构,共有16个64位寄存器,每个寄存器的用途并不单一,如%rax通常保存函数返回结果,但也被应用于imul和idiv指令。这里重点关注%rsp(栈顶指针寄存器)、%rbp(栈底指针寄存器)、%rdi、%rsi、%rdx、%rcx、%r8、%r9(分别对应第1~6函数参数)。 Callee Save说明是否需要被调用者保存寄存器的值。 2.2 函数调用 2.2.1 调用函数栈帧: 在调用一个函数时首先进行的是参数压栈,参数压栈的顺序跟参数定义的顺序相反。注意,并不是参数一定会压栈,在x86-64架构中会针对可以使用寄存器传递的变量,直接通过寄存器传值,如数字、指针、引用等。 接着是**返回地址压栈,**返回地址为被调用函数执行完后,调用函数执行的下一个指令地址。这里牢记返回地址的位置,后续章节会利用到这个返回地址的特性。 针对上面的介绍举个例子说明: 如上图,在main函数中调用了foo函数,首先对参数压栈,三个参数都可以直接用寄存器传递(分别对应%edi、%esi、%edx),然后call指令将下一个指令压栈。 2.2.2 被调用函数栈帧: 被调用函数首先会将上一个函数的栈底指针(%rbp)保存,即%rbp压栈。然后再保存需要被保存的寄存器值,即Callee Save为True的寄存器。接着为临时变量、局部变量申请栈空间。 针对被调用函数,举个例子说明: 如上图,在foo函数执行时,先对main函数的%rbp压栈,再把寄存器中的参数值存放到局部变量(a, b, c)中。 2.3 总结 通过对函数调用的简单介绍,我们可以发现函数栈是一个缜密且脆弱的结构,内存结构必须按照严格的方式被访问,如稍有不慎就可能导致程序崩溃。 三、GDB定位Core 这一节将介绍从core文件打开到定位全流程中可能会遇到的问题以及解决技巧。 3.1 Core文件 core文件在哪里? 查看“/proc/sys/kernel/core_pattern”确定core文件生成规则。 3.2 变量打印 程序debug过程中常常要查看各种变量(内存、寄存器、函数表等)的值是否正确,维持单独用一节介绍下常用的变量打印方法以及一些冷门小技巧。 3.2.1 print命令 print [Expression] print $[Previous value number] print {[Type]}[Address] print [First element]@[Element count] print /[Format] [Expression] Format格式: o - 8进制 x - 16进制 u - 无符号十进制 t - 二进制 f - 浮点数 a - 地址 c - 字符 s - 字符串 3.2.2 x命令 x /<n/f/u> <addr> n:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内容,一个内存单元的大小由第三个参数u定义。 f:表示addr指向的内存内容的输出格式, s对应输出字符串,此处需特别注意输出整型数据的格式: x 按十六进制格式显示变量. d 按十进制格式显示变量。 u 按十进制格式显示无符号整型。 o 按八进制格式显示变量。 t 按二进制格式显示变量。 a 按十六进制格式显示变量。 c 按字符格式显示变量。 f 按浮点数格式显示变量。 u:就是指以多少个字节作为一个内存单元-unit,默认为4。 u还可以用被一些字符表示: 如b=1 byte, h=2 bytes,w=4 bytes,g=8 bytes.<addr>:表示内存地址。 3.2.3 容器对象打印 利用上面的print和x命令,再结合容器的数据结构,我们就能知道容器的详细信息。这里举个完整打印二进制string的例子,string的数据结构如下: string为空时,_M_dataplus._M_p是指向nullptr的。当赋值后会在堆上申请一段内存,分为两段,前半段是meta信息(类型为std::string::_Rep),如length、capacity、refcount,后半段为数据区,_M_p指向数据区。 通常情况下非二进制的string,直接print即可显示数据内容,但当数据为二进制时,'\0'会截断打印内容。因此,打印二进制string的首要任务是确认string的size。 string的size信息保存在std::string::_Rep结构体中,根据上面的数据结构可以发现,_Rep与_M_dataplus._M_p相差一个结构体大小,因此打印_Rep结构体的命令为: #先把_M_p转成_Rep指针,再让指针向低地址偏移一个结构体大小 p *((std::string::_Rep*)(s._M_dataplus._M_p) - 1) 找到string的size(_M_length)后,再通过x命令打印相关的内存区即可,命令为: #这里的n是_Rep._M_length x /ncb s._M_dataplus._M_p 运行效果如下: 为了方便,这里推荐一个方便的脚本:stl-views.gdb(链接:https://sourceware.org/gdb/wiki/STLSupport?action=AttachFile&do=view&target=stl-views-1.0.3.gdb,直接在gdb终端source stl-views.gdb即可,支持常见的容器打印,如vector、map、list、string等。 3.2.4 静态变量打印 程序中经常会使用到静态变量,有时我们需要查看某个静态对象的值是否正确,就涉及到静态对象的打印。看如下例子: void foo() { static std::string s_foo("foo"); } 这里可以借助nm -C ./bin | grep xx找到静态变量的内存地址,再通过gdb的print打印。 3.2.5 内存dump dump [format] memory filename start_addr end_addr dump [format] value filename expr format一般使用binary,其他的可以查看gdb手册。 比如我们可以结合上面查看string内容的例子dump整个string数据到文件中。 dump binary memory file1 s._M_dataplus._M_p s._M_dataplus._M_p + length 如果想查看文件内容的话可把vim -b和xxd结合使用。 接上面string的例子,举一个dump string内存数据到文件的例子: 3.3 定位代码行 定位core的原因,首先要定位崩溃时正在执行的代码行,这一节主要介绍一些定位代码行的方法。通常情况下直接通过gdb的breaktrace即可一览整个函数栈,但有时候函数栈信息并非如此清晰明了,这时就可利用一些小技巧来查看函数栈。 3.3.1 去编译优化 有时候会发现core的函数栈跟实际的代码行不匹配,如果是在线下环境中,可以尝试把编译优化设置成**-O0**,然后再重新复现core问题。 3.3.2 程序****计数器 + addr2line 对于线上core问题,一般没法再对程序进行去编译优化操作,只能在现有的core文件基础上进行代码定位。这一节我们采用一个例子来介绍如何使用程序计数器 + addr2line来定位代码行。 从截图可以发现frame 20指示的代码行与实际的代码行是不匹配的,定位步骤如下: # 跳转到第20号栈 frame 20 # 使用display命令显示程序计数器 display /i $rip # 使用addrline工具做地址转换 shell /opt/compiler/gcc-8.2/bin/addr2line -e bin address 3.3.3 函数栈修复 有时候我们会发现函数调用栈里面会出现很多??的情况,这常发生于栈被写花,某些情况下手动进行修复。函数栈的修复利用的函数栈内存分布知识,见第一节。 ----------------------------------- Low addresses ----------------------------------- 0(%rsp) | top of the stack frame | (this is the same as -n(%rbp)) ---------|-------------------------- n(%rbp) | variable sized stack frame- 8(%rbp) | varied 0(%rbp) | previous stack frame address 8(%rbp) | return address ----------------------------------- High addresses 从上面的栈示意图可以发现,利用%rbp寄存器即可找到上一个函数的返回地址和栈底指针,再利用addr2line命令找到对应的代码行。这里举一个例子: #首先找到当前被调用栈上一个栈的栈底指针值和返回地址 x /2ag $rbp # 2个单位,a=十六进制,g=8字节单元 #使用上一条命令得到的栈底指针值依次递归 x /2ag address 3.3.4 无规律core栈 无规律core栈问题一般发生于堆内存写坏。函数调用是一个非常精密的过程,任何一个位置发生非预期的读写都会导致程序崩溃。这里可以举个小例子来说明: int main(int argc, char* argv[]) { std::string s("abcd"); *reinterpret_cast<uint64_t*>(&s) = 0x11; return 0; } 上面的例子core在string析构上,原因是因为string的_M_ptr被改写成了0x11,析构流程变成了非法内存操作。 同理,由于进程堆空间是共享的,一个线程对堆的非法操作就可能会影响另一个线程的正常操作,由于堆分配的随机性,表现出来的现象就是无规律core栈。 针对无规律core栈最好的方式还是借助AddressSanitizer。 #设置编译参数CXXFLAGS CXXFLAGS="-fPIC -fsanitize=address -fno-omit-frame-pointer" #设置链接参数 LDFLAGS="-lasan" # 设置启动环境变量 export ASAN_OPTIONS=halt_on_error=0:abort_on_error=1:disable_coredump=0 # 启动 LD_PRELOAD=/opt/compiler/gcc-8.2/lib/libasan.so ./bin/xxx 3.3.5 总结 上面提到的几种方法都是为了找到具体的问题代码行,为后续分析core的具体原因提供线索。 3.4 定位Core原因 这一节主要介绍定位Core原因的方法以及一些常见原因的介绍。 3.4.1 确认信号量 从上面的Core分类我们可以发现某些场景的core是由于机器故障导致的,如SIGBUS,因此可以先通过信号量排除掉一些core原因。 3.4.2 定位异常汇编指令 通过上面的代码行定位我们可以大致找到程序core在哪一行,比较简单的core直接print程序上下文即可找到core的原因。 但有些场景下,通过排查上下文无任何异常,这个时候就需要准确定位具体的异常汇编指令,根据指令找原因。 查看汇编指令比较简单的方法是使用layout asm命令,frame指向那个栈,就显示对应栈的汇编。这里举个core例子,如下: 程序显示core在start函数,查看相关上下文变量均无异常。使用layout asm打开正在执行的汇编指令,如下: 查看汇编定位到程序core在mov指令,mov指令上一个指令为sub,为栈申请了3M空间,怀疑是栈空间不足。采用frame 0的%rsp - frame N的%rbp排查为栈空间不足。 通过上面的例子,可以发现定位异常汇编指令位置后,我们能够把异常点进一步压缩,定位到是哪个指令、变量、地址导致的core问题。 3.4.3 排查异常变量 通过上面的操作我们可以准确定位到具体是哪一行代码的哪一条指令出现了问题,根据异常指令我们可以排查相关的变量,确定变量值是否符合预期。 这里举一个比较经典的空指针例子,如下: int main(int argc, char* argv[]) { int* a = nullptr; *a = 1; return *a; } 通过汇编指令我们可以发现是movl $0x1, (%rax)出现了问题,%rax的值来自于0x8(%rbp),x命令打印相关的地址就可以发现为空指针错误。 3.4.4 查看被优化变量 通常情况下程序都是开启了编译优化的,就会出现变量无法被print,提示变量被优化,有时可利用汇编 + 寄存器的方式查看被优化的变量。 这里举一个例子说明下: void foo(char const* str) { char buf[1024] = {'\0'}; memcpy(buf, str, sizeof(buf)); } int main(int argc, char* argv[]) { foo("abcd"); return 0; } 通常情况下在foo函数内部,str变量是会直接别优化掉的,因为可以直接利用%rdi寄存器传递参数。为了能够打印出str的值,这个时候我们可以借助汇编 + 寄存器的方式找到具体的变量值,如下: 首先找到main函数调用foo函数的参数压栈汇编:mov $0x402011, %edi,这里的0x402011即为str的内存地址,通过x命令即可显示str的值了。 比较复杂的场景可能没法直接找到被优化变量,这时可以采用汇编回溯的方式找到变量。 3.4.5 异常函数地址排查 有时的core问题是因为数据异常导致,有时也可能是优化函数地址导致,如调用虚函数地址错误、函数返回地址错误、函数指针值错误。 异常函数地址排查同理于异常变量排查,根据汇编指令确认调用是否异常即可。这里举一个虚函数地址异常的例子,如下: class A { public: virtual ~A() = default; virtual void foo() = 0; }; class B : public A { public: void foo() {} }; int main(int argc, char* argv[]) { A* a = new B; a->foo(); A* b = new B; *reinterpret_cast<void**>(b) = 0x0; b->foo(); return 0; } 从汇编指令看是core在了mov (%rax), %rax,结合指令上下文可发现是在虚函数地址寻址操作,对比两个变量的虚函数表即可发现是函数地址load错误导致的core。 3.4.6 总结 定位core的基本流程可总结为以下几步: 明确core的大致触发原因。机器问题?自身程序问题? 定位代码行。哪一行代码出现了问题。 定位执行指令。哪一行指令干了什么事。 定位异常变量。指令不会有问题,是指令操作的变量不符合预期。 善于利用汇编指令以及**打印指令(x、print、display)**可以更有效的定位Core。 参考资料: 汇编查看工具:https://godbolt.org/ https://cppinsights.io/ 标准GDB文档:https://sourceware.org/gdb/current/onlinedocs/gdb/ 招聘信息: 欢迎加入百度移动生态事业群内容中台架构团队,我们常年需求后端、C++、模型架构、大数据、性能调优的同学、社招,实习,校招都要哦 简历投递邮箱:geektalk@baidu.com (投递备注【内容架构】) 推荐阅读: |面向大规模商业系统的数据库设计和实践 |百度爱番番移动端网页秒开实践 |解密百TB数据分析如何跑进45秒 ---------- END ---------- 百度Geek说 百度官方技术公众号上线啦! 技术干货 · 行业资讯 · 线上沙龙 · 行业大会 招聘信息 · 内推信息 · 技术书籍 · 百度周边 欢迎各位同学关注

优秀的个人博客,低调大师

Docker镜像管理快速入门

简介 介绍如何使用Docker构建镜像,并通过阿里云镜像服务分发到ECS服务器,运行该镜像。 背景知识 容器技术容器是一个允许我们在资源隔离的过程中,运行应用程序和其依赖项的 、轻量的 、操作系统级别的虚拟化技术, 运行应用程序所需的所有必要组件都打包为单个镜像,这个镜像是可以重复使用的。当镜像运行时,它是运行在独立的环境中,并不会和其他的应用共享主机操作系统的内存、CPU或磁盘。这保证了容器内的进程不会影响到容器外的任何进程。 镜像仓库(Registry)Docker的镜像存储中心通常被称为Registry。当您需要获取自己私有镜像时,首先需要登录Registry,然后拉取镜像。修改过镜像之后,您可以再次将镜像推送回Registry中去。或者在本地通过Docker镜像构建的功能生成镜像,再推送到Registry中。 容器镜像服务ACR(Alibaba Cloud Container Registry)阿里云容器镜像服务ACR默认实例版提供基础的容器镜像服务,包括安全的应用镜像托管能力、精确的镜像安全扫描功能、稳定的国内外镜像构建服务以及便捷的镜像授权功能,从而方便用户进行镜像全生命周期管理。 连接ECS服务器(如未购买可在此处体验ECS) 1.打开系统自带的终端工具。Windows:CMD或Powershell。MAC:Terminal。 在终端中输入连接命令ssh [username]@[ipaddress]。您需要将其中的username和ipaddress替换为第1小节中创建的ECS服务器的登录名和公网地址。例如: ssh root@123.123.123.123 命令显示结果如下: 3.输入yes,同意继续后将会提示输入登录密码。 密码为已创建的云服务的ECS的登录密码。 搭建Docker服务 Docker 是一个开源的容器引擎,用于创建、管理和编排容器,可以轻松为任何应用创建一个轻量级、可移植、自给自足的容器。本步骤将在ECS上部署一个Docker服务,并配置DockerHub的镜像加速器。 安装Docker的依赖库。 yum install -y yum-utils device-mapper-persistent-data lvm2 添加Docker CE的软件源信息。 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装Docker CE。 yum makecache fast && yum -y install docker-ce 启动Docker服务。 systemctl start docker 配置DockerHub镜像加速器。 tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://registry.docker-cn.com"] } EOF 重启Docker服务。 systemctl restart docker 准备应用代码和Dockerfile 本步骤操作将在工作空间下创建一个基于Golang的HelloWorld代码文件和一个用来构建运行Hello代码所需环境镜像的Dockerfile文件。 创建工作空间。 mkdir -p /tmp/demo && cd /tmp/demo 在工作空间下创建HelloWorld代码文件,用来在容器环境中监听HTTP服务,输出HelloWorld字符串。 cat > /tmp/demo/main.go << EOF package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello! World\n") }) fmt.Println("start to serve...") http.ListenAndServe(":80", nil) } EOF 在工作空间下创建Dockerfile文件。 cat > /tmp/demo/Dockerfile << EOF FROM golang:1.12-alpine # change current working dir WORKDIR /go/src/app # copy main.go into /go/src/app COPY . . # go build and install the app RUN go install -v ./... # run the app by default CMD ["app"] EOF 本地构建镜像并运行镜像 本步骤将指导您使用Docker基本命令构建和运行镜 使用docker build命令构建镜像。 docker build . -t demo:v1 命令运行结果如下所示: 说明:参数.表示指定当前路径作为构建上下文,即Dockerfile所在的本地路径。参数-t demo:v1指定镜像名称和标签。 使用docker run命令运行镜像。 docker run -d -p 8000:80 demo:v1 命令运行结果如下所示: 说明: 参数-d设置容器运行模式为后台运行。参数-p 8000:80将容器内部使用的网络端口映射到主机上,其中8000为主机端口,80为容器内部使用端口。 使用curl工具访问容器中的HelloWorld服务。 curl localhost:8000 命令运行结果如下所示: 4.使用docker rm命令删除容器。 docker rm -f $(docker ps -a | grep "demo:v1" | awk '{print $1}') 命令运行结果如下所示: 创建远程镜像仓库 本步骤操作将指导您使用自己的阿里云账号开通容器镜像服务并创建镜像仓库。阿里云容器镜像服务默认实例版可免费使用,其使用限制请参见容器镜像服务计费说明。 1.使用您的阿里云主账号登录容器镜像服务控制台。 2.【前往开通】》单击【设置Registry登录密码】》设置阿里云Docker Registry登录密码,然后单击 【确定】 。3.在容器镜像服务控制台,依次单击 【默认实例】 > 【命名空间】 > 【创建命名空间】,在 【创建命名空间】 弹框中填写命令空间名称,然后单击 【确定】 。 单击 【本地仓库】 > 【创建镜像仓库】 。在镜像仓库列表,选择上一步创建的镜像仓库,单击 【管理】 ,查看镜像仓库详情。 推送镜像 本步骤操作将本地镜像推送到远程仓库中,并运行远程仓库中的指定版本镜像。请将本步骤命令中涉及的远程镜像地址替换为步骤六中创建的镜像仓库的公网地址。 1.执行以下命令登录到阿里云Docker Registry。 docker login --username="用户名" registry.cn-hangzhou.aliyuncs.com 说明: 请将下面命令中的用户名替换为您的阿里云账号全名,回车后输入远程镜像仓库密码,密码为步骤六开通服务时设置的密码。 请登录阿里云用户中心查看您的主账号用户名称。命令运行结果如下所示: 2.标记本地镜像,将其归入远程仓库。 docker tag demo:v1 registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 3.将本地镜像推送到远程仓库。 docker push registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 拉取指定版本的远程镜像。 docker pull registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 运行拉取的远程镜像。 docker run -d -p 8000:80 registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 访问HelloWorld服务。 curl localhost:8000 命令运行结果如下所示:

优秀的个人博客,低调大师

混合云的快速指南

混合云模型为企业提供了一种利用公共云和私有云优势的方法。 混合云带来了两全其美的优势。 在最初的几年中,云计算技术日渐盛行,得到了IT和金融专业人士的称赞。但是,那些使用极其敏感或关键任务系统的人仍然不愿意使用该技术。企业开始做的是保持平衡并采用混合模型,而不是全盘采用内部部署数据中心或公共系统。 混合云是一种利用私有云和公共云在同一组织内执行不同功能的集成云服务。所有云计算服务应在不同程度上提供一定的效率,但公共云服务可能比私有云更具成本效益和可扩展性。借助混合云,组织可以通过将公共云服务用于所有不敏感的操作,而仅在需要时依靠私有云,从而确保所有平台无缝集成,从而最大限度地提高效率。 混合云是指由本地基础设施、私有云服务和公共云(例如Amazon Web Services(AWS)或Microsoft Azure)组成的混合计算、存储和服务环境,其中各个平台之间进行了编排。在数据中心中结合使用公共云,本地计算和私有云意味着企业拥有混合云基础设施。 混合云是一种计算环境,它通过允许在数据和应用程序之间共享而将公共云和私有云结合在一起。当计算和处理需求波动时,混合云计算使企业能够将其内部部署基础设施无缝扩展到公共云,以处理任何溢出。无需让第三方数据中心访问其全部数据。组织可以在基本和非敏感计算任务上获得公共云的灵活性和计算能力,同时将关键业务应用程序和数据安全地保留在公司防火墙的内部。 混合云通常是指公共云服务和内部私有云的结合;但是,混合云也可以由不同提供商提供的两个公共云组成,甚至可以由云和传统IT组成。实际上,当前将传统IT基础设施上的现有系统与公共云服务相结合的设置是混合云最常见的用例。 混合云使企业可以使用更广泛的IT服务组合。例如,企业可能在私有云中运行关键任务工作负载,但使用公共云提供商的数据库或归档服务。 这意味着,公共云的即付即用的可扩展性非常适合繁忙或不可预测的流量,并且可以降低IT成本。当企业需要增强安全性并需要对关键业务应用程序和数据进行最终控制时,请合并私有云。利用单租户专用服务器的性能,企业可以在裸机上获得超快的性能、安全性和可靠性。混合云方法可以统一这两种方法,可以为企业提供经济高效的安全解决方案。 混合云用例是大数据处理。例如,一家公司可以使用混合云存储来保留其累积的业务、销售、测试和其他数据,然后在公共云中运行分析查询,这可以扩展Hadoop或其他分析集群以支持要求苛刻的分布式计算任务。 尽管有好处,混合云计算仍会带来技术,业务和管理方面的挑战。这需要本地IT员工和云架构师的大量专业知识。诸如数据库,帮助台系统和其他工具之类的其他软件的实施可能会使私有云进一步复杂化。此外,企业对私有云的技术支持负全部责任,并且必须适应公共云API的任何更改以及服务随时间的变化。 此外,可能存在潜在的连接问题、服务等级协议(SLA)违规以及其他可能的服务中断。为了减轻这些风险,组织可以设计与多个公共云提供商进行互操作的混合云工作负载。在某些情况下,企业需要重新设计混合云计划的工作负载,以解决特定公共云提供商的API。 迁移到混合云可以节省资金,并使企业更高效、,更敏捷。与私有云相比,公共云可能会提供更重要的规模经济,例如集中管理,因此具有更高的成本效率。因此,混合云使企业可以尽可能多地访问这些节省的业务功能,同时仍保持敏感操作的安全性。

优秀的个人博客,低调大师

GraphQL快速入门教程

摘要: 体验神奇的GraphQL! 原文:GraphQL 入门详解 作者:MudOnTire Fundebug经授权转载,版权归原作者所有。 GraphQL简介 定义 一种用于API调用的数据查询语言 核心思想 传统的api调用一般获取到的是后端组装好的一个完整对象,而前端可能只需要用其中的某些字段,大部分数据的查询和传输工作都浪费了。graphQL提供一种全新数据查询方式,可以只获取需要的数据,使api调用更灵活、高效和低成本。 特点 需要什么就获取什么数据 支持关系数据的查询 API无需定义各种路由,完全数据驱动 无需管理API版本,一个版本持续演进 支持大部分主流开发语言和平台 强大的配套开发工具 使用方法 下面我们通过搭建一个SpaceX的新闻网站来直观学习graphQL的基本使用方法,所有数据由 官方API 获得。 GraphQL服务端 服务端采用node + express。新建一个node项目,安装如下依赖: $ npm i graphql express-graphql express axios 创建入口文件 server.js,里面创建express服务。使用graphQL我们只需要设置一个路由,所有的请求都由这个graphQL的request handler处理: const express = require("express"); const graphqlHTTP = require("express-graphql"); const schema = require("./schema"); const app = express(); app.use( "/graphql", graphqlHTTP({ schema, graphiql: true }) ); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); graphqlHTTP是grapql的http服务,用于处理graphql的查询请求,它接收一个options参数,其中schema是一个 GraphQLSchema实例,我们接下来定义,graphiql设置为true可以在浏览器中直接对graphQL进行调试。更多express-graphql的用法请参考 Github express-graphql。 schema 接下来我们定义schema,schema意为‘模式’,其中定义了数据模型的结构、字段的类型、模型间的关系,是graphQL的核心。 新建schema.js文件,首先定义两个数据模型:LaunchType(发射)和 RocketType(火箭)。注意字段的数据类型需要使用GraphQL定义的,不能使用js中的基本数据类型。 const { GraphQLObjectType, GraphQLInt, GraphQLString, GraphQLBoolean, GraphQLList, GraphQLSchema } = require("graphql"); const LaunchType = new GraphQLObjectType({ name: "Launch", fields: () => ({ flight_number: { type: GraphQLInt }, mission_name: { type: GraphQLString }, launch_date_local: { type: GraphQLString }, launch_success: { type: GraphQLBoolean }, rocket: { type: RocketType } }) }); const LaunchType = new GraphQLObjectType({ name: "Rocket", fields: () => ({ rocket_id: { type: GraphQLString }, rocket_name: { type: GraphQLString }, rocket_type: { type: GraphQLString } }) }); 有了数据模型之后,我们需要从数据库或者第三方API获取数据,在此我们从spacex的官方API获取。我们需要定义一个root query,root query做为所有查询的入口,处理并返回数据,更多请参考 GraphQL Root fields & resolvers。 在 schema.js中增加代码: const axios = require("axios"); const RootQuery = new GraphQLObjectType({ name: "RootQueryType", fields: { launches: { type: new GraphQLList(LaunchType), resolve(parent, args) { return axios .get("https://api.spacexdata.com/v3/launches") .then(res => res.data); } } } }); module.exports = new GraphQLSchema({ query: RootQuery }); 查询列表 完成这一步,服务端api基本搭建完成!我们看一下效果,在浏览器中输入 http://localhost:5000/graphql 将打开 Graphiql(生产环境建议禁用): 我们可以只查询所有的 flight_number: 或者更多的属性: 是不是很简单很神奇! 单个查询 我们也可以通过传入参数查询单条信息: const RootQuery = new GraphQLObjectType({ name: "RootQueryType", fields: { launch: { type: LaunchType, args: { flight_number: { type: GraphQLInt } }, resolve(parent, args) { return axios .get( `https://api.spacexdata.com/v3/launches/${ args.flight_number }` ) .then(res => res.data); } } } }); 结果: 推荐大家使用Fundebug,一款很好用的BUG监控工具~ GraphQL前端 刚刚我们都是用GraphiQL在浏览器调用接口,接下来我们看一下在前端页面中怎么调用graphql服务。前端我们使用react。 在项目根目录初始化react项目: $ npx create-react-app client 为了便于调试,在package.json中增加scripts: "start": "node server.js", "server": "nodemon server.js", "client": "npm start --prefix client", "dev":"concurrently \"npm run server\" \"npm run client\" " 样式我们使用bootswatch中的一款主题: GraphQL的客户端有多种实现,本次项目使用 Apollo,最流行的GraphQL Client。更多client请参考 GraphQL Clients。 安装依赖 安装如下依赖: $ cd client $ npm i apollo-boost react-apollo graphql 其中 apollo-boost 是apollo client本身,react-apollo 是react视图层的集成,graphql 用于解析graphql的查询语句。 设置client 修改App.js内容如下: import React, { Component } from "react"; import ApolloClient from "apollo-boost"; import { ApolloProvider } from "react-apollo"; import "./theme.css"; import "./App.css"; import logo from "./spacex-logo-light.png"; const client = new ApolloClient({ uri: "http://localhost:5000/graphql" }); class App extends Component { render() { return ( <ApolloProvider client={client}> <div className="container"> <img src={logo} id="logo" /> </div> </ApolloProvider> ); } } export default App; 和redux使用<Provider>传递store类似,react-apollo 通过 <ApolloProvider>将apollo client向下传递。 实现query 接着我们来实现显示launches的component,新增文件 components/Launches.js: import React, { Component, Fragment } from "react"; import gql from "graphql-tag"; import { Query } from "react-apollo"; import LaunchItem from "./LaunchItem"; const LAUNCHES_QUERY = gql` query LaunchesQuery { launches { flight_number mission_name launch_date_local launch_success } } `; export class Launches extends Component { render() { return ( <Fragment> <h1 className="display-4 my-3">Launches</h1> <Query query={LAUNCHES_QUERY}> {({ loading, error, data }) => { if (loading) return <h4>Loading...</h4>; if (error) console.log(error); return ( <Fragment> {data.launches.map(launch => ( <LaunchItem key={launch.flight_number} launch={launch} /> ))} </Fragment> ); }} </Query> </Fragment> ); } } export default Launches; query语句通过 graphql-tag 定义,传入 <Query> 执行获取数据并传入 LaunchItem 显示。 components/LaunchItem.js: import React from "react"; export default function LaunchItem({ launch: { flight_number, mission_name, launch_date_local, launch_success } }) { return ( <div className="card card-body mb-3"> <div className="col-md-9"> <h4>Mission: {mission_name}</h4> <p>Date: {launch_date_local}</p> </div> <div className="col-md-3"> <button className="btn btn-secondary">Launch Details</button> </div> </div> ); } 查询语句通过graphql-tag定义,然后传入<Query>执行。 运行 由于本地调试,client和server分别运行在不同的端口,所以需要先进行跨域处理,使用 cors。 // server.js const cors = require('cors'); app.use(cors()); 效果 好了,大功告成,我们来看一下效果: 结语 今天就主要介绍GraphQL工程的搭建和GraphQL Query的使用,更多关于GraphQL的内容比如 Mutation下次有空会跟大家逐步讲解。 本文灵感来源:Youtube@Traversy Media,感谢 本文Demo Github地址:Github@MudOnTire 本文Demo线上展示:Heroku@graphql-spacex-launches 关于Fundebug Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle

Oracle

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Apache Tomcat

Apache Tomcat

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。