【Elasticsearch运维系列】Elasticsearch7.12.1启动指定版本JDK:你学废了吗?

一、背景
一套生ES集群,版本为7.12.1,近期频繁告警,频繁出现索引分片异常,索引状态异常,导致应用无法正常写入ES,另外,也经常出现节点掉问题。通过分析相关ES日志,显示和当前JAVA GC有关。当前ES曾经过升级,从ES 6升级到ES 7,在ES应用服务器上除了部署ES本身,还部署运行了其它应用进程。为了方便JAVA管理,ES和其它应用都使用了相同的JDK,版本为1.8.0_202。而ES 7 在启动时会提示建议使用 JDK 11及以后版本。因为采用JDK 1.8,GC垃圾回收还采用的是CMS方式,如下所示:
[root@localhost ~]# java -XX:+PrintFlagsFinal -version | grep Use.*GC.*=.*truebool UseAdaptiveSizeDecayMajorGCCost = true {product}bool UseGCOverheadLimit = true {product}bool UseMaximumCompactionOnSystemGC = true {product}bool UseParallelGC := true {product}bool UseParallelOldGC = true {product}
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)当前JDK 1.8的JAVA_HOME是配置在/etc/profile文件中,作为全局变量使用的,如下所示:
export JAVA_HOME=/usr/java/jdk1.8.0_221
export ES_JAVA_HOME=/usr/java/jdk1.8.0_221
export PATH=$ES_JAVA_HOME/bin:$PATH
export PATH=$JAVA_HOME/bin:$PATH
export JAVA8_HOME=/opt/lib/jdk1.8.0_202
CLASSPATH=/opt/lib/jdk1.8.0_202/lib
JDK_HOME=/opt/lib/jdk1.8.0_202
JAVA_HOME=/opt/lib/jdk1.8.0_202
PATH=/opt/lib/jdk1.8.0_202/bin:/opt/lib/jdk1.8.0_202/jre/bin:$PATH
export CLASSPATH JAVA_HOME JDK_HOME PATH
在和研发等相关人员分析沟通近期问题后,建议将ES采用自带的JDK,并将GC垃圾回收调整为 G1GC,并且对JDK的调整不能影响到当前ES服务器上的其它应用对原JDK 1.8版本的使用。当前ES启动后信息如下:

二、测试过程
如何调整ES使用指定的JDK版本启动呢,我查询了相关资料,显示可以修改/bin/elasticsearch文件,在该文件中加入指定的JAVA_HOME,于是按照相关文档,我在测试环境上进行了验证,测试环境版本和生产版本相同。为方便修改和管理,我将ES自带的JDK目录拷贝到了当前用户家目录下,如下所示:
cp -a /home/esuser/deploy/elasticsearch-7.12.1-9300/jdk /home/esuser/OpenJDK16
对 /home/esuser/deploy/elasticsearch-7.12.1-9300/bin/elasticsearch文件进行修改,修改后的内容如下:
#!/bin/bash#配置指定的JDK
export ES_HOME=/home/esuser/OpenJDK16
export PATH=$ES_HOME/bin:$PATH#添加对JDK的判断
if [ -x "$ES_HOME/bin/java" ]; thenJAVA="/home/esuser/jdk/bin/java"
elseJAVA=`which java`
fisource "`dirname "$0"`"/elasticsearch-envCHECK_KEYSTORE=truepwd
DAEMONIZE=false
for option in "$@"; docase "$option" in-h|--help|-V|--version)CHECK_KEYSTORE=false;;-d|--daemonize)DAEMONIZE=true;;esac
doneif [ -z "$ES_TMPDIR" ]; thenES_TMPDIR=`"$JAVA" "$XSHARE" -cp "$ES_CLASSPATH" org.elasticsearch.tools.launchers.TempDirectory`
fiunset KEYSTORE_PASSWORD
KEYSTORE_PASSWORD=
if [[ $CHECK_KEYSTORE = true ]] \&& bin/elasticsearch-keystore has-passwd --silent
thenif ! read -s -r -p "Elasticsearch keystore password: " KEYSTORE_PASSWORD ; thenecho "Failed to read keystore password on console" 1>&2exit 1fi
fi# 确保传递正确的两个参数到JvmOptionsParser
ES_JAVA_OPTS=`export ES_TMPDIR; "$JAVA" "$XSHARE" -cp "$ES_CLASSPATH" org.elasticsearch.tools.launchers.JvmOptionsParser "$ES_PATH_CONF" "$ES_HOME/plugins"`# manual parsing to find out, if process should be detached
if [[ $DAEMONIZE = false ]]; thenexec \"$JAVA" \"$XSHARE" \$ES_JAVA_OPTS \-Des.path.home="$ES_HOME" \-Des.path.conf="$ES_PATH_CONF" \-Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \-Des.distribution.type="$ES_DISTRIBUTION_TYPE" \-Des.bundled_jdk="$ES_BUNDLED_JDK" \-cp "$ES_CLASSPATH" \org.elasticsearch.bootstrap.Elasticsearch \"$@" <<<"$KEYSTORE_PASSWORD"
elseexec \"$JAVA" \"$XSHARE" \$ES_JAVA_OPTS \-Des.path.home="$ES_HOME" \-Des.path.conf="$ES_PATH_CONF" \-Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \-Des.distribution.type="$ES_DISTRIBUTION_TYPE" \-Des.bundled_jdk="$ES_BUNDLED_JDK" \-cp "$ES_CLASSPATH" \org.elasticsearch.bootstrap.Elasticsearch \"$@" \<<<"$KEYSTORE_PASSWORD" &retval=$?pid=$![ $retval -eq 0 ] || exit $retvalif [ ! -z "$ES_STARTUP_SLEEP_TIME" ]; thensleep $ES_STARTUP_SLEEP_TIMEfiif ! ps -p $pid > /dev/null ; thenexit 1fiexit 0
fiexit $?
修改后,我在前台测试启动elasticsearch,elasticsearch启动后,我通过ps -ef|grep java查看了ES所使用的jdk,发现并未使用/home/esuser/jdk/bin/java,而是使用了 /home/esuser/deploy/elasticsearch-7.12.1-9300/jdk/bin/java,不符合我的要求,如下所示:
[esuser@xsky-node1 ~]$ ps -ef|grep java
esuser 8570 31437 99 16:10 pts/0 00:01:21 /home/esuser/deploy/elasticsearch-7.12.1-9300/jdk/bin/java -Xshare:auto -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -XX:+ShowCodeDetailsInExceptionMessages -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dio.netty.allocator.numDirectArenas=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.locale.providers=SPI,COMPAT --add-opens=java.base/java.io=ALL-UNNAMED -Xms8g -Xmx8g -XX:+UseG1GC -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.io.tmpdir=/tmp/elasticsearch-1427292610172092269 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=data -XX:ErrorFile=logs/hs_err_pid%p.log -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m -Djava.locale.providers=COMPAT -XX:UseAVX=2 -XX:MaxDirectMemorySize=4294967296 -XX:InitiatingHeapOccupancyPercent=30 -XX:G1ReservePercent=25 -Des.path.home=/home/esuser/deploy/elasticsearch-7.12.1-9300 -Des.path.conf=/home/esuser/deploy/elasticsearch-7.12.1-9300/config -Des.distribution.flavor=default -Des.distribution.type=tar -Des.bundled_jdk=true -cp /home/esuser/deploy/elasticsearch-7.12.1-9300/lib/* org.elasticsearch.bootstrap.Elasticsearch
根据脚本分析,发现是对环境变量 ES_HOME 的误用,在 Elasticsearch 中,ES_HOME 通常用来指代 Elasticsearch 的安装目录,而不是 JDK 的安装目录。应该使用 ES_JAVA_HOME 来设置 JDK 路径。这应该是导致 JDK 路径不正确的原因。然后我对上面脚本进行了调整,使用 ES_JAVA_HOME 来设置 JDK 路径。
并确保所有引用 JDK 的地方都使用 ES_JAVA_HOME 而非 ES_HOME。
修改后的bin/elasticsearch脚本如下:
#!/bin/bash#配置指定的JDK
export ES_JAVA_HOME=/home/esuser/OpenJDK16
export PATH=$ES_JAVA_HOME/bin:$PATH#添加对JDK判断
if [ -x "$ES_JAVA_HOME/bin/java" ]; thenJAVA="$ES_JAVA_HOME/bin/java"
elseJAVA=`which java`
fisource "`dirname "$0"`"/elasticsearch-envCHECK_KEYSTORE=true
DAEMONIZE=false
for option in "$@"; docase "$option" in-h|--help|-V|--version)CHECK_KEYSTORE=false;;-d|--daemonize)DAEMONIZE=true;;esac
doneif [ -z "$ES_TMPDIR" ]; thenES_TMPDIR=`"$JAVA" "$XSHARE" -cp "$ES_CLASSPATH" org.elasticsearch.tools.launchers.TempDirectory`
fiunset KEYSTORE_PASSWORD
KEYSTORE_PASSWORD=
if [[ $CHECK_KEYSTORE = true ]] \&& bin/elasticsearch-keystore has-passwd --silent
thenif ! read -s -r -p "Elasticsearch keystore password: " KEYSTORE_PASSWORD ; thenecho "Failed to read keystore password on console" 1>&2exit 1fi
fi# 确保传递正确的两个参数到JvmOptionsParser
ES_JAVA_OPTS=`export ES_TMPDIR; "$JAVA" "$XSHARE" -cp "$ES_CLASSPATH" org.elasticsearch.tools.launchers.JvmOptionsParser "$ES_PATH_CONF" "$ES_HOME/plugins"`# manual parsing to find out, if process should be detached
if [[ $DAEMONIZE = false ]]; thenexec \"$JAVA" \"$XSHARE" \$ES_JAVA_OPTS \-Des.path.home="$ES_HOME" \-Des.path.conf="$ES_PATH_CONF" \-Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \-Des.distribution.type="$ES_DISTRIBUTION_TYPE" \-Des.bundled_jdk="$ES_BUNDLED_JDK" \-cp "$ES_CLASSPATH" \org.elasticsearch.bootstrap.Elasticsearch \"$@" <<<"$KEYSTORE_PASSWORD"
elseexec \"$JAVA" \"$XSHARE" \$ES_JAVA_OPTS \-Des.path.home="$ES_HOME" \-Des.path.conf="$ES_PATH_CONF" \-Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \-Des.distribution.type="$ES_DISTRIBUTION_TYPE" \-Des.bundled_jdk="$ES_BUNDLED_JDK" \-cp "$ES_CLASSPATH" \org.elasticsearch.bootstrap.Elasticsearch \"$@" \<<<"$KEYSTORE_PASSWORD" &retval=$?pid=$![ $retval -eq 0 ] || exit $retvalif [ ! -z "$ES_STARTUP_SLEEP_TIME" ]; thensleep $ES_STARTUP_SLEEP_TIMEfiif ! ps -p $pid > /dev/null ; thenexit 1fiexit 0
fiexit $?
修改上述脚本后,我再次前台启动elasticsearch验证测试,发现这次采用了我指定的JDK目录 /home/esuser/OpenJDK16,如下所示,红色箭头指向的是其中一个ES节点使用了指定的JDK目录,另一个节点使用了/etc/profile中配置的JAVA_HOME目录

在测试过程中,我对/home/esuser/deploy/elasticsearch-7.12.1-9300/config/下的GC参赛进行了调整,修改了如下参数:
# -XX:+UseConcMarkSweepGC
# -XX:CMSInitiatingOccupancyFraction=75
# -XX:+UseCMSInitiatingOccupancyOnly
将上述参数进行了注释,
然后添加 -XX:+UseG1GC通过如下命令 /home/esuser/OpenJDK16/bin/java -XX:+PrintFlagsFinal -version | grep Use.*GC.*=.*true,查看到的信息如下:
[esuser@xsky-node1 deploy]$ /home/esuser/OpenJDK16/bin/java -XX:+PrintFlagsFinal -version | grep Use.*GC.*=.*truebool UseAdaptiveSizeDecayMajorGCCost = true {product} {default}bool UseDynamicNumberOfGCThreads = true {product} {default}bool UseG1GC = true {product} {ergonomic}bool UseGCOverheadLimit = true {product} {default}bool UseMaximumCompactionOnSystemGC = true {product} {default}
openjdk version "16" 2021-03-16
OpenJDK Runtime Environment AdoptOpenJDK (build 16+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 16+36, mixed mode, sharing)

和修改前对比,显示已经使用了G1GC垃圾回收,接下来就是要在生产环境上进行验证测试。
{default}
openjdk version “16” 2021-03-16
OpenJDK Runtime Environment AdoptOpenJDK (build 16+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 16+36, mixed mode, sharing)
和修改前对比,显示已经使用了G1GC垃圾回收,接下来就是要在生产环境上进行验证测试。相关文章:
【Elasticsearch运维系列】Elasticsearch7.12.1启动指定版本JDK:你学废了吗?
一、背景 一套生ES集群,版本为7.12.1,近期频繁告警,频繁出现索引分片异常,索引状态异常,导致应用无法正常写入ES,另外,也经常出现节点掉问题。通过分析相关ES日志,显示和当前JAVA G…...
思通数科大模型在智能数据查询系统中的深度应用:销售数据分析的革新
在企业决策支持系统中,销售数据分析占据着举足轻重的地位。思通数科的大模型技术,结合自然语言处理(NLP)和机器学习,为智能数据查询系统提供了强大的分析能力。本文将详细描述思通数科大模型在销售数据分析中的应用&am…...
上位机图像处理和嵌入式模块部署(树莓派4b和qt应用全屏占有)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 我们都知道,嵌入式应用一般都是为了某一个特定应用而存在的。也就是说,和pc不同,这个嵌入式板子一般都是为了解…...
QT:QT窗口(一)
文章目录 菜单栏创建菜单栏在菜单栏中添加菜单创建菜单项添加分割线 工具栏创建工具栏设置停靠位置创建工具栏的同时指定停靠位置使用QToolBar类提供的setAllowedAreas函数来设置停靠位置 设置浮动属性设置移动属性 状态栏状态栏的创建在状态栏中显示实时消息在状态栏中显示永久…...
matlab例题大全
1.第1章 MATLAB系统环境 1.1 注:plot函数为画图函数。例plot(x1,y1,:,x2,y2,*); 1.2 注:root为求根函数。p为方程变量前面系数矩阵。 1.3 注: 2*x3y-1*z 2; 8*x2*y3*z 4; 45*x3*y9*z 23 求:x,y,z的…...
SwiGLU激活函数
SwiGLU激活函数已经成为LLM的标配了。它是GLU的变体,公式如下: SwiGLU ( x , W , V , b , c , β ) Swish β ( x W b ) ⊗ ( x V c ) \operatorname{SwiGLU}(x, W, V, b, c, \beta)\operatorname{Swish}_\beta(x Wb) \otimes(x Vc) SwiGLU(x,…...
MySQL慢查询优化
当需要优化MySQL的慢查询时,通常需要结合多个方面进行分析和优化,包括索引优化、SQL语句重构、数据库结构调整等。下面,我将通过一个例子来说明如何优化MySQL的慢查询,包括多表关联和条件查询。 假设我们有一个简化的电子商务系统…...
开源数据可视化大屏对接表单数据实践!
如果你需要一个表单系统,进行数据收集;可以使用tduck填鸭进行私有化部署,进行表单制作,完成数据收集。 在实际业务中,往往需要将收集的数据进行展示或分析;此时就可以使用表单数据推送到TReport中…...
08.图形化界面字体问题处理
图形化界面字体问题处理 发现图形存在乱码,不显示文字 zabbix服务器的字符集所在的路径下: /usr/share/zabbix/assets/fonts 将本地windows系统的字体进行上传,选择一个自己喜欢的字体 上传到系统路径下并且直接覆盖掉 回到web浏览器界面…...
【代码随想录算法训练营第37期 第二天 | LeetCode977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II】
代码随想录算法训练营第37期 第二天 | LeetCode977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II 一、977.有序数组的平方 解题代码C: class Solution { public:vector<int> sortedSquares(vector<int>& nums) {int len nums.size();fo…...
Java:Servlet详解
目录 一、什么是Servlet 二、Servlet原理 Servlet的生命周期 三、 Servlet注释 WebServlet 一、什么是Servlet Servlet是JavaWeb开发的一种技术,Servlet程序需要部署在Servlet容器(服务端)中才能运行,常见的Servlet容器有Tom…...
Oracle存储过程怎么定义类并继承
在Oracle数据库中,存储过程(Stored Procedure)是用于执行特定功能的预编译的SQL代码块。然而,Oracle的存储过程并不直接支持面向对象的编程概念,如类(Class)和继承(Inheritance&…...
14_Scala面向对象编程_属性
文章目录 属性1.类中属性声明2.系统默认赋值3.BeanProperty4.整体代码如下 属性 1.类中属性声明 // 1.给Scala声明属性;var name :String "zhangsan"val age :Int 302.系统默认赋值 scala由于初始化变量必须赋值,为了解决此问题可以采…...
什么是网页反作弊
在搜索引擎技术中,网页反作弊是指一种防止网页排名被恶意操纵的技术。搜索引擎会根据特定的算法来评估网页的相关性和质量,以决定其在搜索结果中的排名。然而,有些人可能会尝试通过各种不正当的手段来提高自己网页的排名,这被称为…...
MAVEN打包JAR启动执行manifest
当您使用Maven进行项目打包,特别是需要创建一个可执行的JAR文件时,确保JAR文件的MANIFEST.MF中包含正确的Main-Class属性是非常重要的。这个属性告诉Java运行时环境哪个类包含main方法,作为应用程序的入口点。 如果您发现生成的JAR文件不包含…...
JavaEE 多线程详细讲解(1)
1.线程是什么 (shift F6)改类名 1.1.并发编程是什么 (1)当前的CPU,都是多核心CPU (2)需要一些特定的编程技巧,把要完成的仍无,拆解成多个部分,并且分别让…...
数据分析从入门到精通 1.numpy剑客修炼
会在某一瞬间突然明白,有些牢笼是自己给自己的 —— 24.5.5 一、数据分析秘笈介绍 1.什么是数据分析 是把隐藏在一些看似杂乱无章的数据背后的信息提炼出来,总结出所研究对象的内在规律。使得数据的价值最大化 案例: 分析用户的消…...
【iOS】KVO
文章目录 前言一、KVO使用1.基本使用2.context使用3.移除KVO通知的必要性4.KVO观察可变数组 二、代码调试探索1.KVO对属性观察2.中间类3.中间类的方法3.dealloc中移除观察者后,isa指向是谁,以及中间类是否会销毁?总结 三、KVO本质GNUStep窥探…...
python json字符串怎么用format方法填充参数值报KeyError
python json字符串怎么用format方法填充参数值报KeyError 需求问题分析解决方案 需求 因为python中的字典和json中的一些变量有差异,比如:json中有null、true,在python中就不会被识别,只能转换成字符串,在通过loads()…...
C++新手村指南:入门基础
目录 C概念 C发展史 C关键字(C98) 命名空间 命名空间的定义 命名空间的使用 C中的输入&&输出 缺省参数 缺省参数的概念 缺省参数的分类 函数重载 函数重载概念 函数重载实现 引用 引用的概念 引用的特性 常引用 引用的使用场景…...
基于STM32CubeMx与DSP库的实时音频信号FFT频谱分析实战
1. 从零搭建音频频谱分析系统 去年我在做一个智能家居项目时,需要实时监测环境中的特定声音频率。当时尝试了多种方案,最终发现基于STM32的FFT分析是最经济高效的解决方案。整个过程就像搭积木一样有趣,下面我就把完整的搭建过程分享给大家。…...
ISE 14.7 最后的倔强:手把手教你给CPLD烧录程序(附JTAG连接避坑指南)
ISE 14.7 最后的倔强:手把手教你给CPLD烧录程序(附JTAG连接避坑指南) 在Vivado大行其道的今天,ISE 14.7这个"老古董"依然活跃在某些工程师的工作站上。这不是技术怀旧,而是实实在在的工程需求——当你面对一…...
手把手教你用RK3568 DIY一个6网口的AI工业网关(附Ubuntu系统配置避坑指南)
从零构建RK3568六网口AI网关:硬件选型与Ubuntu系统调优实战 在工业物联网和边缘计算领域,多网口网关设备正成为连接现场设备与云端系统的关键枢纽。RK3568凭借其强大的处理能力和丰富的接口资源,为DIY爱好者提供了极具性价比的开发平台。本文…...
基于Matrix的AI助手baibot:多模型集成与隐私优先部署指南
1. 项目概述:一个为Matrix而生的全能AI助手如果你和我一样,既是Matrix去中心化通信的忠实用户,又对当前各种AI大模型的能力感到兴奋,那么你很可能一直在寻找一个能将两者无缝结合的工具。市面上确实有一些方案,比如基于…...
AI智能体编排框架实战:构建具备记忆与协作能力的智能系统
1. 项目概述:当AI智能体需要“记忆”与“协作”在AI智能体开发领域,我们常常面临一个核心挑战:如何让智能体不仅能在单次对话中表现出色,还能记住历史、规划未来,并与其他智能体协同工作?这就像组建一支足球…...
NCE外汇:平台稳定性与用户体验的全面观察
金融服务行业的复杂性决定了平台需要在多个维度上同时具备较高的水准。NCE外汇经过多年的发展,已经在合规、技术、服务、教育等方面形成了一套相互支撑的体系。本文从评测视角出发,对其综合实力进行多维度的解读,呈现一个具有结构感的平台画像…...
第70篇:Vibe Coding时代:AI Coding 平台运维手册,解决 Agent 上线后故障排查没有 SOP 的问题
第70篇:Vibe Coding时代:AI Coding 平台运维手册,解决 Agent 上线后故障排查没有 SOP 的问题 一、问题场景:Agent 平台上线了,但一出问题大家都不知道怎么查 当 AI Coding Agent 进入团队使用后,常见故障会越来越多: 1. 用户说任务一直不动 2. 任务状态卡在 RUNNING 3…...
开发容器实战:用Dev Containers统一团队开发环境,告别配置地狱
1. 项目概述与核心价值 最近在折腾一个跨平台协作的项目,团队里有人用 Mac,有人用 Windows,还有人用 Linux 桌面,开发环境配置起来简直是“八仙过海,各显神通”,但结果往往是“一地鸡毛”。一个依赖版本不…...
深耕高性价比多模型聚合平台赛道,这些企业值得重点关注
随着AI大模型的普及,单一模型的适配局限、高成本问题逐渐凸显,多模型聚合平台成为企业降本增效的核心选择。行业报告显示,近6个月国内多模型聚合平台的企业付费用户增速超40%,其中高性价比赛道更是成为竞争焦点。一、高性价比的核…...
把旧笔记本变成第二台电脑的“上网卡”:Win10/11网络共享实战指南
旧笔记本秒变网络共享中心:Windows ICS高阶配置指南 你是否遇到过这样的场景:书房里的台式机没有无线网卡,而客厅的旧笔记本却闲置着?其实只需一根网线,就能让这台"退役"设备重新上岗,成为全屋网…...
