ITPub博客

首页 > 自动化运维 > DevOps > docker笔记23-pod的生命周期

docker笔记23-pod的生命周期

原创 DevOps 作者:czxin788 时间:2018-09-21 15:01:35 0 删除 编辑

 查看资源配置清单帮助 

 [root@master manifests]# kubectl explain  pods.spec.containers #查看帮助

  资源配置清单分类

        自主式pod资源:

        资源的清单格式:

            一级字段:apiVersion(group/version组成),kind,metadata(包括name,namespace,labels,annotations),spec ,status。

  pod资源-spec.containers

    - name:pod名字

       image:镜像名字

       imagePullPolicy:表示从哪拉镜像,Always(不管本地有没有镜像,都要从仓库中下载镜像,也就是说,即使本地有镜像了,也不使用本地镜像,而是从仓库下载), Never(从来不从仓库下载镜像,也就是说本地有镜像就用,没有就算了), IfNotPresent(如果本地存在就直接使用,不存在才从仓库下载)。 默认的策略是:当镜像标签版本是latest,默认策略就是Always;如果指定特定版本默认拉取策略就是IfNotPresent。

                 

    如上图,指定ImagePullPolicy策略为ifNotPresent后,即使image指定的版本未latest,这样每次启动容器,也不会从仓库重新下载镜像了。

    ports:指定暴露容器端口号,可以指定多个端口,如下:

[root@master manifests]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: tomcat 
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent #指定ImagePullPolicy策略为ifNotPresent后,即使image指定的版本未latest,以后每次启动容器,也不会从仓库重新下载镜像了
    command:
    - "/bin/sh"
    - "-c"
    - "echo $(date) >> /usr/share/nginx/html/index.html; sleep 5"
   #以上命令也可以写作:command: ["/bin/sh","-c","sleep 3600"]

    args:相当于dockerfile里面的cmd

    command:相当于docker里面的Entrypoint

    如果既没有指定args,也没有指定command,那么会默认使用dockfile的cmd和entrypoint。

    如果指定了command,但没有提供args,那么就直接运行command。

    如果指定了args,但是没指定command,那么将使用镜像中的entrypoint命令,把我们写的args当做参数传递给镜像中的entrypoint。

    如果既用来command,又用了args,那么镜像中的cmd和entrypoint将被忽略,而使用K8s提供的command with args。

    参考文档:https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/

  标签

    标签是k8s极具特色的管理方式。一个标签可以对应多个资源,一个资源也可以有多个标签,它们是多对多的关系。

    一个资源拥有多个标签,可以实现不同维度的管理。

    标签是key=value格式的,key最大63个字符,只能是字母、数字、_、-、.五种类型的组合。只能以字母或数字开头结尾。

    我们也可以使用标签选择器来指定能使用哪些标签。

    可以使用如下命令看标签:

[root@master manifests]# kubectl get pods --show-labels
NAME                          READY     STATUS             RESTARTS   AGE       LABELS
pod-demo                      1/2       CrashLoopBackOff   24         1h        app=myapp,tier=frontend

   可以用-l来过滤包含app标签的pod

[root@master manifests]# kubectl get pods -l app --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   25         1h        app=myapp,tier=frontend

    给资源对象打标签,我们再给前面创建的pod-demo pod打个release=canary的标签:

[root@master manifests]# kubectl label pods pod-demo release=canary
pod/pod-demo labeled
[root@master manifests]# kubectl get pods -l app --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   27         1h        app=myapp,release=canary,tier=frontend

    修改标签的值:

[root@master manifests]# kubectl label pods pod-demo release=stable --overwrite
pod/pod-demo labeled
[root@master manifests]# kubectl get pods -l app --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   27         1h        app=myapp,release=stable,tier=frontend

    查找既有release标签,又拥有app标签的:

[root@master manifests]# kubectl get pods -l app,release --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   27         2h        app=myapp,release=stable,tier=frontend

    标签选择器分为:等值关系的标签选择器和集合关系的标签选择器。

     等值关系的标签选择器: 可以用=或者==表示等于,!=表示不等于:

[root@master manifests]# kubectl get pods -l release=stable --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   29         2h        app=myapp,release=stable,tier=frontend
[root@master manifests]# kubectl get pods -l release=stable,app=myapp --show-labels
NAME       READY     STATUS             RESTARTS   AGE       LABELS
pod-demo   1/2       CrashLoopBackOff   30         2h        app=myapp,release=stable,tier=frontend
[root@master manifests]# kubectl get pods -l release!=stable --show-labels
NAME                          READY     STATUS             RESTARTS   AGE       LABELS
client                        1/1       Running            0          1d        run=client
myapp-fcc5f7f7c-4x2p7         0/1       ImagePullBackOff   0          1d        pod-template-hash=977193937,run=myapp
myapp-fcc5f7f7c-dnkdq         0/1       ImagePullBackOff   0          1d        pod-template-hash=977193937,run=myapp
mytomcat-5f8c6fdcb-7t5s2      1/1       Running            0          1d        pod-template-hash=194729876,run=mytomcat
mytomcat-5f8c6fdcb-lhcsc      1/1       Running            0          1d        pod-template-hash=194729876,run=mytomcat
mytomcat-5f8c6fdcb-rntrg      1/1       Running            0          1d        pod-template-hash=194729876,run=mytomcat
nginx-deploy-5b595999-fpm8x   1/1       Running            0          1d        pod-template-hash=16151555,release=canary,run=nginx-deploy

    集合关系的标签选择器: key in (value1,value2...),key notin  (value1,value2...),!key

[root@master ~]# kubectl get pods -l "release in (canary,beta,alpha)" --show-labels
NAME                          READY     STATUS    RESTARTS   AGE       LABELS
nginx-deploy-5b595999-fpm8x   1/1       Running   0          1d        pod-template-hash=16151555,release=canary,run=nginx-deploy
[root@master ~]# kubectl get pods -l "release notin (canary,beta,alpha)" --show-labels
NAME                       READY     STATUS             RESTARTS   AGE       LABELS
client                     1/1       Running            0          1d        run=client
myapp-fcc5f7f7c-4x2p7      0/1       ImagePullBackOff   0          1d        pod-template-hash=977193937,run=myapp
myapp-fcc5f7f7c-dnkdq      0/1       ImagePullBackOff   0          1d        pod-template-hash=977193937,run=myapp

    我们知道pod控制器和service都是要通过标签来进行关联,通常使用matchLabels,matchExpressions。许多资源支持内嵌字段定义其使用的标签选择器。

    matchLabels:直接给定键值,相当于使用等值关系一样;

    matchExpressions:基于给定的表达式来定义使用标签选择器,格式为{key!"KEY",operator:"OPERATOR",values:[VAL1,VAL2,....]},常用的操作符有:

    1)In,NotIn,:values字段的值必须为非空列表

    2)Exists,NotExists:values字段的值必须为空列表

    不光pods有标签,nodes等对象都有标签,如下:

[root@master ~]# kubectl get nodes --show-labels
NAME      STATUS    ROLES     AGE       VERSION   LABELS
master    Ready     master    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master=
node1     Ready     <none>    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node1
node2     Ready     <none>    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node2

    上面的标签是内建的标签,其中beta.kubernetes.io这部分是能在dns中解析的,该部分字符长度不能超过253个字符。

    同样,我们也可以给nodes打标签,比如我们下面给node1节点打个disktype=ssd的标签

[root@master ~]# kubectl label nodes node1 disktype=ssd
node/node1 labeled

    

[root@master ~]# kubectl get nodes --show-labels
NAME      STATUS    ROLES     AGE       VERSION   LABELS
master    Ready     master    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master=
node1     Ready     <none>    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node1
node2     Ready     <none>    4d        v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node2

节点选择器-nodeSelector和nodeName 

 nodeSelector:可以使pod运行在指定的node节点上,我们如下举例演示该功能。

[root@master ~]# kubectl get pods -o wide
NAME                          READY     STATUS             RESTARTS   AGE       IP            NODE
client                        1/1       Running            0          2d        10.244.2.4    node2
myapp-fcc5f7f7c-4x2p7         0/1       ImagePullBackOff   0          1d        10.244.1.9    node1
myapp-fcc5f7f7c-dnkdq         0/1       ImagePullBackOff   0          1d        10.244.2.6    node2
mytomcat-5f8c6fdcb-7t5s2      1/1       Running            0          1d        10.244.2.8    node2
mytomcat-5f8c6fdcb-lhcsc      1/1       Running            0          1d        10.244.2.7    node2
mytomcat-5f8c6fdcb-rntrg      1/1       Running            0          1d        10.244.1.10   node1
nginx-deploy-5b595999-fpm8x   1/1       Running            0          1d        10.244.1.7    node1
pod-demo                      1/2       CrashLoopBackOff   303        1d        10.244.2.11   node2

    上面我们看到pod-demo运行在node2节点上,下面我们让它运行在node1节点上。我们刚才在node1上打了个disktype=ssd的标签,所以我们用nodeSelector在资源清单中如下定义:

[root@master manifests]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: tomcat 
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent #指定ImagePullPolicy策略为ifNotPresent后,即使image指定的版本未latest,以后每次启动容器,也不会从仓库重新下载镜像了
    command:
    - "/bin/sh"
    - "-c"
    - "echo $(date) >> /usr/share/nginx/html/index.html; sleep 5"
   #以上命令也可以写作:command: ["/bin/sh","-c","sleep 3600"]
  nodeSelector: #指定该pod运行在有disktype=ssd标签的node节点上
    disktype: ssd


[root@master manifests]# kubectl delete -f pod-demo.yaml 
pod "pod-demo" deleted
[root@master manifests]# kubectl create  -f pod-demo.yaml 
pod/pod-demo created
[root@master manifests]# kubectl get pods -o wide
NAME                          READY     STATUS             RESTARTS   AGE       IP            NODE
client                        1/1       Running            0          2d        10.244.2.4    node2
myapp-fcc5f7f7c-4x2p7         0/1       ImagePullBackOff   0          1d        10.244.1.9    node1
myapp-fcc5f7f7c-dnkdq         0/1       ImagePullBackOff   0          1d        10.244.2.6    node2
mytomcat-5f8c6fdcb-7t5s2      1/1       Running            0          1d        10.244.2.8    node2
mytomcat-5f8c6fdcb-lhcsc      1/1       Running            0          1d        10.244.2.7    node2
mytomcat-5f8c6fdcb-rntrg      1/1       Running            0          1d        10.244.1.10   node1
nginx-deploy-5b595999-fpm8x   1/1       Running            0          1d        10.244.1.7    node1
pod-demo                      2/2       Running            2          37s       10.244.1.14   node1

    上面看到,pod-demo运行在了带有disktype=ssd标签的node1节点上了。

    那么有人可能会问了,我们可不可以指定pod-demo运行在指定node上呢。当然可以了,这个选项就是nodeName。大家可以自定去测试。

资源注解annotations

    与labels的区别是,annotaitons不能用于挑选资源对象,仅用于为对象提供原数据。这些元数据可能被某些程序所用到,而且很重要。annotations的键值对没有字符数限制。

    查看资源的注解:

[root@master manifests]# kubectl delete -f pod-demo.yaml 
pod "pod-demo" deleted
[root@master manifests]# cat cat pod-demo.yaml 
cat: cat: No such file or directory
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: tomcat 
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent #指定ImagePullPolicy策略为ifNotPresent后,即使image指定的版本未latest,以后每次启动容器,也不会从仓库重新下载镜像了
    command:
    - "/bin/sh"
    - "-c"
    - "echo $(date) >> /usr/share/nginx/html/index.html; sleep 5"
   #以上命令也可以写作:command: ["/bin/sh","-c","sleep 3600"]
  nodeSelector: #指定该pod运行在有disktype=ssd标签的node节点上
    disktype: ssd
[root@master manifests]# kubectl create -f pod-demo.yaml 
pod/pod-demo created
[root@master manifests]# kubectl describe pods pod-demo
Annotations:        chenzx.com/created-by=cluster-admin
Status:             Running
IP:                 10.244.1.15


pod的生命周期

    一个容器里面可以运行多个进程,但是通常我们只在容器里面运行一个进程。

    在一个pod中,可以运行多个容器,但是通常我们只在一个pod里面运行一个容器。

    一个容器在创建之前,有多个初始化容器(init container)用来进行初始化环境,init container执行完,它就退出了。接下来是主容器(main container)开始启动,主容器启动时也要初始化主容器里面的环境。在主容器刚刚启动之后,用户可以手动嵌入做一个操作叫post start。在主容器结束前,也可以做一个收尾操作pre stop,用来在主容器结束前做一个清理。

    在post start后,就开始做健康检查,第一个健康检查叫存活状态检查(liveness probe ),用来检查主容器存活状态的;第二个健康检查叫准备就绪检查(readyness probe),用来检查主容器是否启动就绪。

    pod的状态有:

        a) Pending:当启动一个容器时,发现条件不满足,就会进入pending状态;

        b) Runing

        c)Failed

        d)Succeeded

        e) Unknown;如果kubelete出现故障,那么apiserver就连不上kubelete了,就会出现未知的错误

    创建pod的过程:pod创建时先和apiserver沟通,然后把信息存储在etcd中;接下来apiserver会请求scheduler,并把调度的结果也保存在etcd中。假如scheduler把pod调度到node1节点上了,此时node1节点上的kublete会从etcd中拿到用户创建的清单,根据清单在node1上运行这个pod。不管pod在node1上运行成功还是失败,都会把结果反馈给apiserver,同时把运行结果保存在etcd中。

    健康检查分三个层次:1、直接执行命令;2、向tcp连接请求;3、向http发get请求。

    总结:pod生命周期中的重要行为:

        1)初始化容器

        2)容器探测(liveness存活行探测和readness准备就绪探测)

livenessProbe(存活状态探测)

 存活不一定就绪。
[root@master manifests]# kubectl explain pods.spec.containers.livenessProbe

    可以看到有三种探针,exec、 httpGet、tcpSocket

    failureThreshold表示探测失败次数,默认是3探测次失败,才认为是真失败了。

    periodSeconds:周期间隔时长,默认10s探测一次;

    timeoutSeconds:超时时间,表示发出探测,对方始终没有响应,需要等多久,默认等1s

    initialDelaySeconds:默认是容器一启动就开始探测,但是此时容器可能还没启动完呢,所以这时探测肯定是失败的。所以initialDelaySeconds表示容器启动多长时间后才开始探测。

[root@master manifests]# kubectl explain pods.spec.containers.livenessProbe.exec
[root@master manifests]# cat liveness.exec.ymal 
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent #如果存在就不要下载了
    command: ["/bin/sh","-c","touch /tmp/healthy;sleep 60;rm -f /tmp/healthy;sleep 3600"]
    livenessProbe: #存活性探测
      exec: 
        command: ["test","-e","/tmp/healthy"] #-e表示探测文件是否存在
      initialDelaySeconds: 1 #表示容器启动后多长时间开始探测
      periodSeconds: 3 #表示每隔3s钟探测一次
[root@master manifests]# kubectl create -f liveness.exec.ymal 
pod/liveness-exec-pod created
[root@master manifests]# kubectl get pods -w
NAME                          READY     STATUS             RESTARTS   AGE
liveness-exec-pod             1/1       Running            2          4m
[root@master manifests]# kubectl get pods -w
NAME                          READY     STATUS             RESTARTS   AGE
liveness-exec-pod             1/1       Running            4          6m
[root@master manifests]# kubectl get pods -w
NAME                          READY     STATUS             RESTARTS   AGE
client                        1/1       Running            0          3d
liveness-exec-pod             1/1       Running            5          9m

  

[root@master manifests]#  kubectl get pods -w
NAME                          READY     STATUS             RESTARTS   AGE
client                        1/1       Running            0          3d
liveness-exec-pod             1/1       Running            9          23m

    可以看到restart此时在随着时间增长。

    上面的例子是用exec执行命令进行探测的。

    下面我们看看基于tcp和httpget探测的选项。

[root@master manifests]kubectl explain pods.spec.containers.livenessProbe.tcpSocket
[root@master manifests]# kubectl explain pods.spec.containers.livenessProbe.httpGet

    下面举个例子:

[root@master manifests]# cat liveness.httpget.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: nginx:latest
    imagePullPolicy: IfNotPresent #如果存在就不要下载了
    ports:
    - name: http
      containerPort: 80
    livenessProbe: #存活性探测
      httpGet:
        port: http
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3 #表示每隔3s钟探测一次
[root@master manifests]# kubectl  create -f liveness.httpget.yaml
[root@master manifests]# kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
liveness-httpget-pod          1/1       Running            0          4m
[root@master manifests]# kubectl exec -it liveness-httpget-pod  -- /bin/sh
# rm -rf  /usr/share/nginx/html/index.html
[root@master manifests]#  kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
liveness-httpget-pod          1/1       Running            1          27m

    上面可以看到,当删除pod里面的/usr/share/nginx/html/index.html,liveness监测到index.html文件被删除了,所以restarts次数为1,但是只重启一次,不会再重启了。这是因为重启一次后,nginx容器就重新初始化了,里面就会又生成index.html文件。所以里面就会有新的index.html文件了。

readlinessProbe(准备就绪型探针)

[root@master manifests]# cat  readiness-httpget.ymal 
apiVersion: v1
kind: Pod
metadata:
  name: readdliness-httpget-pod
  namespace: default
spec:
  containers:
  - name: readliness-httpget-container
    image: nginx:latest
    imagePullPolicy: IfNotPresent #如果存在就不要下载了
    ports:
    - name: http
      containerPort: 80
    readinessProbe: #准备型探针
      httpGet:
        port: http
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3 #表示每隔3s钟探测一次


[root@master manifests]# kubectl create -f readiness-httpget.ymal 
pod/readdliness-httpget-pod created
[root@master ~]# kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
readdliness-httpget-pod       1/1       Running            0          16h
[root@master ~]# kubectl exec -it readdliness-httpget-pod -- /bin/sh
# rm -rf /usr/share/nginx/html/index.html
[root@master ~]# kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
readdliness-httpget-pod       0/1       Running            0          16h

    上面可以看到,ready变成0/1了,但是status是runing的,这就是说nginx进程是在的,只是index.html不见了,可以判定nginx没有就绪。

postStart(启动后钩子)

[root@master ~]# kubectl  explain pods.spec.containers.lifecycle.postStart

      postStart是指容器在启动之后立即执行的操作,如果执行操作失败了,容器将被终止并且重启。而重启与否是由重启策略。

[root@master manifests]# cat poststart-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: poststart-pod
  namespace: default
spec:
  containers:
  - name: busybox-httpd
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    lifecycle: #生命周期事件
      postStart:
        exec:
          command: ["mkdir", "-p","/data/web/html"] #这个command是定义postStart后的需要执行的命令
    command: ["/bin/sh","-c","sleep 3600"] #这是定义容器里面执行的命令,不过这个命令要先于postStart里面的command
    #args: ["-f","-h /data/web/html"]  #-f是前台,-h是家目录

  

[root@master manifests]# kubectl create -f poststart-pod.yaml 
pod/posttart-pod created

说明:删除的方法

[root@master manifests]# kubectl delete -f poststart-pod.yaml 
pod "posttart-pod" deleted


[root@master manifests]# kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
poststart-pod                 1/1       Running            0          3m


[root@master manifests]#  kubectl exec -it poststart-pod -- /bin/sh
/ # ls /data
web
/ # ls /data/web/html/

    上面看到在容器启动后,建立了/data/web/html目录。这就是postStart的用法。

preStop(终止之前钩子)

[root@master ~]# kubectl  explain pods.spec.containers.lifecycle.preStop

    preStop是指容器在终止前要立即执行的命令,等这些命令执行完了,容器才能终止。

容器的重启策略-restartPolicy

   一旦pod中的容器挂了,我们就把容器重启。

    策略包括如下:

    Always:表示容器挂了总是重启,这是默认策略

    OnFailures:表容器状态为错误时才重启,也就是容器正常终止时才重启

    Never:表示容器挂了不予重启

    对于Always这种策略,容器只要挂了,就会立即重启,这样是很耗费资源的。所以Always重启策略是这么做的:第一次容器挂了立即重启,如果再挂了就要延时10s重启,第三次挂了就等20s重启...... 依次类推

 容器的终止策略

    k8s会给容器30s的时间进行终止,如果30s后还没终止,就会强制终止。

  总结

pod:
        apiVersion
        kind
        metadata
        spec
        status(只读)
        spec:
                containers
                nodeSelector
                nodeName
                restartPolicy: Always,Never,OnFailure
                containers:
                        name
                        image
                        imagePullPolicy: Always、Never、IfNotPresent
                        ports:
                                name
                                containerPort
                        livenessProbe
                        readinessProbe
                        liftcycle
                ExecAction: exec
                TCPSocketAction: tcpSocket
                HTTPGetAction: httpGet

    



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28916011/viewspace-2213957/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2014-06-03

  • 博文量
    178
  • 访问量
    504166