PySpark:“异常:Java 网关进程在向驱动程序发送其端口号之前退出”
- 2025-02-28 08:22:00
- admin 原创
- 5
问题描述:
我正在尝试在MacBook Air上运行 PySpark 。当我尝试启动它时,出现错误:
异常:Java 网关进程在向驱动程序发送其端口号之前退出
当启动时调用 sc = SparkContext() 时。我尝试运行以下命令:
./bin/pyspark
./bin/spark-shell
export PYSPARK_SUBMIT_ARGS="--master local[2] pyspark-shell"
无济于事。我也看过这里:
Spark + Python-Java 网关进程在向驱动程序发送其端口号之前退出了吗?
但这个问题一直没有得到解答。我该如何解决?
解决方案 1:
一个可能的原因是未设置JAVA_HOME,因为未安装java。
我遇到了同样的问题。它说
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/apache/spark/launcher/Main : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:296)
at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:406)
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/opt/spark/python/pyspark/conf.py", line 104, in __init__
SparkContext._ensure_initialized()
File "/opt/spark/python/pyspark/context.py", line 243, in _ensure_initialized
SparkContext._gateway = gateway or launch_gateway()
File "/opt/spark/python/pyspark/java_gateway.py", line 94, in launch_gateway
raise Exception("Java gateway process exited before sending the driver its port number")
Exception: Java gateway process exited before sending the driver its port number
sc = pyspark.SparkConf()
我通过运行解决了这个问题
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
来自https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-get-on-ubuntu-16-04
解决方案 2:
这应该可以帮助你
一种解决方案是将 pyspark-shell 添加到 shell 环境变量 PYSPARK_SUBMIT_ARGS:
export PYSPARK_SUBMIT_ARGS="--master local[2] pyspark-shell"
python/pyspark/java_gateway.py 中有一个变化,如果用户设置了 PYSPARK_SUBMIT_ARGS 变量,则需要 PYSPARK_SUBMIT_ARGS 包含 pyspark-shell。
解决方案 3:
openjdk-8-jdk
在 Ubuntu 上运行 pyspark 时出现此错误消息,通过安装包解决了该问题
from pyspark import SparkConf, SparkContext
sc = SparkContext(conf=SparkConf().setAppName("MyApp").setMaster("local"))
^^^ error
安装 Open JDK 8:
apt-get install openjdk-8-jdk-headless -qq
在 MacOS 上
在 Mac OS 上也是一样,我在终端中输入:
$ java -version
No Java runtime present, requesting install.
我被提示从Oracle 的下载站点安装 Java ,选择 MacOS 安装程序,点击jdk-13.0.2_osx-x64_bin.dmg
后检查 Java 是否已安装
$ java -version
java version "13.0.2" 2020-01-14
编辑要安装 JDK 8,您需要转到https://www.oracle.com/java/technologies/javase-jdk8-downloads.html(需要登录)
之后我就能够使用 pyspark 启动 Spark 上下文。
检查是否有效
在 Python 中:
from pyspark import SparkContext
sc = SparkContext.getOrCreate()
# check that it really works by running a job
# example from http://spark.apache.org/docs/latest/rdd-programming-guide.html#parallelized-collections
data = range(10000)
distData = sc.parallelize(data)
distData.filter(lambda x: not x&1).take(10)
# Out: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
请注意,您可能需要设置环境变量PYSPARK_PYTHON
,并且PYSPARK_DRIVER_PYTHON
它们必须与您用于运行 pyspark(驱动程序)的 Python(或 IPython)是相同的 Python 版本。
解决方案 4:
我使用 Mac OS。我已修复该问题!
下面我来介绍一下我如何修复它。
JDK8 似乎运行良好。(https://github.com/jupyter/jupyter/issues/248)
所以我检查了我的 JDK /Library/Java/JavaVirtualMachines ,这条路径上只有jdk-11.jdk 。
我下载了 JDK8(我点击了链接)。它是:
brew tap caskroom/versions
brew cask install java8
之后我又补充道
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home
export JAVA_HOME="$(/usr/libexec/java_home -v 1.8)"
到~/.bash_profile文件。(你应该检查你的 jdk1.8 文件名)
现在它起作用了!希望这能有所帮助 :)
解决方案 5:
我将在这里重新发布我如何解决这个问题,仅供将来参考。
我如何解决类似的问题
先决条件:
anaconda 已经安装
Spark 已安装(https://spark.apache.org/downloads.html)
pyspark 已安装(https://anaconda.org/conda-forge/pyspark)
我执行的步骤(注意:根据您的系统设置文件夹路径)
设置以下环境变量。
SPARK_HOME 为 'C:\spark\spark-3.0.1-bin-hadoop2.7'
将 HADOOP_HOME 设置为 'C:\spark\spark-3.0.1-bin-hadoop2.7'
将 PYSPARK_DRIVER_PYTHON 设置为“jupyter”
将 PYSPARK_DRIVER_PYTHON_OPTS 设置为“笔记本”
将 'C:\spark\spark-3.0.1-bin-hadoop2.7\bin;' 添加到 PATH 系统变量。
直接更改C:下的java安装文件夹(之前java是安装在Program files下,所以我直接重新安装在C:下)
所以我的 JAVA_HOME 将变成这样 'C:\java\jdk1.8.0_271'
现在。它起作用了!
解决方案 6:
我在使用 PySpark 时遇到了同样的错误,将 JAVA_HOME 设置为 Java 11 对我来说是有效的(它最初设置为 16)。我使用的是 macOS 和 PyCharm。您可以通过执行以下操作来检查当前的 Java 版本echo $JAVA_HOME
。
以下是对我有用的方法。在我的 Mac 上,我使用了以下Homebrew命令,但您可以使用其他方法安装所需的 Java 版本,具体取决于您的操作系统。
# Install Java 11 (I believe 8 works too)
brew install openjdk@11
# Set JAVA_HOME by assigning the path where your Java is
export JAVA_HOME=/usr/local/opt/openjdk@11
注意:如果您使用 Homebrew 安装并且需要找到路径的位置,您可以执行以下操作brew --prefix openjdk@11
,它应该返回如下路径:/usr/local/opt/openjdk@11
此时,我可以从终端运行我的 PySpark 程序 - 但是,我的 IDE(PyCharm)仍然出现相同的错误,直到我全局更改了 JAVA_HOME 变量。
要更新变量,首先通过在命令行上运行来检查您使用的是Z shell(可执行文件zsh
)还是Bash shell。对于 Z shell,您将编辑$HOME/.zshenv文件;对于 Bash,您将编辑$HOME/.bash_profile文件。echo $SHELL
# Open the file
vim ~/.zshenv
# Or
vim ~/.bash_profile
# Once inside the file, set the variable with your Java path, then save and close the file
export JAVA_HOME=/usr/local/opt/openjdk@11
# Test if it was set successfully
echo $JAVA_HOME
输出:
/usr/local/opt/openjdk@11
完成此步骤后,我也可以通过 PyCharm IDE 运行 PySpark。
解决方案 7:
我在 Linux(ubuntu)上使用 iphython 笔记本(IPython 3.2.1)时遇到了同样的问题。
在我的案例中缺少的是在 $PYSPARK_SUBMIT_ARGS 环境中设置主 URL,如下所示(假设您使用 bash):
export PYSPARK_SUBMIT_ARGS="--master spark://<host>:<port>"
例如
export PYSPARK_SUBMIT_ARGS="--master spark://192.168.2.40:7077"
您可以将其放入 .bashrc 文件中。您会在日志中获得 spark master 的正确 URL(当您使用 /sbin/start_master.sh 启动 master 时会报告此日志的位置)。
解决方案 8:
在花费数小时尝试了许多不同的解决方案后,我可以确认 Java 10 SDK 导致了此错误。在 Mac 上,请导航到 /Library/Java/JavaVirtualMachines,然后运行此命令以完全卸载 Java JDK 10:
sudo rm -rf jdk-10.jdk/
之后,请下载JDK 8,问题就会解决。
解决方案 9:
这里有很多有价值的提示,但是,没有一个能完全解决我的问题,所以我将展示在Windows上的Anaconda Jupyter Notebook中工作时有用的过程:
下载并安装java和pyspark到没有空格的目录中。
[可能不必要] 在 anaconda 提示符中,键入
where conda
和where python
,并使用 Windows 环境变量工具将 .exe 文件目录的路径添加到 Path 变量中。同时添加变量JAVA_HOME
和SPARK_HOME
它们相应的路径。即使这样做,我仍然必须在 Notebook 中手动设置这些变量以及
PYSPARK_SUBMIT_ARGS
(使用您自己的路径SPARK_HOME
和JAVA_HOME
):
import os
os.environ["SPARK_HOME"] = r"C:Sparkspark-3.2.0-bin-hadoop3.2"
os.environ["PYSPARK_SUBMIT_ARGS"] = "--master local[3] pyspark-shell"
os.environ["JAVA_HOME"] = r"C:Javajre1.8.0_311"
使用 从笔记本中安装 findspark
!pip install findspark
。运行
import findspark
并findspark.init()
运行
from pyspark.sql import SparkSession
并spark = SparkSession.builder.getOrCreate()
一些有用的链接:
https://towardsdatascience.com/installing-apache-pyspark-on-windows-10-f5f0c506bea1
https://www.datacamp.com/community/tutorials/installing-anaconda-windows
解决方案 10:
Spark 对你使用的 Java 版本非常挑剔。强烈建议你使用 Java 1.8(开源的 AdoptOpenJDK 8 也很好用)。安装后,JAVA_HOME
如果你使用 Mac/Linux,请将其设置为你的 bash 变量:
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=$JAVA_HOME/bin:$PATH
解决方案 11:
Java gateway process exited......port number
尽管我设置正确,但还是出现了同样的异常PYSPARK_SUBMIT_ARGS
。我正在运行 Spark 1.6,并尝试让 pyspark 与 IPython4/Jupyter (OS:ubuntu 作为 VM 客户机) 一起工作。
当我收到此异常时,我注意到生成了一个 hs_err_*.log,它以以下内容开头:
There is insufficient memory for the Java Runtime Environment to continue. Native memory allocation (malloc) failed to allocate 715849728 bytes for committing reserved memory.
因此,我通过 VirtualBox 设置增加了分配给我的 ubuntu 的内存,并重新启动了客户机 ubuntu。然后这个Java gateway
异常就消失了,一切都正常了。
解决方案 12:
Exception: Java gateway process exited before sending the driver its port number
当我尝试在 Cloudera VM 中启动支持 CSV 的 IPython 时,遇到了同样的问题,并且出现了语法错误:
PYSPARK_DRIVER_PYTHON=ipython pyspark --packages com.databricks:spark-csv_2.10.1.4.0
会抛出错误,而:
PYSPARK_DRIVER_PYTHON=ipython pyspark --packages com.databricks:spark-csv_2.10:1.4.0
不会。
区别在于最后一个(工作)示例中的最后一个冒号,将Scala 版本号与包版本号分开。
解决方案 13:
如果你尝试在没有 hadoop 二进制文件的情况下运行 spark,你可能会遇到上述错误。一种解决方案是:
1)单独下载 hadoop。2
)将 hadoop 添加到你的 PATH
3)将 hadoop 类路径添加到你的 SPARK 安装中
前两个步骤很简单,最后一步最好通过在每个 spark 节点(主节点和工作节点)的 $SPARK_HOME/conf/spark-env.sh 中添加以下内容来完成
### in conf/spark-env.sh ###
export SPARK_DIST_CLASSPATH=$(hadoop classpath)
有关更多信息,请查看:https://spark.apache.org/docs/latest/hadoop-provided.html
解决方案 14:
当我使用 jdk-1.8 32 位切换到 64 位时出现此错误。
我收到此错误是因为 32 位 java 无法分配 spark 驱动程序所需的超过 3G 的堆内存(16G):
builder = SparkSession.builder \n .appName("Spark NLP") \n .master("local[*]") \n .config("spark.driver.memory", "16G") \n .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer") \n .config("spark.kryoserializer.buffer.max", "1000M") \n .config("spark.driver.maxResultSize", "0")
我测试将其扩大到 2G,它在 32 位下也能正常工作。
解决方案 15:
在花了很长时间解决这个问题后,我终于解决了这个问题。我拥有macOS v10.15 (Catalina),在Anaconda环境中使用 PyCharm 。
Spark 目前仅支持Java 8。如果通过命令行安装 Java,它将默认安装最新的Java 10(或更高版本),并会导致各种问题。要解决此问题,请按照以下步骤操作 -
1. Make sure you have Homebrew, else install Homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
2. Install X-code
xcode-select –-install
3. Install Java8 through the official website (not through terminal)
https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
4. Install Apache-Spark
brew install apache-spark
5. Install Pyspark and Findspark (if you have anaconda)
conda install -c conda-forge findspark
conda install -c conda-forge/label/gcc7 findspark
conda install -c conda-forge pyspark
瞧!这应该可以让你顺利运行 PySpark。
解决方案 16:
我收到此错误是因为磁盘空间不足。
解决方案 17:
在我的情况下,这个错误出现在之前运行良好的脚本中。所以我想这可能是由于我的 JAVA 更新。之前我使用的是 java 1.8,但我意外地更新到了 java 1.9。当我切换回 java 1.8 时,错误消失了,一切都运行正常。对于那些因为同样的原因收到此错误但不知道如何在 ubuntu 上切换回旧 java 版本的人:运行
sudo update-alternatives --config java
并选择 Java 版本
解决方案 18:
我在 Windows 系统中找到了问题所在。Java 的安装目录路径中不能有空格,例如C:Program Files
。我在 中重新安装了 Java CJava
。我将其设置JAVA_HOME
为C:Java
,问题就解决了。
解决方案 19:
对于存在 JAVA_HOME 问题的 Linux(Ubuntu 18.04),关键是将其指向主文件夹:
通过以下方式将 Java 8 设置为默认值
sudo update-alternatives --config java
: 。如果未安装 Jave 8,请通过以下方式安装:sudo apt install openjdk-8-jdk
。将环境变量设置
JAVA_HOME
为master java 8 文件夹。位置由上面删除的第一个命令给出jre/bin/java
。即:export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/"
。如果在命令行上完成,这将仅与当前会话相关(参考:Linux 上的导出命令)。验证:echo $JAVA_HOME
。为了永久设置,请将上面的粗体行添加到在启动 IDE/Jupyter/python 解释器之前运行的文件中。这可以通过将上面的粗体行添加到来实现
.bashrc
。此文件在以交互方式启动 bash 时加载ref: .bashrc
解决方案 20:
遇到了同样的问题,安装 Java 后使用下面的方法解决了该问题!
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
解决方案 21:
我也有同样的错误。
我的故障排除程序是:
查看 Spark 源代码。
按照错误信息操作。在我的例子中:
pyspark/java_gateway.py
,第 93 行,在launch_gateway
。检查代码逻辑,找到根本原因,然后解决它。
就我而言,问题是 PySpark 没有权限创建一些临时目录,因此我只能使用 sudo 运行我的 IDE
解决方案 22:
我在 pycharm 中运行 pyspark 时遇到同样的错误。我通过在 pycharm 的环境变量中添加 JAVA_HOME 解决了该问题。
解决方案 23:
就我而言,这是因为我写的SPARK_DRIVER_MEMORY=10
不是SPARK_DRIVER_MEMORY=10g
`spark-env.sh`
解决方案 24:
我遇到了同样的异常,我尝试了所有方法,设置并重置了所有环境变量。但问题最终归结于 spark 会话的 appname 属性中的空格,即“SparkSession.builder.appName("StreamingDemo").getOrCreate()”。在从 appname 属性的字符串中删除空格后,问题立即得到解决。我在 windows 10 环境中将 pyspark 2.7 与 eclipse 一起使用。它对我有用。附件是所需的屏幕截图。
解决方案 25:
发生此错误是因为机器上未安装 JAVA。Spark 是用 scala 开发的,而 scala 通常在 JAVA 上运行。
尝试安装 JAVA 并执行 pyspark 语句。它将起作用
解决方案 26:
如果您的机器上没有安装 Java,通常会发生这种情况。
转到命令提示符并检查你的 java 版本:输入:java -version
你应该得到这样的输出
`java version "1.8.0_241"
Java(TM) SE Runtime Environment (build 1.8.0_241-b07)
Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)`
如果没有,请转到 orcale 并下载 jdk。查看此视频了解如何下载 java 并将其添加到 buildpath。
https://www.youtube.com/watch?v=f7rT0h1Q5Wo
解决方案 27:
背景
SPARK_HOME/python/lib/pyspark.zip/pyspark/java_gateway.py中出现错误“Java 网关进程在向驱动程序发送其端口号之前退出” 。
if not on_windows:
# Don't send Ctrl + C / SIGINT to the Java gateway:
def preexec_func():
signal.signal(signal.SIGINT, signal.SIG_IGN)
popen_kwargs["preexec_fn"] = preexec_func
proc = Popen(command, **popen_kwargs)
else:
# preexec_fn is not supported on Windows
proc = Popen(command, **popen_kwargs)
# Wait for the file to appear, or for the process
# to exit, whichever happens first.
while not proc.poll() and not os.path.isfile(conn_info_file):
time.sleep(0.1)
if not os.path.isfile(conn_info_file):
raise RuntimeError("Java gateway process exited before sending its port number") # <-----
代码模拟了这个PySpark进程调用来测试PySpark是否已经启动。
测试
运行下面的代码以确保调用 PySpark。这特定于在Apple silicon上使用Homebrew安装的Spark,但这个想法和方法也适用于其他平台。Spark 版本是 3.3.1。我需要相应地更改版本。
import os
import shutil
import signal
import sys
import tempfile
import time
from subprocess import Popen, PIPE
# --------------------------------------------------------------------------------
# Constant
# --------------------------------------------------------------------------------
SPARK_HOME = "/opt/homebrew/Cellar/apache-spark/3.3.1/libexec"
JAVA_HOME = '/opt/homebrew/opt/openjdk'
# --------------------------------------------------------------------------------
# Environment Variables
# NOTE:
# SPARK_HOME must be set to /opt/homebrew/Cellar/apache-spark/3.3.1/libexec",
# NOT /opt/homebrew/Cellar/apache-spark/3.3.1".
# Otherwise Java gateway process exited before sending its port number in java_gateway.py
# --------------------------------------------------------------------------------
os.environ['SPARK_HOME'] = SPARK_HOME
os.environ['JAVA_HOME'] = JAVA_HOME
sys.path.extend([
f"{SPARK_HOME}/python/lib/py4j-0.10.9.5-src.zip",
f"{SPARK_HOME}/python/lib/pyspark.zip",
])
# --------------------------------------------------------------------------------
# PySpark Modules
# --------------------------------------------------------------------------------
from pyspark.serializers import read_int, UTF8Deserializer
# --------------------------------------------------------------------------------
#
# --------------------------------------------------------------------------------
def preexec_func():
signal.signal(signal.SIGINT, signal.SIG_IGN)
def run_pyspark():
"""
"""
pyspark_command = [f'{SPARK_HOME}/bin/spark-submit', '--master', 'local[*]', 'pyspark-shell']
# Create a temporary directory where the gateway server should write the connection
# information.
proc = None
conn_info_dir = tempfile.mkdtemp()
try:
fd, conn_info_file = tempfile.mkstemp(dir=conn_info_dir)
os.close(fd)
os.unlink(conn_info_file)
env = dict(os.environ)
env["_PYSPARK_DRIVER_CONN_INFO_PATH"] = conn_info_file
# Launch the Java gateway.
popen_kwargs = {"stdin": PIPE, "env": env}
# We open a pipe to standard input, so that the Java gateway can die when the pipe is broken
# We always set the necessary environment variables.
print(f"
run pyspark command line {pyspark_command}")
popen_kwargs["preexec_fn"] = preexec_func
proc = Popen(pyspark_command, **popen_kwargs)
# Wait for the file to appear, or for the process to exit, whichever happens first.
count: int = 5
while not proc.poll() and not os.path.isfile(conn_info_file):
print("waiting for PySpark to start...")
count -= 1
if count < 0:
break
time.sleep(1)
if not os.path.isfile(conn_info_file):
raise RuntimeError("Java gateway process exited before sending its port number")
with open(conn_info_file, "rb") as info:
gateway_port = read_int(info)
gateway_secret = UTF8Deserializer().loads(info)
out, err = proc.communicate()
print("-"*80)
print(f"PySpark started with pid {proc.pid}")
print(f"spark process port {gateway_port}")
finally:
shutil.rmtree(conn_info_dir)
if proc:
proc.kill()
def test():
run_pyspark()
if __name__ == "__main__":
test()
环境
macOS v13.0.1(文图拉)
Python 3.9.13
PySpark 3.3.1
OpenJDK 运行环境(AdoptOpenJDK)(build 1.8.0_292-b10)
Scala 代码运行器版本 3.2.2
已使用 brew 安装了 JDK 8、Scala 和 Spark。
brew install --cask adoptopenjdk8
brew install scala
brew install apache-spark
Java 进程可以在系统偏好设置→安全→防火墙中获取传入连接。
参考
PySpark 安装
确保 SPARK_HOME 环境变量指向已提取 tar 文件的目录。更新 PYTHONPATH 环境变量,以便它可以在SPARK_HOME/python/lib下找到 PySpark 和 Py4J 。下面显示了执行此操作的一个示例:
解决方案 28:
此错误表示PySpark 与 Java 版本不匹配。两者不兼容。请参阅下面的兼容性矩阵。
PySpark Version Min Java Version
-------------------------------------
2.0.x - 2.2.x Java 7
2.3.x - 2.4.x Java 8
3.0.x - 3.1.x Java 8
3.2.x Java 11
检查你的 Java 版本。如果是 17,那么你至少需要 PySpark 3.3。因此请升级 PySpark。
解决方案 29:
我遇到了这个问题,其实不是由于 JAVE_HOME 设置。我假设你使用的是 Windows,并使用Anaconda作为你的 Python 工具。请检查你是否可以使用命令提示符。由于[cmd 2崩溃,我无法运行 Spark 。修复此问题后,Spark 可以在我的电脑上正常工作。
解决方案 30:
步骤 1:
从终端检查 Java 版本。
java -version
如果您看到bash: java: command not found
,则表示您的系统中没有安装 Java。
第 2 步:
使用以下命令安装 Java,
sudo apt-get install default-jdk
步骤3:
现在检查 Java 版本,您将看到该版本已下载。
java -version
结果:
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)
步骤4:
现在运行 PySpark 代码,您将不会再看到这样的错误。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)