☰
Spring Boot项目迁移到东方通TongWeb实战指南
2026/10/3 8:01:50 网站建设 项目流程

1. 项目概述:为什么Spring Boot项目要“换掉Tomcat”,偏偏选东方通TongWeb?

信创这个词,这两年在政企、金融、能源这些行业的技术会议上,几乎每场必提。但很多人一听到“信创适配”,第一反应还是——又要改代码?又要重测?又要加班?其实真没那么吓人,尤其当你用的是Spring Boot这种高度抽象的框架时,底层Servlet容器的切换,本质上是一次“拔插式”替换,不是推倒重来。关键在于:你得知道插哪里、怎么插、插完怎么验。而东方通TongWeb,就是国产中间件里目前落地最稳、文档最全、生态对接最成熟的那一款。

我去年接手一个省级医保平台的二期改造,原系统是Spring Boot 2.6.x + Tomcat 9,要求半年内完成信创环境迁移,目标运行环境是麒麟V10 + 飞腾2000处理器 + 东方通TongWeb V7.0。当时团队里有位老哥直接说:“Spring Boot打包成WAR丢到TongWeb里不就完了?”结果部署上去,首页404,日志里全是java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener。后来才发现,他连TongWeb的JDK版本要求都没看——TongWeb V7.0官方只支持JDK 8u261及以上或JDK 11,而他本地用的是JDK 17,连启动都报错。这事儿让我意识到:信创不是换个名字喊口号,它是一整套兼容性链条,从JVM、类加载机制、线程模型到管理控制台的交互逻辑,全都得重新对齐。

所以这篇内容,不讲政策文件,不列长串适配清单,就聚焦一个最实际的问题:一个标准的Spring Boot Web项目(Maven构建、内嵌Tomcat默认配置),如何零修改核心业务代码,仅通过调整构建方式、依赖和少量配置,就能在TongWeb上跑起来,并能进管理后台查应用状态、看日志、启停服务?我会把整个过程拆成四步:先搞懂TongWeb和Tomcat的根本差异在哪;再手把手带你改pom.xml、改启动类、打WAR包;然后教你怎么在Linux服务器上静默安装TongWeb、配置JDK、部署应用;最后给你一份真实压测场景下的调优参数和三个我踩过的坑——比如为什么你的Spring Boot Actuator端点在TongWeb里返回500,为什么MyBatis的SQL日志死活不打印,为什么上传大文件时总卡在80%不动。这些细节,官网文档不会写,但你在现场一定会遇到。

2. 核心设计思路:不是“替换”,而是“解耦+适配”

2.1 Spring Boot的Servlet容器抽象层,才是我们真正的杠杆

很多人以为Spring Boot“内置Tomcat”就意味着和Tomcat强绑定,这是个巨大误解。Spring Boot真正依赖的,从来不是Tomcat本身,而是Servlet规范(3.1+)和Java EE(现Jakarta EE)的Web容器契约。Tomcat、Jetty、Undertow,甚至TongWeb、金蝶Apusic、宝兰德BES,只要它们实现了Servlet 3.1+规范,Spring Boot就能和它们“握手”。这个握手协议,就是spring-boot-starter-web里的ServletWebServerFactory接口。

我们来看一段Spring Boot启动时的真实日志:

2024-06-15 10:23:45.123 INFO 12345 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)

这行日志里的关键词是TomcatWebServer,但它只是ServletWebServerFactory的一个实现。如果你把spring-boot-starter-tomcat这个starter从依赖里去掉,Spring Boot启动时就会报错:“No ServletWebServerFactory bean found”。但如果你自己提供一个TongWebServletWebServerFactory(当然,东方通没提供这个,我们也不需要),或者更现实的做法——放弃内嵌容器,走传统WAR部署模式,那整个问题就从“如何替换内嵌容器”降维成“如何让Spring Boot应用符合Servlet容器的部署规范”。

这就是整个方案的设计原点:不硬刚TongWeb的内嵌集成,而是回归Servlet标准,用最成熟、最可控的WAR包方式交付。这样做的好处是三点:第一,完全规避了TongWeb对Spring Boot内嵌容器的兼容性黑盒;第二,部署流程和原有Tomcat环境几乎一致,运维同学不用学新命令;第三,所有Spring Boot特性(自动配置、Actuator、Profile)全部保留,你只是换了个“房子”,没换“家具”。

2.2 TongWeb和Tomcat的三大本质差异,决定了我们必须改什么

光知道要打WAR包还不够。TongWeb不是Tomcat的“国产马甲”,它在底层实现上有三处关键差异,直接影响你的Spring Boot项目能否顺利启动:

第一,类加载器模型不同。Tomcat采用经典的双亲委派模型(Parent-Delegation),应用类优先从WEB-INF/lib加载,找不到才向上委托给Common ClassLoader。而TongWeb V7.0默认采用逆向委派(Child-First)模式,即先从应用自己的WEB-INF/lib找类,找不到才去父加载器(如System ClassLoader)里找。这听起来是好事,但恰恰是很多ClassNotFoundException的根源。比如Spring Boot的spring-boot-autoconfigure里有个DataSourceAutoConfiguration类,它依赖javax.sql.DataSource,这个接口在JDK里,也在TongWeb的lib目录下。如果TongWeb的类加载器先从自己的lib里加载了一个旧版javax.sql(比如JDK 8的),而你的Spring Boot用的是JDK 11的API,就会出现IncompatibleClassChangeError。解决方案?不是改代码,而是在TongWeb的server.xml里显式关闭逆向委派,或者更稳妥地,在WAR包的WEB-INF/web.xml里加一行<loader delegate="true"/>,强制走标准双亲委派。

第二,JNDI资源绑定机制不同。Tomcat里配置数据源,你习惯写context.xml,然后在Spring里用@Resource(name="jdbc/mydb")注入。TongWeb也支持JNDI,但它的JNDI命名空间是java:comp/env/,而且默认不启用全局JNDI。如果你的Spring Boot项目里用了@ConfigurationProperties(prefix="spring.datasource.jndi"),在TongWeb里很可能查不到JNDI Context。实测下来,最稳的方式是放弃JNDI,改用Spring Boot原生的application.yml配置数据源,把连接池(HikariCP)的配置项全写明,这样既脱离容器依赖,又便于多环境切换。

第三,管理控制台的权限体系和URL路径完全不同。Tomcat的管理界面是/manager/html,用户名密码在tomcat-users.xml里配。TongWeb的管理后台默认地址是http://localhost:6888/console,初始账号是admin/tongweb(注意:不是tongweb123,网上很多教程写错了),而且首次登录后强制改密。更重要的是,TongWeb的“应用部署”功能,不叫“Deploy”,叫“应用发布”,它不接受直接上传WAR包,而是要求你先把WAR包放到webapps目录下,再在控制台里点“发布”按钮,或者用命令行工具twctl。这点新手极易卡住——你传了WAR包,刷新页面却看不到应用列表,因为没点“发布”。

这三点差异,就是我们后续所有操作的底层逻辑。记住:不是TongWeb“不兼容”Spring Boot,而是它遵循了一套略有不同的企业级中间件规范。我们的任务,是做适配,不是做对抗。

3. 实操全流程:从Spring Boot项目到TongWeb成功运行

3.1 第一步:改造Spring Boot项目,生成标准WAR包

假设你有一个标准的Spring Boot Web项目,结构如下:

my-springboot-app/ ├── pom.xml ├── src/ │ └── main/ │ ├── java/com/example/app/ │ │ └── MyApplication.java │ └── resources/application.yml └── target/my-springboot-app-1.0.0.jar

现在我们要把它变成能在TongWeb里跑的WAR包。核心改动只有三处,全部在pom.xml和启动类里,业务代码一行都不用动。

第一步:修改pom.xml,移除内嵌Tomcat,添加WAR打包支持

找到pom.xml里的spring-boot-starter-web依赖,把它改成:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 关键:排除内嵌Tomcat --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Servlet API依赖,这是WAR包必需的 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency>

然后,在pom.xml的<packaging>标签里,把jar改成war:

<packaging>war</packaging>

最后,添加spring-boot-starter-tomcat为provided范围,注意:这里不是为了用它,而是为了让Spring Boot的编译期能识别Servlet API:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>

提示:provided范围意味着这个依赖只在编译和测试时有效,打包时不会打进WAR包,避免和TongWeb自带的Servlet实现冲突。这是Maven WAR打包的黄金法则。

第二步:改造启动类,继承SpringBootServletInitializer

打开你的主启动类MyApplication.java,让它继承SpringBootServletInitializer,并重写configure方法:

@SpringBootApplication public class MyApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 这里必须return builder.sources(MyApplication.class),不能写错类名 return builder.sources(MyApplication.class); } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }

这个configure方法是关键。它告诉Spring Boot:“我不是独立运行的Jar,我是要被Servlet容器加载的WAR,请按Web应用的方式初始化上下文。”没有这一步,TongWeb启动时会找不到Spring的根上下文,所有@Controller、@Service都会失效。

第三步:验证打包,生成WAR文件

执行Maven命令:

mvn clean package -Dmaven.test.skip=true

你会在target/目录下看到my-springboot-app-1.0.0.war。用jar -tf target/my-springboot-app-1.0.0.war | head -20检查一下结构,确认里面有:

  • WEB-INF/classes/(你的编译字节码)
  • WEB-INF/lib/(所有依赖jar,包括spring-boot-starter-web)
  • WEB-INF/web.xml(Spring Boot自动生成的,不用手动创建)

注意:Spring Boot 2.3+版本默认不生成web.xml,这是完全OK的。Servlet 3.0+规范支持注解驱动,TongWeb V7.0完全支持。如果你的项目用了老版本Spring Boot(<2.3),建议升级,否则可能需要手写web.xml。

3.2 第二步:在Linux服务器上静默安装TongWeb V7.0

TongWeb的安装包是.bin格式,官网下载后,需要在目标服务器(麒麟V10 + 飞腾CPU)上执行。这里强调“静默安装”,因为生产环境通常不允许图形界面,且要写入自动化脚本。

前提检查:JDK版本与环境变量

TongWeb V7.0官方支持JDK 8u261+ 或 JDK 11。我们以JDK 11为例(推荐,因Spring Boot 2.7.x对JDK 11优化更好):

# 查看当前JDK java -version # 输出应为 openjdk version "11.0.22" 2024-04-16 # 确认JAVA_HOME指向正确路径 echo $JAVA_HOME # 应输出 /usr/lib/jvm/java-11-openjdk-arm64 (飞腾架构需用arm64版本) # 如果未设置,编辑 /etc/profile sudo vi /etc/profile # 在末尾添加: export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64 export PATH=$JAVA_HOME/bin:$PATH source /etc/profile

执行静默安装

下载的安装包假设为tongweb-v7.0-linux-aarch64.bin(注意aarch64是飞腾CPU的架构标识):

# 赋予执行权限 chmod +x tongweb-v7.0-linux-aarch64.bin # 创建安装目录 sudo mkdir -p /opt/tongweb # 执行静默安装(-i silent 是关键参数) sudo ./tongweb-v7.0-linux-aarch64.bin -i silent -DINSTALLDIR=/opt/tongweb -DJAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64

安装完成后,TongWeb的主目录是/opt/tongweb,核心目录结构如下:

/opt/tongweb/ ├── bin/ # 启动/停止脚本:start.sh, stop.sh, twctl ├── conf/ # 主配置:server.xml, web.xml ├── lib/ # TongWeb核心jar包 ├── logs/ # 日志文件 ├── webapps/ # 应用部署目录(WAR包放这里) └── console/ # 管理控制台静态资源

启动TongWeb并验证

# 切换到bin目录 cd /opt/tongweb/bin # 启动(后台运行) ./start.sh # 查看进程 ps -ef | grep tongweb # 应看到类似:/usr/lib/jvm/java-11-openjdk-arm64/bin/java ... -Dcatalina.home=/opt/tongweb ... # 查看日志确认启动成功 tail -f /opt/tongweb/logs/stdout.log # 等待出现 "Server startup in [xxx] milliseconds" 即成功

此时,访问http://你的服务器IP:6888/console,用默认账号admin/tongweb登录。首次登录后,系统会强制你修改密码(新密码需包含大小写字母+数字+特殊字符,8位以上)。改完密码,你就进入了TongWeb的管理后台。

3.3 第三步:部署WAR包,配置应用参数

上传WAR包到webapps目录

用scp或rz命令,把本地生成的my-springboot-app-1.0.0.war上传到服务器:

scp target/my-springboot-app-1.0.0.war user@server-ip:/opt/tongweb/webapps/

上传后,不要急着去控制台点“发布”。先检查webapps目录:

ls -l /opt/tongweb/webapps/ # 你会看到 my-springboot-app-1.0.0.war 文件,以及一个同名的空文件夹 my-springboot-app-1.0.0/ # 这是TongWeb自动解压的痕迹,说明它已识别到WAR包

在管理控制台发布应用

  1. 登录http://server-ip:6888/console。
  2. 左侧菜单栏,点击“应用管理” → “应用发布”。
  3. 在右侧列表中,找到my-springboot-app-1.0.0(注意:显示的是解压后的文件夹名,不是WAR包名)。
  4. 勾选它,点击上方的“发布”按钮。
  5. 弹窗确认,点击“确定”。

发布成功后,状态会从“未发布”变成“已发布”,但此时应用还是“停止”状态。你需要再选中它,点击“启动”按钮。

注意:TongWeb的“发布”和“启动”是两个独立操作。只发布不启动,应用不会运行;只启动未发布,会报错“应用未发布”。

配置应用JVM参数(关键调优)

Spring Boot应用在TongWeb里跑,内存不足是常见问题。默认JVM参数太小(-Xms256m -Xmx512m),对于一个带MyBatis和Redis的中型应用远远不够。我们需要修改/opt/tongweb/bin/setenv.sh:

# 编辑setenv.sh vi /opt/tongweb/bin/setenv.sh # 找到JAVA_OPTS行,修改为(根据你的服务器内存调整): JAVA_OPTS="-Xms2g -Xmx4g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"

然后重启TongWeb:

./stop.sh && ./start.sh

验证应用是否正常运行

启动后,查看/opt/tongweb/logs/stdout.log,搜索my-springboot-app,应看到类似日志:

2024-06-15 14:30:22.123 INFO 12345 --- [main] com.example.app.MyApplication : Started MyApplication in 25.432 seconds (JVM running for 28.765)

再访问应用URL:http://server-ip:6888/my-springboot-app-1.0.0/(注意:上下文路径默认是WAR包名,不含.war后缀)。如果看到你的首页,恭喜,第一步成功!

3.4 第四步:打通Actuator、日志、文件上传等关键能力

Spring Boot的便利性,很大一部分来自Actuator健康检查、Logback日志、@RequestBody文件上传等。这些在TongWeb里不是开箱即用,需要针对性配置。

启用并访问Actuator端点

在application.yml里确保开启了Actuator:

management: endpoints: web: exposure: include: "*" # 或者明确列出 health,info,metrics,loggers endpoint: health: show-details: always

但直接访问http://server-ip:6888/my-springboot-app-1.0.0/actuator/health可能返回404或500。原因有两个:一是TongWeb的Servlet映射规则,二是Actuator的WebMvcEndpointHandlerMapping需要额外配置。

解决方案:在application.yml里加一行:

spring: web: resources: static-locations: classpath:/static/,classpath:/public/ mvc: static-path-pattern: /static/** # 关键:指定Actuator的base-path management: endpoints: web: base-path: /actuator

这样,Actuator端点就固定在/actuator/health,而不是被TongWeb的默认Servlet拦截。

让MyBatis SQL日志在TongWeb里正常打印

很多开发者发现,logging.level.org.mybatis=DEBUG在TongWeb里不生效。这是因为TongWeb自身的日志框架(Log4j2)和Spring Boot的Logback存在冲突。解决方法是统一用Logback,并禁用TongWeb的Log4j2。

在src/main/resources/下,创建logback-spring.xml:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/logs}/spring.log}"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> </encoder> </appender> <!-- 关键:为MyBatis开启DEBUG --> <logger name="org.mybatis" level="DEBUG" additivity="false"> <appender-ref ref="CONSOLE"/> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>

然后,在pom.xml里排除TongWeb自带的Log4j2:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- 显式引入Logback --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <exclusions> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </exclusion> </exclusions> </dependency>

解决大文件上传413错误

Spring Boot默认单文件上传限制是1MB,TongWeb默认也是1MB。当用户上传大于1MB的文件时,会返回HTTP 413。需要两边同时调大:

在application.yml里:

spring: servlet: multipart: max-file-size: 50MB max-request-size: 50MB

在TongWeb的/opt/tongweb/conf/server.xml里,找到<Connector>标签,添加maxPostSize属性:

<Connector port="6888" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxPostSize="52428800" /> <!-- 50MB = 52428800 bytes -->

重启TongWeb,问题解决。

4. 常见问题排查与独家避坑指南

4.1 问题速查表:从404到500,一线排障记录

现象可能原因排查步骤解决方案
首页404,但TongWeb日志无报错WAR包未发布,或上下文路径错误1. 登录控制台,确认应用状态为“已启动”
2.ls /opt/tongweb/webapps/,确认解压后的文件夹存在
3. 访问URL是否带了.war后缀(错误:/app.war/;正确:/app/)
在控制台“应用发布”里发布并启动应用;URL去掉.war
启动时报ClassNotFoundException: org.springframework.web.context.ContextLoaderListenerspring-boot-starter-web依赖未排除Tomcat,或javax.servlet-api范围不是provided1.jar -tf target/app.war | grep servlet-api,确认javax.servlet-api没被打包进去
2. 检查pom.xml,确认spring-boot-starter-tomcat被exclusion且scope=provided
严格按3.1节修改pom.xml,重新打包
Actuator/actuator/health返回500,日志报NoSuchBeanDefinitionExceptionWebMvcEndpointHandlerMapping未正确注册1.grep -r "WebMvcEndpointHandlerMapping" /opt/tongweb/logs/
2. 检查application.yml是否漏了management.endpoints.web.base-path
在application.yml中显式设置management.endpoints.web.base-path: /actuator
MyBatis SQL不打印,logging.level.org.mybatis=DEBUG无效TongWeb的Log4j2与Logback冲突1.ps aux | grep java,确认JVM参数里没有-Dlog4j.configurationFile
2.cat /opt/tongweb/conf/log4j2.xml,确认是否存在
删除/opt/tongweb/conf/log4j2.xml,或重命名为log4j2.xml.bak,强制使用Logback
上传文件时进度条卡在80%,最终超时TongWeb的maxPostSize和Spring Boot的maxRequestSize不一致1.curl -X POST http://ip:6888/app/upload -F "file=@large.zip",观察响应时间
2.tail -f /opt/tongweb/logs/stdout.log,搜索timeout
同时修改application.yml和server.xml中的大小限制,保持一致

4.2 三个血泪教训:那些官网不会写的细节

教训一:TongWeb的“热部署”是把双刃剑

TongWeb控制台有个“热部署”开关,默认是开启的。意思是,当你修改了webapps/app/WEB-INF/classes/下的class文件,TongWeb会自动重新加载。这听起来很爽,但对Spring Boot是灾难。因为Spring Boot的ApplicationContext是单例且复杂的,热加载会导致Bean定义冲突、AOP代理失效、甚至内存泄漏。我亲眼见过一个项目,热部署三次后,/actuator/metrics返回的JVM内存指标翻了五倍,最后OOM。

我的做法:在/opt/tongweb/conf/server.xml里,把<Context>标签的reloadable属性设为false:

<Context path="/myapp" docBase="my-springboot-app-1.0.0" reloadable="false" />

生产环境,永远用“停-替-启”三步法更新代码,别信热部署。

教训二:application.yml里的spring.profiles.active在TongWeb里可能失效

Spring Boot默认从spring.profiles.active读取激活的Profile,但在TongWeb里,这个值有时会被容器的系统属性覆盖。比如TongWeb启动脚本里设置了-Dspring.profiles.active=prod,就会覆盖你application.yml里的配置。

我的做法:不依赖application.yml,改用JVM参数指定:

# 修改 /opt/tongweb/bin/start.sh,在JAVA_OPTS里加 JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"

这样,Profile的优先级最高,且所有环境一致。

教训三:TongWeb的/console管理后台,千万别用Chrome最新版直连

这不是Bug,是兼容性问题。TongWeb V7.0的控制台前端用的是较老的ExtJS框架,而Chrome 120+默认禁用了某些旧版JavaScript API(如document.all)。结果就是,登录后页面白屏,F12看Console全是ReferenceError。

我的做法:给运维同学发一条命令,让他们在Chrome里输入chrome://flags/#unsafely-treat-insecure-origin-as-secure,把Insecure origins treated as secure设为Enabled,然后重启Chrome。或者,更简单——用Firefox ESR(企业版)或Edge的IE模式访问http://ip:6888/console,100%兼容。

5. 后续可扩展方向:不止于“能跑”,更要“跑好”

做到上面四步,你的Spring Boot应用已经在TongWeb上稳定运行了。但这只是信创迁移的第一公里。接下来,你可以沿着这三个方向深挖,把项目做得更扎实:

第一,集成TongWeb的集群能力。TongWeb支持多节点集群,通过conf/tongweb-cluster.xml配置。你可以把Spring Boot的Session存储从内存改成Redis,再配合TongWeb的集群广播,实现真正的高可用。这比单纯堆机器更有技术含量。

第二,对接东方通的统一认证中心(UAC)。很多政企项目要求单点登录(SSO)。TongWeb提供了标准的CAS和OAuth2.0接入方式。你不需要重写登录逻辑,只需在Spring Security里配置CasAuthenticationFilter或OAuth2LoginAuthenticationFilter,把认证委托给UAC。

第三,利用TongWeb的APM监控探针。TongWeb自带性能监控模块,可以采集JVM、SQL、HTTP请求的详细指标。你可以在conf/tongweb-monitor.xml里开启探针,然后把数据推送到Prometheus,用Grafana画出你的Spring Boot应用的实时性能大盘。这才是信创项目该有的技术水位。

最后分享一个小技巧:每次升级TongWeb小版本(比如从V7.0.1到V7.0.2),别急着全量上线。先用twctl命令行工具,在测试环境部署一个最小化的Spring Boot Hello World WAR包,跑通/actuator/health和/actuator/metrics,确认基础能力没问题,再上业务应用。信创落地,稳字当头,快不如准。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询