TOMCAT
参考:http://blog.oldboyedu.com/java-tomcat/
一,Tomcat简介
Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。
Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。
Tomcat和Nginx、Apache(httpd)、lighttpd等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Nginx/Apache服务器。
二,安装TOMCAT
先安装java环境
上传jar包jdk-8u60-linux-x64.tar.gz apache-tomcat-8.0.27.tar.gz
配置java环境
1 2 3 4 5 6 | tar - xf jdk - 8u60 - linux - x64.tar.gz mv jdk1. 8.0_60 / / application / cd / application / mv jdk1. 8.0_60 / jdk sed - i.ori '$a export JAVA_HOME=/application/jdk\nexport PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH\nexport CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar' / etc / profile source / etc / profile |
验证
1 | java - version |
解压配置tomcat
1 2 3 4 | tar - xf apache - tomcat - 8.0 . 27.tar .gz mv apache - tomcat - 8.0 . 27 / application / tomcat echo 'export TOMCAT_HOME=/application/tomcat' >> / etc / profile source / etc / profile |
三,TOMCAT目录介绍
1 | tree tomcat / - L 1 |
1 2 3 4 5 6 7 8 9 10 11 12 | tomcat / ├── bin #启动关闭 sh是linux系统下使用的 bat是window下使用的 ├── conf #配置文件 ├── lib #存放web应用能访问的JAR包 ├── LICENSE ├── logs #日志文件 ├── NOTICE ├── RELEASE - NOTES ├── RUNNING.txt ├── temp #临时文件 ├── webapps #Web应用程序跟目录 └── work #用以产生由JSP编译出的Servlet的.java和.class文件 |
web程序跟目录
1 2 3 4 5 | drwxr - xr - x. 14 root root 4096 4 月 28 06 : 56 docs #tomcat帮助文档 drwxr - xr - x. 6 root root 83 4 月 28 06 : 56 examples #web应用实例 drwxr - xr - x. 5 root root 87 4 月 28 06 : 56 host - manager #管理 drwxr - xr - x. 5 root root 103 4 月 28 06 : 56 manager #管理 drwxr - xr - x. 3 root root 4096 4 月 28 06 : 56 ROOT #默认网站根目录 |
四,启动和关闭tomcat
1 2 | / application / tomcat / bin / startup.sh / application / tomcat / bin / shutdown.sh |
默认启动端口为8080
1 | netstat - tunlp|grep java |
通过web界面访问
默认访问的首页位置为/application/tomcat/webapps/ROOT/index.jsp
假如删除访问会出现404找不到文件提示
启动日志
1 | tail - f / application / tomcat / logs / catalina.out |
PS:catalina.out日志会每天自动切割,但是不会清除分割后的日子,运行时间久了使用重定向>清空,不要直接删除文件
localhost_access_log.2018-04-28.txt为访问日志
五,tomcat管理
测试用,生产不要用
Tomcat管理功能用于对Tomcat自身以及部署在Tomcat上的应用进行管理的web应用。在默认情况下是处于禁用状态的。如果需要开启这个功能,就需要配置管理用户,即配置前面说过的tomcat-users.xml。
点击Server Status出现页面需要输入用户名和密码
取消报401没有认证错误,并且告诉怎么配置
修改配置文件/application/tomcat/conf/tomcat-users.xml
在</tomcat-users>前面加三行
1 2 3 | <role rolename = "manager-gui" / > <role rolename = "admin-gui" / > <user username = "tomcat" password = "tomcat" roles = "manager-gui,admin-gui" / > |
重启tomcat
1 2 | / application / tomcat / bin / shutdown.sh / application / tomcat / bin / startup.sh |
然后输入用户名tomcat和密码tomcat访问
通过web端关闭应用
可以上传war包
添加虚拟主机
类似于nginx的虚拟主机,生产中不常用,因为tomcat主要处理动态请求,生产中一个tomcat就是一个站点,如果有多个站点就启动多个tomcat(一般一个tomcat分配内存1.5-2G)类似于MySQL多实例
tomcat生产优化: 把docs examples host-manager manager全部移除,ROOT目录下所有文件移除,然后在打一个tar包统一用于生产主机
六,tomcat配置文件
配置文件路径
1 | / application / tomcat / conf / server.xml |
使用本机往8005端口发送SHUTDOWN关闭tomcat
自动解压和加载
配置文件注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?xml version = '1.0' encoding = 'utf-8' ?> <! - - <Server>元素代表整个容器,是Tomcat实例的顶层元素.由org.apache.catalina.Server接口来定义.它包含一个<Service>元素.并且它不能做为任何元素的子元素. port指定Tomcat监听shutdown命令端口.终止服务器运行时,必须在Tomcat服务器所在的机器上发出shutdown命令.该属性是必须的. shutdown指定终止Tomcat服务器运行时,发给Tomcat服务器的shutdown监听端口的字符串.该属性必须设置 - - > <Server port = "8005" shutdown = "SHUTDOWN" > <Listener className = "org.apache.catalina.startup.VersionLoggerListener" / > <Listener className = "org.apache.catalina.core.AprLifecycleListener" SSLEngine = "on" / > <Listener className = "org.apache.catalina.core.JreMemoryLeakPreventionListener" / > <Listener className = "org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" / > <Listener className = "org.apache.catalina.core.ThreadLocalLeakPreventionListener" / > <GlobalNamingResources> <Resource name = "UserDatabase" auth = "Container" type = "org.apache.catalina.UserDatabase" description = "User database that can be updated and saved" factory = "org.apache.catalina.users.MemoryUserDatabaseFactory" pathname = "conf/tomcat-users.xml" / > < / GlobalNamingResources> <! - - service服务组件 - - > <Service name = "Catalina" > <! - - connector:接收用户请求,类似于httpd的listen配置监听端口. port指定服务器端要创建的端口号,并在这个端口监听来自客户端的请求。 address:指定连接器监听的地址,默认为所有地址(即 0.0 . 0.0 ) protocol连接器使用的协议,支持HTTP和AJP。AJP(Apache Jserv Protocol)专用于tomcat与apache建立通信的, 在httpd反向代理用户请求至tomcat时使用(可见Nginx反向代理时不可用AJP协议)。 minProcessors服务器启动时创建的处理请求的线程数 maxProcessors最大可以创建的处理请求的线程数 enableLookups如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址 redirectPort指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号 acceptCount指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理 connectionTimeout指定超时的时间数(以毫秒为单位) - - > <Connector port = "8080" protocol = "HTTP/1.1" connectionTimeout = "20000" redirectPort = "8443" / > <Connector port = "8009" protocol = "AJP/1.3" redirectPort = "8443" / > <! - - engine,核心容器组件,catalina引擎,负责通过connector接收用户请求,并处理请求,将请求转至对应的虚拟主机host defaultHost指定缺省的处理请求的主机名,它至少与其中的一个host元素的name属性值是一样的 - - > <Engine name = "Catalina" defaultHost = "localhost" > <! - - Realm表示存放用户名,密码及role的数据库 - - > <Realm className = "org.apache.catalina.realm.LockOutRealm" > <Realm className = "org.apache.catalina.realm.UserDatabaseRealm" resourceName = "UserDatabase" / > < / Realm> <! - - host表示一个虚拟主机 name指定主机名 appBase应用程序基本目录,即存放应用程序的目录.一般为appBase = "webapps" ,相对于CATALINA_HOME而言的,也可以写绝对路径。 unpackWARs如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接从WAR文件中运行应用程序 autoDeploy:在tomcat启动时,是否自动部署。 xmlValidation:是否启动xml的校验功能,一般xmlValidation = "false" 。 xmlNamespaceAware:检测名称空间,一般xmlNamespaceAware = "false" 。 - - > <Host name = "localhost" appBase = "webapps" unpackWARs = "true" autoDeploy = "true" > <! - - Context表示一个web应用程序,通常为WAR文件 docBase应用程序的路径或者是WAR文件存放的路径,也可以使用相对路径,起始路径为此Context所属Host中appBase定义的路径。 path表示此web应用程序的url的前缀,这样请求的url为http: / / localhost: 8080 / path / * * * * reloadable这个属性非常重要,如果为true,则tomcat会自动检测应用程序的 / WEB - INF / lib 和 / WEB - INF / classes目录的变化,自动装载新的应用程序,可以在不重启tomcat的情况下改变应用程序 - - > <Context path = " " docBase=" " debug=" " / > <Valve className = "org.apache.catalina.valves.AccessLogValve" directory = "logs" prefix = "localhost_access_log" suffix = ".txt" pattern = "%h %l %u %t " % r " %s %b" / > < / Host> < / Engine> < / Service> |
七,WEB站点部署
上线的代码有两种方式,第一种方式是直接将程序目录放在webapps目录下面,这种方式大家已经明白了,就不多说了。第二种方式是使用开发工具将程序打包成war包,然后上传到webapps目录下面。下面让我们见识一下这种方式。
上传war包
上传后会自动解压
web访问http://192.168.56.11:8080/memtest/meminfo.jsp
tomcat多实例
复制两份原tomcat
1 2 3 | cd / application / cp - a tomcat / tomcat8_1 cp - a tomcat / tomcat8_2 |
修改配置文件
1 2 3 4 5 | mkdir - p / data / www / www / ROOT cp / application / tomcat / webapps / memtest / meminfo.jsp / data / www / www / ROOT / sed - i '22s#8005#8011#;69s#8080#8081#;123s#appBase=".*"# appBase="/data/www/www"#' / application / tomcat8_1 / conf / server.xml sed - i '22s#8005#8012#;69s#8080#8082#;123s#appBase=".*"# appBase="/data/www/www"#' / application / tomcat8_2 / conf / server.xml diff / application / tomcat / conf / server.xml / application / tomcat8_1 / conf / server.xml |
启动
1 | for i in { 1. . 2 };do / application / tomcat8_$i / bin / startup.sh;done |
查看
web访问
tomcat集群
使用nginx反向代理tomcat即可(nginx反向代理配置不详述)
tomcat安装JPress
JPress,一个wordpress的java代替版本,使用JFinal开发。 需要maven支持
1 2 | tar - xf apache - maven - 3.3 . 9 - bin .tar.gz - C / application / ln - s / application / apache - maven - 3.3 . 9 / / application / maven |
vim /etc/profile在尾部添加
1 2 | export MAVEN_HOME = / application / maven export PATH = "$MAVEN_HOME/bin:$PATH" |
设置生效
1 | source / etc / profile |
验证
上传jar包
会自动解压
在浏览器安装
tomcat监控
使用脚本找出前五个最忙的进程
通用脚本,假如那台服务器CPU高直接执行把报错发给开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | #!/bin/bash # @Function # Find out the highest cpu consumed threads of java, and print the stack of these threads. # # @Usage # $ ./show-busy-java-threads # # @online-doc https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#beer-show-busy-java-threads # @author Jerry Lee (oldratlee at gmail dot com) # @author superhj1987 (superhj1987 at 126 dot com) readonly PROG = "`basename $0`" readonly - a COMMAND_LINE = ( "$0" "$@" ) # Get current user name via whoami command # See https://www.lifewire.com/current-linux-user-whoami-command-3867579 # Because if run command by `sudo -u`, env var $USER is not rewritten/correct, just inherited from outside! readonly USER = "`whoami`" ################################################################################ # util funtions ################################################################################ # NOTE: $'foo' is the escape sequence syntax of bash readonly ec = $ '\033' # escape char readonly eend = $ '\033[0m' # escape end colorPrint() { local color = $ 1 shift # if stdout is console, turn on color output. [ - t 1 ] && echo "$ec[1;${color}m$@$eend" || echo "$@" [ - n "$append_file" ] && echo "$@" >> "$append_file" } redPrint() { colorPrint 31 "$@" } greenPrint() { colorPrint 32 "$@" } yellowPrint() { colorPrint 33 "$@" } bluePrint() { colorPrint 36 "$@" } normalPrint() { echo "$@" [ - n "$append_file" ] && echo "$@" >> "$append_file" } fatal() { redPrint "$@" 1 >& 2 exit 1 } usage() { [ - n "$1" - a "$1" ! = 0 ] && local out = / dev / stderr || local out = / dev / stdout > $out cat <<EOF Usage: ${PROG} [OPTION]... [delay [count]] Find out the highest cpu consumed threads of java, and print the stack of these threads. Example: ${PROG} # show busy java threads info ${PROG} 1 # update every 1 second, (stop by eg: CTRL+C) ${PROG} 3 10 # update every 3 seconds, update 10 times Options: - p, - - pid <java pid> find out the highest cpu consumed threads from the specifed java process, default from all java process. - c, - - count <num> set the thread count to show, default is 5 - a, - - append - file < file > specify the file to append output as log - s, - - jstack - path <path> specify the path of jstack command - F, - - force set jstack to force a thread dump use when jstack <pid> does not respond (process is hung) - m, - - mix - native - frames set jstack to print both java and native frames (mixed mode) - l, - - lock - info set jstack with long listing. Prints additional information about locks - h, - - help display this help and exit delay the delay between updates in seconds count the number of updates delay / count arguments imitates the style of vmstat command EOF exit $ 1 } ################################################################################ # Check os support ################################################################################ uname | grep '^Linux' - q || fatal "Error: $PROG only support Linux, not support `uname` yet!" ################################################################################ # parse options ################################################################################ readonly ARGS = `getopt - n "$PROG" - a - o p:c:a:s:Fmlh - l count:,pid:,append - file :,jstack - path:,force,mix - native - frames,lock - info, help - - "$@" ` [ $? - ne 0 ] && usage 1 eval set - - "${ARGS}" while true; do case "$1" in - c| - - count) count = "$2" shift 2 ;; - p| - - pid) pid = "$2" shift 2 ;; - a| - - append - file ) append_file = "$2" shift 2 ;; - s| - - jstack - path) jstack_path = "$2" shift 2 ;; - F| - - force) force = - F shift 1 ;; - m| - - mix - native - frames) mix_native_frames = - m shift 1 ;; - l| - - lock - info) more_lock_info = - l shift 1 ;; - h| - - help ) usage ;; - - ) shift break ;; esac done count = ${count: - 5 } update_delay = ${ 1 : - 0 } [ - z "$1" ] && update_count = 1 || update_count = ${ 2 : - 0 } [ $update_count - lt 0 ] && update_count = 0 ################################################################################ # check the existence of jstack command ################################################################################ if [ - n "$jstack_path" ]; then [ - f "$jstack_path" ] || fatal "Error: $jstack_path is NOT found!" [ - x "$jstack_path" ] || fatal "Error: $jstack_path is NOT executalbe!" elif which jstack &> / dev / null; then jstack_path = "`which jstack`" else [ - z "$JAVA_HOME" ] && fatal "Error: jstack not found on PATH and No JAVA_HOME setting! Use -s option set jstack path manually." [ - f "$JAVA_HOME/bin/jstack" ] || fatal "Error: jstack not found on PATH and \$JAVA_HOME/bin/jstack($JAVA_HOME/bin/jstack) file does NOT exists! Use -s option set jstack path manually." [ - x "$JAVA_HOME/bin/jstack" ] || fatal "Error: jstack not found on PATH and \$JAVA_HOME/bin/jstack($JAVA_HOME/bin/jstack) is NOT executalbe! Use -s option set jstack path manually." jstack_path = "$JAVA_HOME/bin/jstack" fi ################################################################################ # biz logic ################################################################################ readonly uuid = `date + % s`_${RANDOM}_$$ cleanupWhenExit() { rm / tmp / ${uuid}_ * &> / dev / null } trap "cleanupWhenExit" EXIT printStackOfThreads() { local line local counter = 0 while IFS = " " read - a line ; do local pid = ${line[ 0 ]} local threadId = ${line[ 1 ]} local threadId0x = "0x`printf %x ${threadId}`" local pcpu = ${line[ 3 ]} local user = ${line[ 4 ]} ((counter + + )) local jstackFile = / tmp / ${uuid}_${pid} [ - f "${jstackFile}" ] || { if [ "${user}" = = "${USER}" ]; then # run without sudo, when java process user is current user "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile} elif [ $UID = = 0 ]; then # if java process user is not current user, must run jstack with sudo sudo - u "${user}" "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile} else # current user is not root user, so can not run with sudo; print error message and rerun suggestion redPrint "[$counter] Fail to jstack busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})." redPrint "User of java process($user) is not current user($USER), need sudo to rerun:" yellowPrint " sudo ${COMMAND_LINE[@]}" normalPrint continue fi || { redPrint "[$counter] Fail to jstack busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})." normalPrint rm ${jstackFile} continue } } bluePrint "[$counter] Busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user}):" if [ - n "$mix_native_frames" ]; then local sed_script = " / - - - - - - - - - - - - - - - $threadId - - - - - - - - - - - - - - - / , / ^ - - - - - - - - - - - - - - - / { / - - - - - - - - - - - - - - - $threadId - - - - - - - - - - - - - - - / b # skip first seperator line / ^ - - - - - - - - - - - - - - - / s / . * / / # replace sencond seperator line to empty line p }" elif [ - n "$force" ]; then local sed_script = "/^Thread ${threadId}:/,/^$/p" else local sed_script = "/nid=${threadId0x} /,/^$/p" fi sed "$sed_script" - n ${jstackFile} | tee ${append_file: + - a "$append_file" } done } headInfo() { echo = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = echo "$(date " + % Y - % m - % d % H: % M: % S. % N ") [$((i+1))/$update_count]: ${COMMAND_LINE[@]}" echo = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = echo } # if update_count <= 0, infinite loop till user interupted (eg: CTRL+C) for ((i = 0 ; update_count < = 0 || i < update_count; + + i)); do [ "$i" - gt 0 ] && sleep "$update_delay" [ - n "$append_file" ] && headInfo >> "$append_file" [ "$update_count" - ne 1 ] && headInfo # use wide output(unlimited width), avoid trunk user column to username_fo+ or $uid alike ps - wwLeo pid,lwp,comm,pcpu,user - - no - headers | { [ - z "${pid}" ] && awk '$3=="java"{print $0}' || awk - v "pid=${pid}" '$1==pid,$3=="java"{print $0}' } | sort - k4, 4 - r - n | head - n "${count}" | printStackOfThreads done |
zabbix监控tomcat
开启tomcat远程监控的功能
/application/tomcat/bin/catalina.sh
1 | CATALINA_OPTS = "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=12345" |
zabbix server的java支持
编译加上--enable-jave参数,或者yum安装zabbix-java-gateway
启动zabbix-java-gateway
1 | systemctl start zabbix - java - gateway |
验证
修改配置文件/etc/zabbix/zabbix_server.conf
重启zabbix-server
1 | systemctl restart zabbix - server |
验证(有这五个进程代表正常)
在zabbix页面添加jvm监控
tomcat调优
安全优化
降权启动
复制tomcat文件夹至普通用户文件夹并且授权给普通用户然后切换到普通用户即可使用普通用户启动tomcat
telnet管理端口保护
把默认的8005端口改成其他端口,关闭命令默认是SHUTDOWN可以修改成其他的
ajp连接端口保护
注释8009端口哪行
禁用管理端
删除默认的doc,managed目录
其他优化
性能优化
屏蔽dns查询 enableLookup=“false”
1 2 3 | <Connector port = "8081" protocol = "HTTP/1.1" connectionTimeout = "6000" enableLookups = "false" acceptCount = "800" redirectPort = "8443" / > |
jvm调优
tomcat最吃内存,只要内存够,就跑的很快
如果系统资源有限,那就需要进行调优,提高资源利用率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 优化catalina.sh配置文件。在catalina.sh配置文件中添加以下代码: JAVA_OPTS = "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=512m -XX:PermSize=512m -XX:MaxPermSize=512m" server:一定要作为第一个参数,在多个CPU时性能佳 - Xms:初始堆内存Heap大小,使用的最小内存,cpu性能高时此值应设的大一些 - Xmx:初始堆内存heap最大值,使用的最大内存 上面两个值是分配JVM的最小和最大内存,取决于硬件物理内存的大小,建议均设为物理内存的一半。 - XX:PermSize:设定内存的永久保存区域 - XX:MaxPermSize:设定最大内存的永久保存区域 - XX:MaxNewSize: - Xss 15120 这使得JBoss每增加一个线程(thread)就会立即消耗 15M 内存,而最佳值应该是 128K ,默认值好像是 512k . + XX:AggressiveHeap 会使得 Xms没有意义。这个参数让jvm忽略Xmx参数,疯狂地吃完一个G物理内存,再吃尽一个G的swap。 - Xss:每个线程的Stack大小 - verbose:gc 现实垃圾收集信息 - Xloggc:gc.log 指定垃圾收集日志文件 - Xmn:young generation的heap大小,一般设置为Xmx的 3 、 4 分之一 - XX: + UseParNewGC :缩短minor收集的时间 - XX: + UseConcMarkSweepGC :缩短major收集的时间 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!