基于Skywalking实现全链路追踪的万字攻略(有手就能操作版) -

标签: | 发表时间:2023-12-13 14:47 | 作者:
出处:https://dbaplus.cn
本文将重点介绍基于Skywalking的全链路实现,包括Skywalking的整体架构和基本概念原理、Skywalking环境部署、SpringBoot和Python集成Skywalking监控实现等。

 

一、Skywalking基本介绍

 

 1.Skywalking整体架构

 

图片

Skywalking 8.x版本架构图

 

SkyWalking整体架构在逻辑上分为四部分:探针Agent、平台后端OAP、存储和UI界面。

 

  • 探针Agent:负责从应用中收集链路信息,并发送给SkyWalking的OAP服务器。它会收集Tracing和Metrics数据,将数据格式化为SkyWalking适用的格式。探针安装在服务所在的服务器上,以方便数据的获取。

  • 平台后端OAP:接收探针发送的数据,并在内存中使用分析引擎进行数据的整合运算,然后将数据存储到对应的存储介质上。OAP支持数据聚合、数据分析以及驱动数据流从探针到用户界面的流程。分析包括Skywalking原生链路跟踪和性能指标以及第三方来源,包括Istio及 Envoy telemetry、Zipkin链路跟踪格式化等。

  • 存储:通过开放的插件化的接口存放SkyWalking数据,目前支持的存储器有Elasticsearch、MySQL、ShardingSphere、TiDB、H2等。

  • 用户界面UI:负责提供控制台,查看链路、服务指标等。UI是一个基于接口高度定制化的Web系统,用户可以可视化查看和管理SkyWalking数据。

 

 2.Skywalking基本概念

 

1)Skywalking中的核心概念

 

与Prometheus不同,SkyWalking的度量机制是围绕以下具有层次结构的核心概念构建的:

 

  • 层(Layer):表示计算机科学中的一个抽象框架,如 Operating System(OS_LINUX 层)、Kubernetes(k8s层)。该层将是从不同技术检测到的不同服务的所有者。

  • 服务:表示一组或一组工作负载,它为传入请求提供相同的行为。

  • 服务实例(Service Instance):服务组中的单个工作负载。

  • 端点(Endpoint):传入请求的服务路径。

  • 进程:操作系统进程。在某些场景下,service instance不是一个进程,比如一个 Kubernetes Pod可能包含多个进程。

 

2)Skywalking中的指标流

 

Metric名称和属性(标签)由SkyWalking OAP服务器根据数据源以及OAL和MAL配置。SkyWalking 提供了对时间序列指标进行下采样(down-sampling),并生成不同时间段数据(分钟、小时、天)的能力。SkyWalking指标流如下:

 

图片

 

二、Skywalking原理解析

 

 1.Skywalking中Trace实现


Skywalking中实现了OpenTracing中的Trace、Span、Tags、Logs等核心概念,不同之处是在Trace级别和Span级别之间加了一个Segment概念,用于表示一个服务实例内的Span集合。

 

1)Trace ID

 

在Skywalking中,全局ID由三个 long 类型的字段(part1、part2、part3)构成,分别记录了ServiceInstanceId、Thread ID和Context生成序列,格式如下所示:

 

  •  
  ${ServiceInstanceId}.${Thread ID}.(${时间戳} * 10000 + 线程自增序列([0, 9999]))

 

2) TraceSegment

 

在SkyWalking中,TraceSegment是一个介于Trace与Span之间的概念,它是一条Trace的一段,可以包含多个Span。在微服务架构中,一个请求基本都会涉及跨进程(以及跨线程)的操作,例如,RPC调用、通过MQ异步执行、HTTP请求远端资源等,处理一个请求就需要涉及到多个服务的多个线程。TraceSegment记录了一个请求在一个线程中的执行流程(即Trace信息)。将该请求关联的TraceSegment串联起来,就能得到该请求对应的完整Trace。

 

图片

 

TraceSegment的核心结构如图所示,包括以下核心字段:

 

  • traceSegmentId(ID类型):TraceSegment的全局唯一标识

  • refs(List<TraceSegmentRef> 类型):它指向父TraceSegment

  • relatedGlobalTraces(DistributedTraceIds类型):记录当前TraceSegment所属Trace的Trace ID。

  • spans(List<AbstractTracingSpan>类型):当前TraceSegment包含的所有Span。

  • ignore(boolean类型):ignore字段表示当前TraceSegment是否被忽略。

 

3)Context

 

SkyWalking中的每个TraceSegment都与一个Context上下文对象一对一绑定,Context上下文不仅记录了TraceSegment的上下文信息,还提供了管理TraceSegment生命周期、创建Span以及跨进程(跨线程)传播相关的功能。

 

 2.Trace的收集和发送

 

1)Context的生成与采样

 

应用访问时,如果不做任何限制,每个请求都会生成一条完整的Trace。在面对海量的业务请求时会同步产生海量的Trace数据,对网络和存储都带来巨大的压力,因此几乎所有的Trace系统都支持采样功能。在Skywalking Agent中是通过SamplingService服务实现的,SamplingService的trySampling()方法递增samplingFactorHolder字段,当增加到阈值(默认值为3,可以通过agent.sample_n_per_3_secs配置进行修改)时会返回false,表示采样失,这时就会生成IgnoredTracerContext,IgnoredTracerContext是个空Context实现,不会记录Trace信息。

 

图片

 

2)Trace的收集

 

当TracingContext通过stopSpan()方法关闭最后一个Span时,会调用finish()方法关闭相应的TraceSegment,与此同时,还会通知所有监听TracingContext关闭事件的监听器TracingContextListener。TraceSegmentServiceClient主要功能就是在TraceSegment结束时对其进行收集,并发送到后端的OAP集群。

 

图片

 

 3.Skywalking OAP内核架构

 

Skywalking OAP采用微内核架构,使用ModuleManager(组件管理器)管理多个Module(组件),一个Module可以对应多个ModuleProvider(组件服务提供者),ModuleProvider是Module底层真正的实现。

 

 

在OAP服务启动时,一个Module只能选择使用一个ModuleProvider对外提供服务。一个ModuleProvider可能支撑了一个非常复杂的大功能,在一个ModuleProvider中,可以包含多个Service,一个Service实现了一个ModuleProvider中的一部分功能,通过将多个Service进行组装集成,可以得到ModuleProvider的完整功能。

 

三、Skywalking环境部署

 

Skywalking测试demo环境如下所示,分别测试SpringBoot应用和Python程序的监控实现。

 

图片

 

 1.解压安装包,使用9.3.0版本

 

  •  
  •  
  # tar -xzvf apache-skywalking-apm-9.3.0.tar.gz  # mv apache-skywalking-apm-bin/ /usr/local/skywalking

 

 2.修改OAP配置文件,指定存储类型为MySQL

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  # vi config/application.yml  cluster:    selector: ${SW_CLUSTER:standalone}  storage:    selector: ${SW_STORAGE:mysql}    mysql:      properties:        jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://192.168.112.121:3306/swtest?rewriteBatchedStatements=true&allowMultiQueries=true"}        dataSource.user: ${SW_DATA_SOURCE_USER:root}        dataSource.password: ${SW_DATA_SOURCE_PASSWORD:123456}        dataSource.cachePrepStmts: ${SW_DATA_SOURCE_CACHE_PREP_STMTS:true}        dataSource.prepStmtCacheSize: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_SIZE:250}        dataSource.prepStmtCacheSqlLimit: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_LIMIT:2048}        dataSource.useServerPrepStmts: ${SW_DATA_SOURCE_USE_SERVER_PREP_STMTS:true}      metadataQueryMaxSize: ${SW_STORAGE_MYSQL_QUERY_MAX_SIZE:5000}      maxSizeOfBatchSql: ${SW_STORAGE_MAX_SIZE_OF_BATCH_SQL:2000}      asyncBatchPersistentPoolSize: ${SW_STORAGE_ASYNC_BATCH_PERSISTENT_POOL_SIZE:4}

 

 3.对Webapp进行配置

 

  •  
  •  
  •  
  •  
  # vi webapp/application.yml  serverPort: ${SW_SERVER_PORT:-18080}  # Comma seperated list of OAP addresses.  oapServices: ${SW_OAP_ADDRESS:-http://192.168.112.121:12800}

 

默认使用8080端口访问,修改为18080。

 

 4.下载MySQL连接jar并拷贝到oap-libs

 

  •  
  •  
  •  
  #下载链接:mysql-connector-java-8.0.28.jar  https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.28/mysql-connector-java-8.0.28.jar  # cp mysql-connector-java-8.0.28.jar /usr/local/skywalking/oap-libs/

 

将jar包放在oap-libs目录下即可,如果没有连接jar包,会抛如下异常:

 

  •  
  java.lang.RuntimeException: Failed to get driver instance for jdbcUrl=jdbc:mysql://localhost:3306/swtest?rewriteBatchedStatements=true&allowMultiQueries=true

 

 5.连接mysql创建应用库swtest

 

  •  
  •  
  # mysql -uroot -p  mysql> create database swtest;

 

在mysql中创建配置文件中的应用库swtest,否则会提示报错。

 

  •  
  com.zaxxer.hikari.pool.HikariPool - 574 [main] ERROR [] - HikariPool-1 - Exception during pool initialization.java.sql.SQLSyntaxErrorException: Unknown database 'swtest'

 

 6.启动Skywalking服务

 

进入bin目录执行startup.sh文件即可启动SkyWalking平台。

 

  •  
  •  
  cd ../bin  ./startup.sh

 

启动成功看log输出日志:

 

  •  
  •  
  2023-11-04 16:06:18,455 - com.linecorp.armeria.common.util.SystemInfo - 237 [main] INFO  [] - hostname: tango-db01 (from /proc/sys/kernel/hostname)  2023-11-04 16:06:20,036 - com.linecorp.armeria.server.Server - 797 [armeria-boss-http-*:12800] INFO  [] - Serving HTTP at /0:0:0:0:0:0:0:0%0:12800 - http://127.0.0.1:12800/

 

再查看swtest库中,已经创建了很多表:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  mysql> show tables;  +--------------------------------------------------------+  | Tables_in_swtest                                       |  +--------------------------------------------------------+  | alarm_record                                           |  | alarm_record_tag                                       |  | browser_app_error_rate                                 |

 

 7.访问UI页面,端口为18080:http://192.168.112.121:18080/

 

图片

 

四、Skywalking全链路监控实现

 

 1.SpringBoot应用集成Skywalking监控

 

1)环境准备

 

在mysql中创建表并插入数据:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  mysql> use sw_mysql;  Database changed  mysql> CREATE TABLE `sw_tb` (       -> `id` int(11) NOT NULL AUTO_INCREMENT,       -> `username` varchar(50) DEFAULT NULL,       -> `password` varchar(50) DEFAULT NULL,      -> PRIMARY KEY (`id`)       -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8      -> ;  Query OK, 0 rows affected, 2 warnings (0.03 sec)  mysql> insert into sw_mysql.sw_tb(`username`,`password`) values('张三','AAA'),('李四','BBB'),('王五','CCC');  Query OK, 3 rows affected (0.00 sec)  Records: 3  Duplicates: 0  Warnings: 0  mysql> select * from sw_mysql.sw_tb;  +----+----------+----------+  | id | username | password |  +----+----------+----------+  |  1 | 张三     | AAA      |  |  2 | 李四     | BBB      |  |  3 | 王五     | CCC      |  +----+----------+----------+  3 rows in set (0.00 sec)

 

2)在Eclipse中创建SpringBoot项目

 

在pom.xml文件中添加引入Skywalking的依赖包:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
    <dependency>              <groupId>org.apache.skywalking</groupId>              <artifactId>apm-toolkit-trace</artifactId>              <version>8.3.0</version>          </dependency>          <dependency>              <groupId>org.apache.skywalking</groupId>              <artifactId>apm-toolkit-log4j-2.x</artifactId>              <version>8.3.0</version>          </dependency>    <dependency> <!-- 引入log4j2依赖 -->        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-log4j2</artifactId>    </dependency>

 

配置trace:

 

使用apm-toolkit-trace输出traceid信息,并修改log4j2.xml配置日志格式。这样会将traceid信息写入日志,用于后续的日志采集和集中分析。

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
      <Properties>          <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符 %logger{36} 表示 Logger 名字最长36个字符-->          <!--1.文件输出格式-->          <property name="file_pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%traceId] [%thread] [%-5level]  %msg %l%n" />          <!--2.控制台显示日志格式-->          <!--[%traceId]:追踪id-->          <!--[%sw_ctx]:打印为[$serviceName,$instanceName,$traceId,$traceSegmentId,$spanId]:服务名,实例名,追踪id,追踪片段id,跨度id-->          <property name="console_pattern" value="%red{%d{yyyy-MM-dd HH:mm:ss}} [%traceId] %green{[%thread]} %magenta{[%-5level]} %cyan{%msg} %l%n"/>          <!--3.skyWalking收集格式-->          <property name="skyWalking_pattern" value="%msg %l%n"/>          <!-- 定义日志存储的路径 -->          <property name="FILE_PATH" value="./log/spring-skywalking/" />          <property name="FILE_NAME" value="spring-skywalking" />      </Properties>

 

控制程序如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  package com.tango.skywalking_mysql.controller;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.jdbc.core.BeanPropertyRowMapper;  import org.springframework.jdbc.core.JdbcTemplate;  import org.springframework.web.bind.annotation.GetMapping;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.RestController;  import com.tango.skywalking_mysql.SkywalkingMysqlApplication;  import org.apache.logging.log4j.LogManager;    import org.apache.logging.log4j.Logger;    @RestController  @RequestMapping("/demo")  public class DemoController {      private static final Logger logger = LogManager.getLogger(SkywalkingMysqlApplication.class);       @Autowired      private JdbcTemplate template;      @GetMapping("/mysql")      public String mysql() {        String result="";        try {          this.selectById(1);          System.out.println("skywalking-test!");          logger.info("skywalking-test!");          result="MySQL查询正常";        } catch(Exception e) {          System.out.println(e);          logger.error(e);          result="MySQL查询异常";        }                  return result;      }      public Object selectById(Integer id) {          return template.queryForObject("SELECT id, username, password FROM sw_tb WHERE id = ?",new BeanPropertyRowMapper<>(Object.class), id);      }  }

 

在IDE配置中添加如下选项,配置Skywalking agent和服务的地址。

 

  •  
  •  
  •  
  -javaagent:D:\Skywalking-demo\skywalking-agent\skywalking-agent.jar  -Dskywalking.agent.service_name=skywalking-demo-service  -Dskywalking.collector.backend_service=192.168.112.121:11800

 

图片

 

3)运行SpringBoot服务程序

 

运行应用后输出以下信息,表示agent启动成功。

 

  •  
  •  
  •  
  INFO 2023-11-04 17:11:44.918 main SkyWalkingAgent : Skywalking agent begin to install transformer ...   Starting application skywalking_mysql  [31m2023-11-04 17:11:52[m [TID: N/A] [32m[main][m [35m[INFO ][m [36mStarting application skywalking_mysql[m com.tango.skywalking_mysql.SkywalkingMysqlApplication.main(SkywalkingMysqlApplication.java:13)

 

同时能够查看到日志中的traceid信息:

 

  •  
  •  
  •  
  •  
  [31m2023-11-04 17:17:43[m [TID: e6978740bf3e41bfa6a53760e2d64b8a.44.16988302617370001] [32m[http-nio-18079-exec-1][m [35m[INFO ][m [36mskywalking-test![m com.tango.skywalking_mysql.controller.DemoController.mysql(DemoController.java:29)  skywalking-test!  [31m2023-11-04 17:20:39[m [TID: e6978740bf3e41bfa6a53760e2d64b8a.47.16988304392400001] [32m[http-nio-18079-exec-4][m [35m[INFO ][m [36mskywalking-test![m com.tango.skywalking_mysql.controller.DemoController.mysql(DemoController.java:29)  skywalking-test!

 

查看服务端的11800端口,已经有服务。

 

  •  
  •  
  •  
  [root@tango-DB01 config]# netstat -an|grep 11800  tcp6       0      0 :::11800                :::*                    LISTEN       tcp6       0      0 192.168.112.121:11800   192.168.112.1:49590     ESTABLISHED

 

访问SpringBoot应用服务:

 

每查询一次发起一笔业务访问:http://192.168.112.1:18079/demo/mysql

 

4)登录Skywalking监控服务运行情况

 

在Skywalking界面看到新的Service:skywalking-demo-service

 

图片

 

查看服务的运行性能指标情况:

 

图片

 

查看服务的拓扑结构,这是一个访问mysql数据库的应用。

 

图片

 

查看trace信息:

 

图片

 

查看具体的SQL语句执行情况:

 

图片

 

 2.Python应用集成Skywalking监控

 

1)Python程序中Agent配置

 

在Python程序中引入Skywalking Agent:

 

  •  
  •  
  •  
  •  
  •  
  from skywalking import agent,config  #配置OAP服务信息  config.init(agent_collector_backend_services='192.168.112.121:11800', agent_name='skywalking-demo-python')  agent.start()

 

2)运行Python程序,在Skywalking监控服务运行情况

 

拓扑图如下所示,包括服务和mysql数据库:

 

图片

 

查看Trace信息:

 

图片

 

查看具体执行的SQL信息:

 

图片

 

3)代码实现

 

完整代码实现如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  # -*- coding: utf-8 -*-  import pymysql  import sys  import time  import codecs  import logging  import base64  from skywalking import agent,config   # 配置logging模块    logging.basicConfig(filename='test.log', level=logging.INFO)    def getInfo(sql):    ip="192.168.112.121"    port=3306    user="root"    pwd=base64.decodebytes(b"MTIzNDU2Cg==").strip().decode('utf-8')    dbname="sw_mysql"    info = []    conn = pymysql.connect(host=ip,port=port,user=user,passwd=pwd,database=dbname,charset='utf8')    cursor = conn.cursor()    try:      cursor.execute(sql)      info = cursor.fetchall()    except Exception as e:      print(e)    conn.commit()    cursor.close()    conn.close()    return info  if __name__ == '__main__':      if sys.version[0] == "2":          reload(sys)          sys.setdefaultcoding("utf8")            config.init(agent_collector_backend_services='192.168.112.121:11800', agent_name='skywalking-demo-python')      agent.start()            exec_sql = "select id,username,password from sw_mysql.sw_tb"            while True:          get_info = getInfo(exec_sql)                    if len(get_info) > 0:              print(get_info[0])              print("Success!")          else:              print("Error!")                  time.sleep(5)      agent.stop()

 

以上是基于Skywalking的全链路跟踪的简单指标采集实现。Skywalking功能强大,还具备拓扑关联分析、分布式跟踪和上下文传播、告警等功能,值得深入研究。

 

>>>> 

参考资料

 

  • https://github.com/apache/skywalking

  • https://github.com/SkyAPM/document-cn-translation-of-skywalking

  • https://skywalking.apache.org/docs/skywalking-python

  • https://blog.csdn.net/weixin_42073629/article/details/106775584

 

相关 [skywalking 万字 攻略] 推荐:

基于Skywalking实现全链路追踪的万字攻略(有手就能操作版) -

- -
本文将重点介绍基于Skywalking的全链路实现,包括Skywalking的整体架构和基本概念原理、Skywalking环境部署、SpringBoot和Python集成Skywalking监控实现等. 一、Skywalking基本介绍.  1.Skywalking整体架构  . Skywalking 8.x版本架构图.

谈SkyWalking进行服务链监控(12.27)

- - 人月神话的BLOG
在谈Nacos服务注册的时候,刚好看到了Skywalking分布式追踪系统,而这个本身也完全可以用于微服务架构里面的服务链监控上面. 对于APM应用性能监控工具有很多,常说的主要是类似Zipkin, Pinpoint, Cat等,而Skywalking是国产的一款开源APM工具软件,包括了包括了分布式追踪、性能指标分析、应用和服务依赖分析等.

SkyWalking探针在 k8s 中集成

- - 掘金 后端
最近公司需要在 k8s 环境接入 SkyWalking,要让应用无感知接入. 开始打算的是把agent文件放到基础镜像中,这样应用只需要引用包含agent的基础镜像即可. 但是这样会有几个问题,首先不好管理agent,升级需要应用重新打镜像部署,动静太大. 第二,不是所有应用都需要接入,要按需引入不同基础镜像,这样就多个一个步骤,应用会有感知.

使用 docker 部署 spring boot 并接入 skywalking

- - SegmentFault 最新的文章
最近在研究skywalking,打算使用k8s部署 skywalking 并将 pod 中的应用接入 skywalking 进行服务链路追踪. 这篇文章先不介绍 skywalking 在k8s中的部署和使用,而是先介绍如何使用手动和docker的方式使用 skywalking. 在整个实践过程中查阅了大量文档,遇到了各种问题,这里将我自己的实践过程记录下来,希望对有同样需求的小伙伴提供一些帮助.

k8s 部署 skywalking 并将 pod 应用接入链路追踪

- - SegmentFault 最新的文章
前面写了两篇文章介绍使用 docker 部署 spring boot 和 tomcat 项目,并将其接入skywalking,这篇文章主要介绍使用 k8s 部署 skywalking 并将 pod 应用接入链路追踪. 二、使用 helm 部署 skywalking. 在 k8s 中使用 helm 的前提是需要先安装 helm 客户端,关于 helm 的安装可以查看官方文档.

我的 Google 攻略

- baixian - 電腦玩物
我上網絕大多數的時間,可能都和Google有關. 別的不說,電腦玩物使用的部落格平台就是Google的Blogger;我的資訊來源是Google Reader;用的郵件系統是Gmail;行事曆使用Google日曆;聽音樂看影片最喜歡去YouTube;平常照片整理使用Picasa;出門旅遊當然要查查Google地圖;工作上還會跟夥伴透過Google文件協同作業;而遇到任何疑難雜症,當然一定會上去讓我們變笨的Google搜尋看看.

我的 Firefox 攻略

- Yuli - 電腦玩物
明年初要推出的Firefox 4,依據我最近測試Beta版的體驗,應該會是一款延續火狐精神又帶有極大創新意義的代表作. 從革新的角度來看,Firefox 4在速度上大幅飛躍到足以與Google Chrome、Opera比肩,網址列結合狀態列資訊讓操作視點更集中,分頁群組管理只要再優化一下效能就可以變成實用功能.

阳台种菜全攻略

- berryman - Lzhi&#39;s Views
阳台种菜全攻略一:阳台种什么菜?. ”花种得好,可以欣赏;菜种得好,不仅吃上自己种的无公害蔬菜,翠枝绿叶和硕果累累,也可以成为一道美景. 可是我们生活在钢筋水泥丛林中的城里人没有大片的土地,怎么种菜呢. 没关系,小小阳台其实也是种菜的好地方. 下面就为您一一介绍阳台种菜的知识、方法和窍门,首先介绍阳台适合种什么蔬菜.

北京地铁攻略

- afanso - 《Geek》官方Blog
据说7·23事故以后,全国各交通部门可谓草木皆兵,比如北京方面就声称要严格把好安全生产关,严禁地铁超载……霎那间,北京众多无辜的上班族们悲从中来,这NM还让我们怎么上班. 开车堵死你,坐地铁等死你,坐公交挤死你,骑车撞死你……鉴于严峻的形势,估计北京方面会对“超载”一词灵活运用,甚至重新定义的. 言归正传,首先还是让咱们来看看北京地铁一族的早高峰攻略吧.

【转载】我的 Google 攻略

- helen - Page to Page
我上网绝大多数的时间,可能都和Google有关. 别的不说,电脑玩物使用的部落格平台就是Google的Blogger;我的信息来源是Google Reader;用的邮件系统是Gmail;待办事项使用Google日历;听音乐看影片最喜欢去YouTube;平常照片整理使用Picasa;出门旅游当然要查查Google地图;工作上还会跟伙伴透过Google文件协同作业;而遇到任何疑难杂症,当然一定会上去让我们变笨的Google搜索看看.