spring Boot(十九):使用Spring Boot Actuator监控应用

微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的处理和传递,出现了异常如何快速定位是哪个环节出现了问题?

在这种框架下,微服务的监控显得尤为重要。本文主要结合Spring Boot Actuator,跟大家一起分享微服务Spring Boot Actuator的常见用法,方便我们在日常中对我们的微服务进行监控治理。

一、Actuator监控

Spring Boot使用“习惯优于配置的理念”,采用包扫描和自动化配置的机制来加载依赖jar中的Spring bean,不需要任何Xml配置,就可以实现Spring的所有配置。虽然这样做能让我们的代码变得非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上,这使得我们分析整个应用中资源和实例的各种关系变得非常的困难。

Actuator是Spring Boot提供的对应用系统的自省和监控的集成功能,可以查看应用配置的详细信息,例如自动化配置信息、创建的Spring beans以及一些环境属性等。

Actuator监控只需要添加以下依赖就可以完成。

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-actuator</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-security</artifactId>
  13. </dependency>
  14. </dependencies>

为了保证actuator暴露的监控接口的安全性,需要添加安全控制的依赖spring-boot-start-security依赖,访问应用监控端点时,都需要输入验证信息。Security依赖,可以选择不加,不进行安全管理,但不建议这么做。

二、Actuator 的 REST 接口

Actuator监控分成两类:原生端点和用户自定义端点;自定义端点主要是指扩展性,用户可以根据自己的实际应用,定义一些比较关心的指标,在运行期进行监控。

原生端点是在应用程序里提供众多 Web 接口,通过它们了解应用程序运行时的内部状况。原生端点又可以分成三类:

  • 应用配置类:可以查看应用在运行期的静态信息:例如自动配置信息、加载的springbean信息、yml文件配置信息、环境信息、请求映射信息;
  • 度量指标类:主要是运行期的动态信息,例如堆栈、请求连、一些健康指标、metrics信息等;
  • 操作控制类:主要是指shutdown,用户可以发送一个请求将应用的监控功能关闭。

Actuator 提供了 13 个接口,具体如下表所示。

三、快速上手

1,相关配置

(1)项目依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-actuator</artifactId>
  9. </dependency>
  10. </dependencies>

(2)配置文件

  1. server:
  2. port: 8080
  3. management:
  4. security:
  5. enabled: false #关掉安全认证
  6. port: 8088 #管理端口调整成8088
  7. context-path: /monitor #actuator的访问路径
  8. endpoints:
  9. shutdown:
  10. enabled: true
  11.  
  12. info:
  13. app:
  14. name: spring-boot-actuator
  15. version: 1.0.0
  • management.security.enabled=false默认有一部分信息需要安全验证之后才可以查看,如果去掉这些安全认证,直接设置management.security.enabled=false
  • management.context-path=/monitor 代表启用单独的url地址来监控Spring Boot应用,为了安全一般都启用独立的端口来访问后端的监控信息
  • endpoints.shutdown.enabled=true 启用接口关闭Spring Boot

配置完成之后,启动项目就可以继续验证各个监控功能了。

四、命令详解

1,autoconfig

Spring Boot的自动配置功能非常便利,但有时候也意味着出问题比较难找出具体的原因。使用 autoconfig 可以在应用运行时查看代码了某个配置在什么条件下生效,或者某个自动配置为什么没有生效。

启动示例项目,访问:http://localhost:8088/monitor/autoconfig返回部分信息如下:

  1. {
  2. "positiveMatches": {
  3. "DevToolsDataSourceAutoConfiguration": {
  4. "notMatched": [
  5. {
  6. "condition": "DevToolsDataSourceAutoConfiguration.DevToolsDataSourceCondition",
  7. "message": "DevTools DataSource Condition did not find a single DataSource bean"
  8. }
  9. ],
  10. "matched": [ ]
  11. },
  12. "RemoteDevToolsAutoConfiguration": {
  13. "notMatched": [
  14. {
  15. "condition": "OnPropertyCondition",
  16. "message": "@ConditionalOnProperty (spring.devtools.remote.secret) did not find property 'secret'"
  17. }
  18. ],
  19. "matched": [
  20. {
  21. "condition": "OnClassCondition",
  22. "message": "@ConditionalOnClass found required classes 'javax.servlet.Filter', 'org.springframework.http.server.ServerHttpRequest'; @ConditionalOnMissingClass did not find unwanted class"
  23. }
  24. ]
  25. }
  26. }
  27. }

2,configprops

查看配置文件中设置的属性内容,以及一些配置属性的默认值。

启动示例项目,访问:http://localhost:8088/monitor/configprops返回部分信息如下:

  1. {
  2. ...
  3. "environmentEndpoint": {
  4. "prefix": "endpoints.env",
  5. "properties": {
  6. "id": "env",
  7. "sensitive": true,
  8. "enabled": true
  9. }
  10. },
  11. "spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties": {
  12. "prefix": "spring.http.multipart",
  13. "properties": {
  14. "maxRequestSize": "10MB",
  15. "fileSizeThreshold": "0",
  16. "location": null,
  17. "maxFileSize": "1MB",
  18. "enabled": true,
  19. "resolveLazily": false
  20. }
  21. },
  22. "infoEndpoint": {
  23. "prefix": "endpoints.info",
  24. "properties": {
  25. "id": "info",
  26. "sensitive": false,
  27. "enabled": true
  28. }
  29. }
  30. ...
  31. }

3,beans

根据示例就可以看出,展示了bean的别名、类型、是否单例、类的地址、依赖等信息。

启动示例项目,访问:http://localhost:8088/monitor/beans返回部分信息如下:

  1. [
  2. {
  3. "context": "application:8080:management",
  4. "parent": "application:8080",
  5. "beans": [
  6. {
  7. "bean": "embeddedServletContainerFactory",
  8. "aliases": [
  9.  
  10. ],
  11. "scope": "singleton",
  12. "type": "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory",
  13. "resource": "null",
  14. "dependencies": [
  15.  
  16. ]
  17. },
  18. {
  19. "bean": "endpointWebMvcChildContextConfiguration",
  20. "aliases": [
  21.  
  22. ],
  23. "scope": "singleton",
  24. "type": "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcChildContextConfiguration$$EnhancerBySpringCGLIB$$a4a10f9d",
  25. "resource": "null",
  26. "dependencies": [
  27.  
  28. ]
  29. }
  30. }
  31. ]

4,dump

/dump 接口会生成当前线程活动的快照。这个功能非常好,方便我们在日常定位问题的时候查看线程的情况。 主要展示了线程名、线程ID、线程的状态、是否等待锁资源等信息。

启动示例项目,访问:http://localhost:8088/monitor/dump返回部分信息如下:

  1. [
  2. {
  3. "threadName": "http-nio-8088-exec-6",
  4. "threadId": 49,
  5. "blockedTime": -1,
  6. "blockedCount": 0,
  7. "waitedTime": -1,
  8. "waitedCount": 2,
  9. "lockName": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@1630a501",
  10. "lockOwnerId": -1,
  11. "lockOwnerName": null,
  12. "inNative": false,
  13. "suspended": false,
  14. "threadState": "WAITING",
  15. "stackTrace": [
  16. {
  17. "methodName": "park",
  18. "fileName": "Unsafe.java",
  19. "lineNumber": -2,
  20. "className": "sun.misc.Unsafe",
  21. "nativeMethod": true
  22. },
  23. {
  24. "methodName": "park",
  25. "fileName": "LockSupport.java",
  26. "lineNumber": 175,
  27. "className": "java.util.concurrent.locks.LockSupport",
  28. "nativeMethod": false
  29. },
  30. {
  31. "methodName": "await",
  32. "fileName": "AbstractQueuedSynchronizer.java",
  33. "lineNumber": 2039,
  34. "className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject",
  35. "nativeMethod": false
  36. },
  37. ...
  38. {
  39. "methodName": "getTask",
  40. "fileName": "ThreadPoolExecutor.java",
  41. "lineNumber": 1067,
  42. "className": "java.util.concurrent.ThreadPoolExecutor",
  43. "nativeMethod": false
  44. },
  45. {
  46. "methodName": "runWorker",
  47. "fileName": "ThreadPoolExecutor.java",
  48. "lineNumber": 1127,
  49. "className": "java.util.concurrent.ThreadPoolExecutor",
  50. "nativeMethod": false
  51. },
  52. {
  53. "methodName": "run",
  54. "fileName": "ThreadPoolExecutor.java",
  55. "lineNumber": 617,
  56. "className": "java.util.concurrent.ThreadPoolExecutor$Worker",
  57. "nativeMethod": false
  58. },
  59. {
  60. "methodName": "run",
  61. "fileName": "TaskThread.java",
  62. "lineNumber": 61,
  63. "className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
  64. "nativeMethod": false
  65. },
  66. {
  67. "methodName": "run",
  68. "fileName": "Thread.java",
  69. "lineNumber": 745,
  70. "className": "java.lang.Thread",
  71. "nativeMethod": false
  72. }
  73. ],
  74. "lockedMonitors": [
  75.  
  76. ],
  77. "lockedSynchronizers": [
  78.  
  79. ],
  80. "lockInfo": {
  81. "className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject",
  82. "identityHashCode": 372286721
  83. }
  84. }
  85. ...
  86. ]

5,env

展示了系统环境变量的配置信息,包括使用的环境变量、JVM 属性、命令行参数、项目使用的jar包等信息。和configprops不同的是,configprops关注于配置信息,env关注运行环境信息。

启动示例项目,访问:http://localhost:8088/monitor/env返回部分信息如下:

  1. {
  2. "profiles": [
  3.  
  4. ],
  5. "server.ports": {
  6. "local.management.port": 8088,
  7. "local.server.port": 8080
  8. },
  9. "servletContextInitParams": {
  10.  
  11. },
  12. "systemProperties": {
  13. "com.sun.management.jmxremote.authenticate": "false",
  14. "java.runtime.name": "Java(TM) SE Runtime Environment",
  15. "spring.output.ansi.enabled": "always",
  16. "sun.boot.library.path": "C:\\Program Files\\Java\\jdk1.8.0_101\\jre\\bin",
  17. "java.vm.version": "25.101-b13",
  18. "java.vm.vendor": "Oracle Corporation",
  19. "java.vendor.url": "http://java.oracle.com/",
  20. "java.rmi.server.randomIDs": "true",
  21. "path.separator": ";",
  22. "java.vm.name": "Java HotSpot(TM) 64-Bit Server VM",
  23. "file.encoding.pkg": "sun.io",
  24. "user.country": "CN",
  25. "user.script": "",
  26. "sun.java.launcher": "SUN_STANDARD",
  27. "sun.os.patch.level": "",
  28. "PID": "5268",
  29. "com.sun.management.jmxremote.port": "60093",
  30. "java.vm.specification.name": "Java Virtual Machine Spe

为了避免敏感信息暴露到 /env 里,所有名为password、secret、key(或者名字中最后一段是这些)的属性在 /env 里都会加上“*”。举个例子,如果有一个属性名字是database.password,那么它在/env中的显示效果是这样的:

"database.password":"******"

/env/{name}用法

就是env的扩展 可以获取指定配置信息,比如:http://localhost:8088/monitor/env/java.vm.version,返回:{"java.vm.version":"25.101-b13"}

6,health

可以看到 HealthEndPoint 给我们提供默认的监控结果,包含 磁盘检测和数据库检测

启动示例项目,访问:http://localhost:8088/monitor/health返回部分信息,下面的JSON响应是由状态、磁盘空间和db。描述了应用程序的整体健康状态,UP 表明应用程序是健康的。磁盘空间描述总磁盘空间,剩余的磁盘空间和最小阈值。application.properties阈值是可配置的:

  1. {
  2. "status": "UP",
  3. "diskSpace": {
  4. "status": "UP",
  5. "total": 209715195904,
  6. "free": 183253909504,
  7. "threshold": 10485760
  8. }
  9. "db": {
  10. "status": "UP",
  11. "database": "MySQL",
  12. "hello": 1
  13. }
  14. }

其实看 Spring Boot-actuator 源码,你会发现 HealthEndPoint 提供的信息不仅限于此,org.springframework.boot.actuate.health 包下 你会发现 ElasticsearchHealthIndicator、RedisHealthIndicator、RabbitHealthIndicator 等

7,info

info就是我们自己配置在配置文件中以Info开头的配置信息,比如我们在示例项目中的配置是:

  1. info:
  2. app:
  3. name: spring-boot-actuator
  4. version: 1.0.0

启动示例项目,访问:http://localhost:8088/monitor/info返回部分信息如下:

  1. {
  2. "app": {
  3. "name": "spring-boot-actuator",
  4. "version": "1.0.0"
  5. }
  6. }

8,mappings

描述全部的URI路径,以及它们和控制器的映射关系

启动示例项目,访问:http://localhost:8088/monitor/mappings返回部分信息如下:

  1. {
  2. "/**/favicon.ico": {
  3. "bean": "faviconHandlerMapping"
  4. },
  5. "{[/hello]}": {
  6. "bean": "requestMappingHandlerMapping",
  7. "method": "public java.lang.String com.neo.controller.HelloController.index()"
  8. },
  9. "{[/error]}": {
  10. "bean": "requestMappingHandlerMapping",
  11. "method": "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"
  12. }
  13. }

9,metrics

最重要的监控内容之一,主要监控了JVM内容使用、GC情况、类加载信息等。

启动示例项目,访问:http://localhost:8088/monitor/metrics返回部分信息如下:

  1. {
  2. "mem": 337132,
  3. "mem.free": 183380,
  4. "processors": 4,
  5. "instance.uptime": 254552,
  6. "uptime": 259702,
  7. "systemload.average": -1.0,
  8. "heap.committed": 292864,
  9. "heap.init": 129024,
  10. "heap.used": 109483,
  11. "heap": 1827840,
  12. "nonheap.committed": 45248,
  13. "nonheap.init": 2496,
  14. "nonheap.used": 44269,
  15. "nonheap": 0,
  16. "threads.peak": 63,
  17. "threads.daemon": 43,
  18. "threads.totalStarted": 83,
  19. "threads": 46,
  20. "classes": 6357,
  21. "classes.loaded": 6357,
  22. "classes.unloaded": 0,
  23. "gc.ps_scavenge.count": 8,
  24. "gc.ps_scavenge.time": 99,
  25. "gc.ps_marksweep.count": 1,
  26. "gc.ps_marksweep.time": 43,
  27. "httpsessions.max": -1,
  28. "httpsessions.active": 0
  29. }

对 /metrics接口提供的信息进行简单分类如下表:

解释说明:

  • 请注意,这里的一些度量值,比如数据源和Tomcat会话,仅在应用程序中运行特定组件时才有数据。你还可以注册自己的度量信息。

  • HTTP的计数器和度量值需要做一点说明。counter.status 后的值是HTTP状态码,随后是所请求的路径。举个例子,counter.status.200.metrics 表明/metrics端点返回 200(OK) 状态码的次数。

  • HTTP的度量信息在结构上也差不多,却在报告另一类信息。它们全部以gauge.response 开头,,表明这是HTTP响应的度量信息。前缀后是对应的路径。度量值是以毫秒为单位的时间,反映了最近处理该路径请求的耗时。

  • 这里还有几个特殊的值需要注意。root路径指向的是根路径或/。star-star代表了那些Spring 认为是静态资源的路径,包括图片、JavaScript和样式表,其中还包含了那些找不到的资源。这就是为什么你经常会看到 counter.status.404.star-star,这是返回了HTTP 404 (NOT FOUND) 状态的请求数。  

  • /metrics接口会返回所有的可用度量值,但你也可能只对某个值感兴趣。要获取单个值,请求时可以在URL后加上对应的键名。例如,要查看空闲内存大小,可以向/metrics/mem.free发一 个GET请求。例如访问:http://localhost:8088/monitor/metrics/mem.free,返回:{"mem.free":178123}

10,shutdown

开启接口优雅关闭Spring Boot应用,要使用这个功能首先需要在配置文件中开启:

  1. endpoints:
  2. shutdown:
  3. enabled: true

配置完成之后,启动示例项目,访问:http://localhost:8088/monitor/shutdown返回部分信息如下:

  1. {
  2. "message": "Shutting down, bye..."
  3. }

此时你会发现应用已经被关闭。

11,trace

/trace 接口能报告所有Web请求的详细信息,包括请求方法、路径、时间戳以及请求和响应的头信息,记录每一次请求的详细信息。

启动示例项目,先访问一次:http://localhost:8080/hello,再到浏览器执行:http://localhost:8088/monitor/trace查看返回信息:

  1. [
  2. {
  3. "timestamp": 1516780334777,
  4. "info": {
  5. "method": "GET",
  6. "path": "/hello",
  7. "headers": {
  8. "request": {
  9. "host": "localhost:8080",
  10. "connection": "keep-alive",
  11. "cache-control": "max-age=0",
  12. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
  13. "upgrade-insecure-requests": "1",
  14. "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
  15. "accept-encoding": "gzip, deflate, br",
  16. "accept-language": "zh-CN,zh;q=0.9",
  17. "cookie": "UM_distinctid=16053ba344f1cd-0dc220c44cc94-b7a103e-13c680-16053ba3450751; Hm_lvt_0fb30c642c5f6453f17d881f529a1141=1513076406,1514961720,1515649377; CNZZDATA1260945749=232252692-1513233181-%7C1516085149; Hm_lvt_6d8e8bb59814010152d98507a18ad229=1515247964,1515296008,1515672972,1516086283"
  18. },
  19. "response": {
  20. "X-Application-Context": "application:8080",
  21. "Content-Type": "text/html;charset=UTF-8",
  22. "Content-Length": "11",
  23. "Date": "Wed, 24 Jan 2018 07:52:14 GMT",
  24. "status": "200"
  25. }
  26. },
  27. "timeTaken": "4"
  28. }
  29. }
  30. ]

上述信息展示了,/hello请求的详细信息。

五、其它配置

1,敏感信息访问限制

根据上面表格,鉴权为false的,表示不敏感,可以随意访问,否则就是做了一些保护,不能随意访问。

endpoints.mappings.sensitive=false

这样需要对每一个都设置,比较麻烦。敏感方法默认是需要用户拥有ACTUATOR角色,因此,也可以设置关闭安全限制:

management.security.enabled=false

或者配合Spring Security做细粒度控制。

2,启用和禁用接口

虽然Actuator的接口都很有用,但你不一定需要全部这些接口。默认情况下,所有接口(除 了/shutdown)都启用。比如要禁用 /metrics 接口,则可以设置如下:

endpoints.metrics.enabled = false

如果你只想打开一两个接口,那就先禁用全部接口,然后启用那几个你要的,这样更方便。

  1. endpoints.enabled = false
  2. endpoints.metrics.enabled = true

spring Boot(十九):使用Spring Boot Actuator监控应用的更多相关文章

  1. Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例

    Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...

  2. Spring Boot(十四):spring boot整合shiro-登录认证和权限管理

    Spring Boot(十四):spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉 ...

  3. Spring Boot(十二):spring boot如何测试打包部署

    Spring Boot(十二):spring boot如何测试打包部署 一.开发阶段 1,单元测试 在开发阶段的时候最重要的是单元测试了,springboot对单元测试的支持已经很完善了. (1)在p ...

  4. Spring入门(十四):Spring MVC控制器的2种测试方法

    作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环 ...

  5. (转)Spring Boot (十九):使用 Spring Boot Actuator 监控应用

    http://www.ityouknow.com/springboot/2018/02/06/spring-boot-actuator.html 微服务的特点决定了功能模块的部署是分布式的,大部分功能 ...

  6. 学习 Spring Boot:(二十九)Spring Boot Junit 单元测试

    前言 JUnit 是一个回归测试框架,被开发者用于实施对应用程序的单元测试,加快程序编制速度,同时提高编码的质量. JUnit 测试框架具有以下重要特性: 测试工具 测试套件 测试运行器 测试分类 了 ...

  7. Spring(十九):Spring AOP(三):切面的优先级、重复使用切入点表达式

    背景: 1)指定切面优先级示例:有的时候需要对一个方法指定多个切面,而这多个切面有时又需要按照不同顺序执行,因此,切面执行优先级别指定功能就变得很实用. 2)重复使用切入点表达式:上一篇文章中,定义前 ...

  8. Spring学习(十九)----- Spring与WEB容器整合

    首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关.即不会因为 filter 写在 listener 的前面而会先加载 filter.最终得出的结论是:listener -> ...

  9. Spring核心技术(九)——Spring管理的组件和Classpath扫描

    Spring管理的组件和Classpath的扫描 在前文描述中使用到的Spring中的Bean的定义,都是通过指定的XML来配置的.而前文中描述的注解的解析则是在源代码级别来提供配置元数据的.在那些例 ...

随机推荐

  1. JAVA编程思想学习笔记4-chap10-12-斗之气4段

    1.内部类:Iterator 2..this生成对外部类的引用 3..new:通过外部类对象创建内部类对象 package com.chengjie; public class TestInnerCl ...

  2. UGUI实现摇杆

    效果图

  3. unity3d生命周期

  4. php url链接地址传数组方法 json_decode解析数组失败 经过url链接的json数组解析出错的解决方法 (原)

    先说出现的问题: 请求一个接口(例如  http://www.a.com/getmes.php)需要传一个数组参数 param ,值为 数组 array(0=>'刘师傅',1=>'1760 ...

  5. 字符串转化为int数组

    String a = "1,2,3,4,5,6" String str[] = a.split(","); int array[] = new int[str. ...

  6. C# Mongo Client 2.4.2创建索引

    static async Task CreateIndex() { var client = new MongoClient(); var database = client.GetDatabase( ...

  7. 说说html 的<!DOCTYPE>声明&标准模式与兼容模式

    我们都知道<!DOCTYPE>声明位于文档的最前面,处于<html>标签之前. <!DOCTYPE>声明不是html标签,它的作用:告知web浏览界面应该使用哪个h ...

  8. HighCharts学习笔记(一)

    HighChars基本概述 Highcharts是一个纯js写成的插件库,很好的外观表现可以满足任何图标需求. 开始使用chart之前进行配置 全局配置: Highcharts.setOptions( ...

  9. sitecore系列教程之营销人员和技术人员如何策划与消费者的对话以提升体验?

    “每次良好的交谈都要从良好的倾听开始.” - 未知 你是如何听取网站访问者的?你是在倾听还是只是回复? 拥有内容管理系统只是良好网站战略的一个要素.毕竟,内容必须是动态的,及时的和相关的. 当网站访问 ...

  10. 20155228 2016-2017-2 《Java程序设计》第10周学习总结

    20155228 2016-2017-2 <Java程序设计>第10周学习总结 教材学习内容总结 网络 网络是能够波此通信的计算机的集合根据范}到的宽度,网络可以分为局域网和广域网.LAN ...