Spark on Yarn遇到的几个问题

1 概述

Spark的on Yarn模式。其资源分配是交给Yarn的ResourceManager来进行管理的。可是眼下的Spark版本号,Application日志的查看,仅仅能通过Yarn的yarn logs命令实现。

在部署和执行Spark Application的过程中,假设不注意一些小的细节,或许会导致一些问题的出现。

2 防火墙

部署好Spark的包和配置文件,on yarn的两种模式都无法执行,在NodeManager端的日志都是说Connection Refused,连接不上Driver所在的client节点,可是client的80port能够正常訪问!同一时候,在日志中有类似信息出现:

Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient memory

内存肯定是够的。但就是无法获取资源。检查防火墙,果然client仅仅开启的对80port的訪问,其它都禁止了!

假设你的程序在执行的时候也有类似连接被拒绝的情况。最好也是先检查下防火墙的配置!

3 Spark Driver程序host的指定

部署完Spark后,分别使用yarn-cluster模式和yarn-client模式执行Spark自带的计算pi的演示样例。

Spark的一些配置文件除了一些基本属性外,均未做配置,结果执行的时候两种执行模式出现了不同的状况。

yarn-cluster模式可以正常执行,yarn-client模式总是执行失败。查看ResourceManager、NodeManager端的日志。发现程序总是找不到ApplicationMaster,这就奇怪了!

而且,客户端的Driver程序开启的port。在NodeManager端訪问被拒绝。非Spark的其它MR任务。可以正常执行。

检查client配置文件。发现原来在client的/etc/hosts文件里。client的一个IP相应了多个Host,Driver程序会默认去取最后相应的那个Host,比方是hostB,可是在NodeManager端是配置的其它Host。hostA。所以导致程序无法訪问。为了不影响其它的程序使用client的Host列表,这里在Spark配置文件spark-defaults.conf中使用属性spark.driver.host来指定yarn-client模式执行中和Yarn通信的DriverHost。此时yarn-client模式能够正常执行。

上面配置完了之后,发现yarn-cluster模式又不能执行了!

想想原因,肯定是上面那个配置參数搞的鬼,凝视掉之后,yarn-cluster模式能够继续执行。原因是,yarn-cluster模式下。spark的入口函数是在client执行,可是Driver的其它功能是在ApplicationMaster中执行的。上面的那个配置相当于指定了ApplicationMaster的地址,实际上的ApplicationMaster在yarn-master模式下是由ResourceManager随机指定的。

4 on  Yarn日志的查看

測试环境下,通过yarn logs -applicationId xxx能够查看执行结束的Application的日志,可是搞到还有一个环境下发现使用上述命令查看日志时,总是提演示样例如以下信息:

Logs not available at /tmp/nm/remote/logs/hadoop/logs/application_xxx_xxx

Log aggregation has not completed or is not enabled.

去相应的NodeManger文件夹下,确实找不到日志文件。

可是/tmp/nm/remote/logs却是在yarn-site.xml中指定了的文件夹。这个是对的,究竟什么原因呢?难道是Yarn的日志聚集没有起作用?

去NodeManager上查看相应Application的日志:

2014-08-04 09:14:47,513 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.AppLogAggregatorImpl: Starting aggregate log-file for app application_xxx_xxx at /tmp/nm/remote/logs/spark/logs/application_xxx_xxx/hostB.tmp2014-08-04 09:14:47,525 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.AppLogAggregatorImpl: Uploading logs for container container_xxx_xxx_01_000007. Current good log dirs are /data/nm/log2014-08-04 09:14:47,526 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.AppLogAggregatorImpl: Uploading logs for container container_xxx_xxx_000001. Current good log dirs are /data/nm/log2014-08-04 09:14:47,526 INFO org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor: Deleting path : /data/nm/log/application_xxx_xxx2014-08-04 09:14:47,607 INFO org.apache.hadoop.yarn.server.nodemanager.containermanager.logaggregation.AppLogAggregatorImpl: Finished aggregate log-file for app application_xxx_xxx

可见,日志聚集确实起作用了,可是为什么通过命令不能查看!

猛然见看到日志中“/tmp/nm/remote/logs/spark/logs/ application_xxx_xxx/hostB.tmp”。日志的路径有问题,在使用yarn logs命令查看的时候。用的是hadoop用户。实际Spark Application的提交运行用的是spark用户,而yarn logs命令默认去找的是当前用户的路径,这就是查看不到日志的原因。切换到spark用户再查看,日志最终出来了。

5 LZO相关问题

假设在Spark中使用了LZO作为EventLog的的压缩算法等,就得实现安装好LZO这个东东,否则会出现类似于例如以下的异常:

Caused by: java.lang.IllegalArgumentException: Compression codec com.hadoop.compression.lzo.LzoCodec not found.at org.apache.hadoop.io.compress.CompressionCodecFactory.getCodecClasses(CompressionCodecFactory.java:134)at org.apache.hadoop.io.compress.CompressionCodecFactory.<init>(CompressionCodecFactory.java:174)at org.apache.hadoop.mapred.TextInputFormat.configure(TextInputFormat.java:45)... 66 moreCaused by: java.lang.ClassNotFoundException: Class com.hadoop.compression.lzo.LzoCodec not foundat org.apache.hadoop.conf.Configuration.getClassByName(Configuration.java:1680)at org.apache.hadoop.io.compress.CompressionCodecFactory.getCodecClasses(CompressionCodecFactory.java:127)... 68 more

或者

[ERROR] [2014-08-05 10:34:41 933] com.hadoop.compression.lzo.GPLNativeCodeLoader [main] (GPLNativeCodeLoader.java:36) Could not load native gpl libraryjava.lang.UnsatisfiedLinkError: no gplcompression in java.library.path

解决的方法就是得安装好LZO,而且在HDFS、SPARK中配置好相关的包、文件等,详细步骤见:

http://find.searchhub.org/document/a128707a98fe4ec6

https://github.com/twitter/hadoop-lzo/blob/master/README.md

http://hsiamin.com/posts/2014/05/03/enable-lzo-compression-on-hadoop-pig-and-spark/

6 Spark Hive无法訪问Mysql的问题

生产环境下,节点之间肯定是有防火墙限制的。并且Hive的元数据库Mysql,更是对请求的IP和用户等限制的严格,假设在Spark集群中使用yarn-cluster模式进行提交Spark的Application。其执行时Driver是和ApplicationMaster执行在一起。由Yarn的ResourceManager负责分配到集群中的某个NodeManager节点上,假设在Hive-site.xml中仅仅配置了Mysql数据库而没有配置MetaStore的话,或许会遇到连接元数据库失败的问题,此时,就得看下Hive-site.xml的配置,是否Mysql的相关权限配置正确、MetaStore服务能否够正常连接。

7 内存溢出问题

在Spark中使用hql方法运行hive语句时。因为其在查询过程中调用的是Hive的获取元数据信息、SQL解析,而且使用Cglib等进行序列化反序列化。中间可能产生较多的class文件,导致JVM中的持久代使用较多,假设配置不当,可能引起类似于例如以下的OOM问题:

Exception in thread "Thread-2" java.lang.OutOfMemoryError: PermGen space

原因是实际使用时。假设用的是JDK1.6版本号,Server模式的持久代默认大小是64M,Client模式的持久代默认大小是32M,而Driver端进行SQL处理时。其持久代的使用可能会达到90M,导致OOM溢出。任务失败。

解决方法就是在Spark的conf文件夹中的spark-defaults.conf里。添加对Driver的JVM配置。由于Driver才负责SQL的解析和元数据获取。

配置例如以下:

spark.driver.extraJavaOptions -XX:PermSize=128M -XX:MaxPermSize=256M   

可是,上述情况是在yarn-cluster模式下出现。yarn-client模式执行时倒是正常的。原来在$SPARK_HOME/bin/spark-class文件里已经设置了持久代大小:

JAVA_OPTS="-XX:MaxPermSize=256m $OUR_JAVA_OPTS"

当以yarn-client模式执行时。driver就执行在客户端的spark-submit进程中。其JVM參数是取的spark-class文件里的设置,所谓未出现持久代溢出现象。

总结一下Spark中各个角色的JVM參数设置:

(1)Driver的JVM參数:
-Xmx。-Xms,假设是yarn-client模式,则默认读取spark-env文件里的SPARK_DRIVER_MEMORY值,-Xmx,-Xms值一样大小;假设是yarn-cluster模式。则读取的是spark-default.conf文件里的spark.driver.extraJavaOptions相应的JVM參数值。

PermSize,假设是yarn-client模式,则是默认读取spark-class文件里的JAVA_OPTS="-XX:MaxPermSize=256m $OUR_JAVA_OPTS"值;假设是yarn-cluster模式。读取的是spark-default.conf文件里的spark.driver.extraJavaOptions相应的JVM參数值。
GC方式,假设是yarn-client模式,默认读取的是spark-class文件里的JAVA_OPTS。假设是yarn-cluster模式,则读取的是spark-default.conf文件里的spark.driver.extraJavaOptions相应的參数值。
以上值最后均可被spark-submit工具中的--driver-java-options參数覆盖。

(2)Executor的JVM參数:
-Xmx,-Xms。假设是yarn-client模式,则默认读取spark-env文件里的SPARK_EXECUTOR_MEMORY值,-Xmx。-Xms值一样大小。假设是yarn-cluster模式,则读取的是spark-default.conf文件里的spark.executor.extraJavaOptions相应的JVM參数值。
PermSize。两种模式都是读取的是spark-default.conf文件里的spark.executor.extraJavaOptions相应的JVM參数值。
GC方式。两种模式都是读取的是spark-default.conf文件里的spark.executor.extraJavaOptions相应的JVM參数值。

(3)Executor数目及所占CPU个数
假设是yarn-client模式。Executor数目由spark-env中的SPARK_EXECUTOR_INSTANCES指定,每一个实例的数目由SPARK_EXECUTOR_CORES指定;假设是yarn-cluster模式。Executor的数目由spark-submit工具的--num-executors參数指定,默认是2个实例,而每一个Executor使用的CPU数目由--executor-cores指定,默觉得1核。
每一个Executor执行时的信息能够通过yarn logs命令查看到,类似于例如以下:

14/08/13 18:12:59 INFO org.apache.spark.Logging$class.logInfo(Logging.scala:58): Setting up executor with commands: List($JAVA_HOME/bin/java, -server, -XX:OnOutOfMemoryError='kill %p', -Xms1024m -Xmx1024m , -XX:PermSize=256M -XX:MaxPermSize=256M -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:/tmp/spark_gc.log, -Djava.io.tmpdir=$PWD/tmp, -Dlog4j.configuration=log4j-spark-container.properties, org.apache.spark.executor.CoarseGrainedExecutorBackend, akka.tcp://spark@sparktest1:41606/user/CoarseGrainedScheduler, 1, sparktest2, 3, 1>, <LOG_DIR>/stdout, 2>, <LOG_DIR>/stderr)

当中。akka.tcp://spark@sparktest1:41606/user/CoarseGrainedScheduler表示当前的Executor进程所在节点,后面的1表示Executor编号。sparktest2表示ApplicationMaster的host,接着的3表示当前Executor所占用的CPU数目。

8 序列化异常

在Spark上运行hive语句的时候,出现类似于例如以下的异常:

org.apache.spark.SparkDriverExecutionException: Execution errorat org.apache.spark.scheduler.DAGScheduler.handleTaskCompletion(DAGScheduler.scala:849)at org.apache.spark.scheduler.DAGSchedulerEventProcessActor$$anonfun$receive$2.applyOrElse(DAGScheduler.scala:1231)at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)at akka.actor.ActorCell.invoke(ActorCell.scala:456)at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)at akka.dispatch.Mailbox.run(Mailbox.scala:219)at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.lang.ClassCastException: scala.collection.mutable.HashSet cannot be cast to scala.collection.mutable.BitSetat org.apache.spark.sql.execution.BroadcastNestedLoopJoin$$anonfun$7.apply(joins.scala:336)at org.apache.spark.rdd.RDD$$anonfun$19.apply(RDD.scala:813)at org.apache.spark.rdd.RDD$$anonfun$19.apply(RDD.scala:810)at org.apache.spark.scheduler.JobWaiter.taskSucceeded(JobWaiter.scala:56)at org.apache.spark.scheduler.DAGScheduler.handleTaskCompletion(DAGScheduler.scala:845)

  排查其前后的日志。发现大都是序列化的东西:

14/08/13 11:10:01 INFO org.apache.spark.Logging$class.logInfo(Logging.scala:58): Serialized task 8.0:3 as 20849 bytes in 0 ms
14/08/13 11:10:01 INFO org.apache.spark.Logging$class.logInfo(Logging.scala:58): Finished TID 813 in 25 ms on sparktest0 (progress: 3/200)

  而在spark-default.conf中,事先设置了序列化方式为Kryo:

spark.serializer org.apache.spark.serializer.KryoSerializer

依据异常信息,可见是HashSet转为BitSet类型转换失败。Kryo把松散的HashSet转换为了紧凑的BitSet,把序列化方式凝视掉之后,任务能够正常运行。难道Spark的Kryo序列化做的还不到位?此问题须要进一步跟踪。

9 Executor僵死问题

执行一个Spark任务,发现其执行速度远远慢于执行相同SQL语句的Hive的执行,甚至出现了OOM的错误,最后卡住达几小时!

而且Executor进程在疯狂GC。

截取其一Task的OOM异常信息:

能够看到这是在序列化过程中发生的OOM。

依据节点信息。找到相应的Executor进程,观察其Jstack信息:

Thread 36169: (state = BLOCKED)- java.lang.Long.valueOf(long) @bci=27, line=557 (Compiled frame)- com.esotericsoftware.kryo.serializers.DefaultSerializers$LongSerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=5, line=113 (Compiled frame)- com.esotericsoftware.kryo.serializers.DefaultSerializers$LongSerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=4, line=103 (Compiled frame)- com.esotericsoftware.kryo.Kryo.readClassAndObject(com.esotericsoftware.kryo.io.Input) @bci=158, line=732 (Compiled frame)- com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=158, line=338 (Compiled frame)- com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=4, line=293 (Compiled frame)- com.esotericsoftware.kryo.Kryo.readObject(com.esotericsoftware.kryo.io.Input, java.lang.Class, com.esotericsoftware.kryo.Serializer) @bci=136, line=651 (Compiled frame)- com.esotericsoftware.kryo.serializers.FieldSerializer$ObjectField.read(com.esotericsoftware.kryo.io.Input, java.lang.Object) @bci=143, line=605 (Compiled frame)- com.esotericsoftware.kryo.serializers.FieldSerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=44, line=221 (Compiled frame)- com.esotericsoftware.kryo.Kryo.readObject(com.esotericsoftware.kryo.io.Input, java.lang.Class, com.esotericsoftware.kryo.Serializer) @bci=136, line=651 (Compiled frame)- com.esotericsoftware.kryo.serializers.FieldSerializer$ObjectField.read(com.esotericsoftware.kryo.io.Input, java.lang.Object) @bci=143, line=605 (Compiled frame)- com.esotericsoftware.kryo.serializers.FieldSerializer.read(com.esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input, java.lang.Class) @bci=44, line=221 (Compiled frame)- com.esotericsoftware.kryo.Kryo.readClassAndObject(com.esotericsoftware.kryo.io.Input) @bci=158, line=732 (Compiled frame)- org.apache.spark.serializer.KryoDeserializationStream.readObject(scala.reflect.ClassTag) @bci=8, line=118 (Compiled frame)- org.apache.spark.serializer.DeserializationStream$$anon$1.getNext() @bci=10, line=125 (Compiled frame)- org.apache.spark.util.NextIterator.hasNext() @bci=16, line=71 (Compiled frame)- org.apache.spark.storage.BlockManager$LazyProxyIterator$1.hasNext() @bci=4, line=1031 (Compiled frame)- scala.collection.Iterator$$anon$13.hasNext() @bci=4, line=371 (Compiled frame)- org.apache.spark.util.CompletionIterator.hasNext() @bci=4, line=30 (Compiled frame)- org.apache.spark.InterruptibleIterator.hasNext() @bci=22, line=39 (Compiled frame)- scala.collection.Iterator$$anon$11.hasNext() @bci=4, line=327 (Compiled frame)- org.apache.spark.sql.execution.HashJoin$$anonfun$execute$1.apply(scala.collection.Iterator, scala.collection.Iterator) @bci=14, line=77 (Compiled frame)- org.apache.spark.sql.execution.HashJoin$$anonfun$execute$1.apply(java.lang.Object, java.lang.Object) @bci=9, line=71 (Interpreted frame)- org.apache.spark.rdd.ZippedPartitionsRDD2.compute(org.apache.spark.Partition, org.apache.spark.TaskContext) @bci=48, line=87 (Interpreted frame)- org.apache.spark.rdd.RDD.computeOrReadCheckpoint(org.apache.spark.Partition, org.apache.spark.TaskContext) @bci=26, line=262 (Interpreted frame)

有大量的BLOCKED线程,继续观察GC信息。发现大量的FULL GC。

分析。在插入Hive表的时候,实际上须要写HDFS,在此过程的HashJoin时,伴随着大量的Shuffle写操作。JVM的新生代不断GC,Eden Space写满了就往Survivor Space写,同一时候超过一定大小的数据会直接写到老生代。当新生代写满了之后,也会把老的数据搞到老生代。假设老生代空间不足了。就触发FULL GC,还是空间不够,那就OOM错误了,此时线程被Blocked。导致整个Executor处理数据的进程被卡住。

当处理大数据的时候,假设JVM配置不当就easy引起上述问题。解决办法就是增大Executor的使用内存。合理配置新生代和老生代的大小,能够将老生代的空间适当的调大点。

10 小节

问题是比較严重,Application都直接无法执行了。可是引起问题的解决办法都比較小,归根结底还是部署的时候环境较为复杂,不够细致!再接再砺!

以后遇到相关的问题,会再这里持续更新,方便自己,也方便遇到类似问题的朋友们!

-------------------------------------------------------------------------------

假设您看了本篇博客,认为对您有所收获。请点击下方的 [顶]

假设您想转载本博客。请注明出处

假设您对本文有意见或者建议,欢迎留言

感谢您的阅读。请关注我的兴许博客

posted on 2017-06-08 12:52 mthoutai 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/mthoutai/p/6962242.html

Spark on Yarn遇到的几个问题相关推荐

  1. 2021年大数据Spark(九):Spark On Yarn两种模式总结

    目录 Spark On Yarn两种模式 引入 一.当一个MR应用提交运行到Hadoop YARN上时 二.当一个Spark应用提交运行在集群上时 注意 client 模式 cluster 模式 总结 ...

  2. Apache Spark源码走读之8 -- Spark on Yarn

    欢迎转载,转载请注明出处,徽沪一郎. 概要 Hadoop2中的Yarn是一个分布式计算资源的管理平台,由于其有极好的模型抽象,非常有可能成为分布式计算资源管理的事实标准.其主要职责将是分布式计算集群的 ...

  3. spark on yarn 完全分布式_Spark编程笔记(1)-架构基础与运行原理

    引言 根据IBM前首席执行官郭士纳的观点,IT领域每隔十五年就会迎来一 次重大变革 .当前我们正处于第三次信息浪潮(2010年前后),物联网.云计算和大数据技术突飞猛进. 信息爆炸是我们当前所需要解决 ...

  4. Spark通过YARN提交任务不成功(包含YARN cluster和YARN client)

    无论用YARN cluster和YARN client来跑,均会出现如下问题. [spark@master spark-1.6.1-bin-hadoop2.6]$ jps 2049 NameNode ...

  5. Running Spark on YARN

    Running Spark on YARN 对 YARN (Hadoop NextGen) 的支持是从Spark-0.6.0开始的,后续的版本也一直持续在改进. Launching Spark on ...

  6. spark on yarn :state: ACCEPTED一直 出现

    今天运行spark on yarn 一直出现 16/09/20 18:40:41 INFO yarn.Client: Application report for application_147417 ...

  7. Spark On YARN 集群安装部署

    2019独角兽企业重金招聘Python工程师标准>>> 最近毕设需要用到 Spark 集群,所以就记录下了部署的过程.我们知道 Spark 官方提供了三种集群部署方案: Standa ...

  8. spark on yarn 完全分部署_大数据Spark面试题(一)

    1.spark的有几种部署模式,每种模式特点?(☆☆☆☆☆) 1)本地模式 Spark不一定非要跑在hadoop集群,可以在本地,起多个线程的方式来指定.将Spark应用以多线程的方式直接运行在本地, ...

  9. Spark on Yarn 模式编写workcount实例

    Spark on Yarn 模式编写workcount实例 一:上传输入文件到hadoop,本例上传的文件名为spark.txt 二:打包程序,打包名为:Spark-0.0.1-SNAPSHOT.ja ...

  10. Spark on Yarn集群多Application并行执行

    在工作中遇到向Spark集群提交多个任务,并且这些任务是需要同时执行的.但是遇到很多错误,所以把遇到的问题记录下来. 修改hadoop/etc/hadoop/yarn-site.xml文件 需要添加的 ...

最新文章

  1. 带left join 的sql的执行顺序
  2. 第三次学JAVA再学不好就吃翔(part26)--static关键字
  3. EntityFramework实体默认值遇到Oracle自增主键
  4. 拦截游戏窗口被移动_熊孩子骗家长人脸识别? 腾讯游戏出了个新招
  5. 为什么t6显示登录不到服务器,t6客户端登录不到服务器
  6. 数据结构笔记(十三)-- 串的堆分配存储表示
  7. 类加载常见错误总结,写得非常好!
  8. DataGridView中如何在textbox列中限制输入。
  9. Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
  10. mysql 中 where条件的OR 和 and 加括号的说法
  11. SAAS PASS IASS 理解
  12. 仿9GAG制作过程(四)
  13. 三级指标 主成分分析_一文看懂主成分分析(PCA)
  14. 包含负数的二进制补码的加减运算
  15. Android 利用重力感应调整手机模式
  16. Realtek 1296 (RTD1296) OpenWRT Android 双系统全功能开发板
  17. 哪些是GraphPad Prism 的当前版本?如何更新或升级?
  18. LeetCode久不久来一题系列之Add Two Numbers
  19. nodejs addon
  20. MGC TOKEN—必将超越PlusToken的搬砖套利项目!

热门文章

  1. 服务器关机显示正在停止服务,云服务器一直停止中
  2. 【汇编语言】程序格式
  3. Linux下通过HostName访问主机以及修改HostName方法
  4. Linux开发sudo apt-cache search检索包
  5. (三)java版spring cloud+spring boot 社交电子商务平台 - Spring Cloud集成项目简介
  6. centos下docker无法正常启动检查与解决方法
  7. javaScript与MVC
  8. PHP提示Notice: Undefined variable的解决办法
  9. [转]测试的三重境界
  10. gcc -nostartfiles; -nodefaultlibs; -nostdlib;-f...