通过FD耗尽实验谈谈使用HttpClient的正确姿势

一段问题代码实验

在进行网络编程时,正确关闭资源是一件很重要的事。在高并发场景下,未正常关闭的资源数逐渐积累会导致系统资源耗尽,影响系统整体服务能力,但是这件重要的事情往往又容易被忽视。我们进行一个简单的实验,使用HttpClient-3.x编写一个demo请求指定的url,看看如果不正确关闭资源会发生什么事。

public String doGetAsString(String url) {GetMethod getMethod = null;String is = null;InputStreamReader inputStreamReader = null;BufferedReader br = null;try {HttpClient httpclient = new HttpClient();//问题标记①getMethod = new GetMethod(url);httpclient.executeMethod(getMethod);if (HttpStatus.SC_OK == getMethod.getStatusCode()) {......//对返回结果进行消费,代码省略}return is;} catch (Exception e) {if (getMethod != null) {getMethod.releaseConnection();  //问题标记②              }            } finally {inputStreamReader.close();br.close();......//关闭流时的异常处理代码省略}return null;}

这段代码逻辑很简单, 先创建一个HttpClient对象,用url构建一个GetMethod对象,然后发起请求。但是用这段代码并发地以极高的QPS去访问外部的url,很快就会在日志中看到“打开文件太多,无法打开文件”的错误,后续的http请求都会失败。这时我们用lsof -p ${javapid}命令去查看java进程打开的文件数,发现达到了655350这么多。
分析上面的代码片段,发现存在以下2个问题:
(1)初始化方式不对。标记①直接使用new HttpClient()的方式来创建HttpClient,没有显示指定HttpClient connection manager,则构造函数内部默认会使用SimpleHttpConnectionManager,而SimpleHttpConnectionManager的默认参数中alwaysClose的值为false,意味着即使调用了releaseConnection方法,连接也不会真的关闭。
(2)在未使用连接池复用连接的情况下,代码没有正确调用releaseConnection。catch块中的标记②是唯一调用了releaseConnection方法的代码,而这段代码仅在发生异常时才会走到,大部分情况下都走不到这里,所以即使我们前面用正确的方式初始化了HttpClient,由于没有手动释放连接,也还是会出现连接堆积的问题。

可能有同学会有以下疑问:
1、明明是发起Http请求,为什么会打开这么多文件呢?为什么是655350这个上限呢?
2、正确的HttpClient使用姿势是什么样的呢?
这就涉及到linux系统中fd的概念。

什么是fd

在linux系统中有“一切皆文件”的概念。打开和创建普通文件、Socket(套接字)、Pipeline(管道)等,在linux内核层面都需要新建一个文件描述符来进行状态跟踪和使用。我们使用HttpClient发起请求,其底层需要首先通过系统内核创建一个Socket连接,相应地就需要打开一个fd。
为什么我们的应用最多只能创建655350个fd呢?这个值是如何控制的,能否调整呢?事实上,linux系统对打开文件数有多个层面的限制:
1)限制单个Shell进程以及其派生子进程能打开的fd数量。用ulimit命令能查看到这个值。
2)限制每个user能打开的文件总数。具体调整方法是修改/etc/security/limits.conf文件,比如下图中的红框部分就是限制了userA用户只能打开65535个文件,userB用户只能打开655350个文件。由于我们的应用在服务器上是以userB身份运行的,自然就受到这里的限制,不允许打开多于655350个文件。

# /etc/security/limits.conf
#
#<domain>      <type>  <item>     <value>
userA          -      nofile         65535
userB             -         nofile         655350# End of file

3)系统层面允许打开的最大文件数限制,可以通过“cat /proc/sys/fs/file-max”查看。
前文demo代码中错误的HttpClient使用方式导致连接使用完成后没有成功断开,连接长时间保持CLOSE_WAIT状态,则fd需要继续指向这个套接字信息,无法被回收,进而出现了本文开头的故障。

再识HttpClient

我们的代码中错误使用common-httpclient-3.x导致后续请求失败,那这里的common-httpclient-3.x到底是什么东西呢?相信所有接触过网络编程的同学对HttpClient都不会陌生,由于java.net中对于http访问只提供相对比较低级别的封装,使用起来很不方便,所以HttpClient作为Jakarta Commons的一个子项目出现在公众面前,为开发者提供了更友好的发起http连接的方式。然而目前进入Jakarta Commons HttpClient官网,会发现页面最顶部的“End of life”栏目,提示此项目已经停止维护了,它的功能已经被Apache HttpComponents的HttpClient和HttpCore所取代。
同为Apache基金会的项目,Apache HttpComponents提供了更多优秀特性,它总共由3个模块构成:HttpComponents Core、HttpComponents Client、HttpComponents AsyncClient,分别提供底层核心网络访问能力、同步连接接口、异步连接接口。在大多数情况下我们使用的都是HttpComponents Client。为了与旧版的Commons HttpClient做区分,新版的HttpComponents Client版本号从4.x开始命名。

从源码上来看,Jakarta Commons HttpClient和Apache HttpComponents Client虽然有很多同名类,但是两者之间没有任何关系。以最常使用到的HttpClient类为例,在commons-httpclient中它是一个类,可以直接发起请求;而在4.x版的httpClient中,它是一个接口,需要使用它的实现类。

既然3.x与4.x的HttpClient是两个完全独立的体系,那么我们就分别讨论它们的正确用法。

HttpClient 3.x用法

回顾引发故障的那段代码,通过直接new HttpClient()的方式创建HttpClient对象,然后发起请求,问题出在了这个构造函数上。由于我们使用的是无参构造函数,查看三方包源码,会发现内部会通过无参构造函数new一个SimpleHttpConnectionManager,它的成员变量alwaysClose在不特别指定的情况下默认为false。

alwaysClose这个值是如何影响到我们关闭连接的动作呢?继续跟踪下去,发现HttpMethodBase(它的多个实现类分别对应HTTP中的几种方法,我们最常用的是GetMethod和PostMethod)中的releaseConnection()方法首先会尝试关闭响应输入流(下图中的①所指代码),然后在finally中调用ensureConnectionRelease(),这个方法内部其实是调用了HttpConnection类的releaseConnection()方法,如下图中的标记③所示,它又会调用到SimpleHttpConnectionManager的releaseConnection(conn)方法,来到了最关键的标记④和⑤。

标记④的代码说明,如果alwaysClose=true,则会调用httpConnection.close()方法,它的内部会把输入流、输出流都关闭,然后把socket连接关闭,如标记⑥和⑦所示。

然后,如果标记④处的alwaysClose=false,则会走到⑤的逻辑中,调用finishLastResponse()方法,如标记⑧所示,这段逻辑实际上只是把请求响应的输入流关闭了而已。我们的问题代码就是走到了这段逻辑,导致没能把之前使用过的连接断开,而后续的请求又没有复用这个httpClient,每次都是new一个新的,导致大量连接处于CLOSE_WAIT状态占用系统文件句柄。

通过以上分析,我们知道使用commons-httpclient-3.x之后如果想要正确关闭连接,就需要指定always=true且正确调用method.releaseConnection()方法。

上述提到的几个类,他们的依赖关系如下图(红色箭头标出的是我们刚才讨论到的几个类):

其中SimpleHttpConnectionManager这个类的成员变量和方法列表如下图所示:

事实上,通过对commons-httpclient-3.x其他部分源码的分析,可以得知还有其他方法也可以正确关闭连接。
方法1:先调用method.releaseConnection(),然后获取到httpClient对象的SimpleHttpConnectionManager成员变量,主动调用它的shutdown()方法即可。对应的三方包源码如下图所示,其内部会调用httpConnection.close()方法。

方法2:先调用method.releaseConnection(),然后获取到httpClient对象的SimpleHttpConnectionManager成员变量,主动调用closeIdleConnections(0)即可,对应的三方包源码如下。

方法3:由于我们使用的是HTTP/1.1协议,默认会使用长连接,所以会出现上面的连接不释放的问题。如果客户端与服务端双方协商好不使用长连接,不就可以解决问题了吗。commons-httpclient-3.x也确实提供了这个支持,从下面的注释也可以看出来。具体这样操作,我们在创建了method后使用method.setRequestHeader("Connection", "close")设置头部信息,并在使用完成后调用一次method.releaseConnection()。Http服务端在看到此头部后会在response的头部中也带上“Connection: close”,如此一来httpClient发现返回的头部有这个信息,则会在处理完响应后自动关闭连接。

HttpClient 4.x用法

既然官方已经不再维护3.x,而是推荐所有使用者都升级到4.x上来,我们就顺应时代潮流,重点看看4.x的用法。

(1)简易用法

最简单的用法类似于3.x,调用三方包提供的工具类静态方法创建一个CloseableHttpClient对象,然后发起调用,如下图。这种方式创建的CloseableHttpClient,默认使用的是PoolingHttpClientConnectionManager来管理连接。由于CloseableHttpClient是线程安全的,因此不需要每次调用时都重新生成一个,可以定义成static字段在多线程间复用。

如上图,我们在获取到response对象后,自己决定如何处理返回数据。HttpClient的三方包中已经为我们提供了EntityUtils这个工具类,如果使用这个类的toString()或consume()方法,则上图finally块红框中的respnose.close()就不是必须的了,因为EntityUtils的方法内部会在处理完数据后把底层流关闭。

(2)简易用法涉及到的核心类详解

CloseableHttpClient是一个抽象类,我们通过HttpClients.createDefault()创建的实际是它的子类InternalHttpClient。

/*** Internal class.** @since 4.3*/
@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
@SuppressWarnings("deprecation")
class InternalHttpClient extends CloseableHttpClient implements Configurable {... ...
}

继续跟踪httpclient.execute()方法,发现其内部会调用CloseableHttpClient.doExecute()方法,实际会调到InternalHttpClient类的doExecute()方法。通过对请求对象(HttpGet、HttpPost等)进行一番包装后,最后实际由execChain.execute()来真正执行请求,这里的execChain是接口ClientExecChain的一个实例。接口ClientExecChain有多个实现类,由于我们使用HttpClients.createDefault()这个默认方法构造了CloseableHttpClient,没有指定ClientExecChain接口的具体实现类,所以系统默认会使用RedirectExec这个实现类。

/*** Base implementation of {@link HttpClient} that also implements {@link Closeable}.** @since 4.3*/
@Contract(threading = ThreadingBehavior.SAFE)
public abstract class CloseableHttpClient implements HttpClient, Closeable {private final Log log = LogFactory.getLog(getClass());protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,HttpContext context) throws IOException, ClientProtocolException;... ...
}

RedirectExec类的execute()方法较长,下图进行了简化。

可以看到如果远端返回结果标识需要重定向(响应头部是301、302、303、307等重定向标识),则HttpClient默认会自动帮我们做重定向,且每次重定向的返回流都会自动关闭。如果中途发生了异常,也会帮我们把流关闭。直到拿到最终真正的业务返回结果后,直接把整个response向外返回,这一步没有帮我们关闭流。因此,外层的业务代码在使用完response后,需要自行关闭流。

执行execute()方法后返回的response是一个CloseableHttpResponse实例,它的实现是什么?点开看看,这是一个接口,此接口唯一的实现类是HttpResponseProxy。


/*** Extended version of the {@link HttpResponse} interface that also extends {@link Closeable}.** @since 4.3*/
public interface CloseableHttpResponse extends HttpResponse, Closeable {
}

我们前面经常看到的response.close(),实际是调用了HttpResponseProxy的close()方法,其内部逻辑如下:

/*** A proxy class for {@link org.apache.http.HttpResponse} that can be used to release client connection* associated with the original response.** @since 4.3*/class HttpResponseProxy implements CloseableHttpResponse {    @Overridepublic void close() throws IOException {if (this.connHolder != null) {this.connHolder.close();}}... ...
}
/*** Internal connection holder.** @since 4.3*/
@Contract(threading = ThreadingBehavior.SAFE)
class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable {... ...@Overridepublic void close() throws IOException {releaseConnection(false);}}

可以看到最终会调用到ConnectionHolder类的releaseConnection(reusable)方法,由于ConnectionHolder的close()方法调用releaseConnection()时默认传入了false,因此会走到else的逻辑中。这段逻辑首先调用managedConn.close()方法,然后调用manager.releaseConnection()方法。

managedConn.close()方法实际是把连接池中已经建立的连接在socket层面断开连接,断开之前会把inbuffer清空,并把outbuffer数据全部传送出去,然后把连接池中的连接记录也删除。manager.releaseConnection()对应的代码是PoolingHttpClientConnectionManager.releaseConnection(),这段代码代码本来的作用是把处于open状态的连接的socket超时时间设置为0,然后把连接从leased集合中删除,如果连接可复用则把此连接加入到available链表的头部,如果不可复用则直接把连接关闭。由于前面传入的reusable已经强制为false,因此实际关闭连接的操作已经由managedConn.close()方法做完了,走到PoolingHttpClientConnectionManager.releaseConnection()中真正的工作基本就是清除连接池中的句柄而已。

如果想了解关闭socket的细节,可以通过HttpClientConnection.close()继续往下跟踪,最终会看到真正关闭socket的代码在BHttpConnectionBase中。

/*** This class serves as a base for all {@link HttpConnection} implementations and provides* functionality common to both client and server HTTP connections.** @since 4.0*/
public class BHttpConnectionBase implements HttpConnection, HttpInetConnection {... ...@Overridepublic void close() throws IOException {final Socket socket = this.socketHolder.getAndSet(null);if (socket != null) {try {this.inbuffer.clear();this.outbuffer.flush();try {try {socket.shutdownOutput();} catch (final IOException ignore) {}try {socket.shutdownInput();} catch (final IOException ignore) {}} catch (final UnsupportedOperationException ignore) {// if one isn't supported, the other one isn't either}} finally {socket.close();}}}... ...
}

为什么说调用了EntityUtils的部分方法后,就不需要再显示地关闭流呢?看下它的源码就明白了。

/*** Static helpers for dealing with {@link HttpEntity}s.** @since 4.0*/
public final class EntityUtils {/*** Ensures that the entity content is fully consumed and the content stream, if exists,* is closed.** @param entity the entity to consume.* @throws IOException if an error occurs reading the input stream** @since 4.1*/public static void consume(final HttpEntity entity) throws IOException {if (entity == null) {return;}if (entity.isStreaming()) {final InputStream instream = entity.getContent();if (instream != null) {instream.close();}}}... ...
}

(3)HttpClient进阶用法
在高并发场景下,使用连接池有效复用已经建立的连接是非常必要的。如果每次http请求都重新建立连接,那么底层的socket连接每次通过3次握手创建和4次握手断开连接将是一笔非常大的时间开销。
要合理使用连接池,首先就要做好PoolingHttpClientConnectionManager的初始化。如下图,我们设置maxTotal=200且defaultMaxPerRoute=20。maxTotal=200指整个连接池中连接数上限为200个;defaultMaxPerRoute用来指定每个路由的最大并发数,比如我们设置成20,意味着虽然我们整个池子中有200个连接,但是连接到"http://www.taobao.com"时同一时间最多只能使用20个连接,其他的180个就算全闲着也不能给发到"http://www.taobao.com"的请求使用。因此,对于高并发的场景,需要合理分配这2个参数,一方面能够防止全局连接数过多耗尽系统资源,另一方面通过限制单路由的并发上限能够避免单一业务故障影响其他业务。

private static volatile CloseableHttpClient instance;static {PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();// Increase max total connection to 200cm.setMaxTotal(200);// Increase default max connection per route to 20cm.setDefaultMaxPerRoute(20);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(1000).setSocketTimeout(1000).setConnectionRequestTimeout(1000).build();instance = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).build();}

官方同时建议我们在后台起一个定时清理无效连接的线程,因为某些连接建立后可能由于服务端单方面断开连接导致一个不可用的连接一直占用着资源,而HttpClient框架又不能百分之百保证检测到这种异常连接并做清理,因此需要自给自足,按照如下方式写一个空闲连接清理线程在后台运行。

public class IdleConnectionMonitorThread extends Thread {private final HttpClientConnectionManager connMgr;private volatile boolean shutdown;Logger logger = LoggerFactory.getLogger(IdleConnectionMonitorThread.class);public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {super();this.connMgr = connMgr;}@Overridepublic void run() {try {while (!shutdown) {synchronized (this) {wait(5000);// Close expired connectionsconnMgr.closeExpiredConnections();// Optionally, close connections// that have been idle longer than 30 secconnMgr.closeIdleConnections(30, TimeUnit.SECONDS);} }} catch (InterruptedException ex) {logger.error("unknown exception", ex);// terminate}}public void shutdown() {shutdown = true;synchronized (this) {notifyAll();}}
}

我们讨论到的几个核心类的依赖关系如下:

HttpClient作为大家常用的工具,看似简单,但是其中却有很多隐藏的细节值得探索。

 


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

与“十“俱进 阿里数据库运维10年演进之路

导语 阿里巴巴集团拥有超大的数据库实例规模&#xff0c;在快速发展的过程中我们在运维管理方面也在不断的面临变化&#xff0c;从物理器到容器、从独占到混布、从本地盘到存储计算分离、从集团内到大促云资源&#xff0c;从开源的MySQL到自研分布式数据库&#xff0c;运维管控…

jmeter 压测 RabbitMQ_单机

文章目录一、MQ压测1. 资料列表2. jmeter软件包3. 插件列表二、远程服务器监控2.1. 监控声明2.2. 监控场景的区别2.3. 软件列表2.4. 插件操作2.5. 软件操作三、jmeter编写MQ脚本3.1.创建线程组3.2. 创建MQ生产者3.3. 创建MQ消费者四、监听器4.1. 聚合报告4.2. 观察树4.3. 监控五…

云+X案例展 | 民生类:纷享销客助力沃得农机构筑智能化、信息化之路

本案例由纷扬科技投递并参与评选&#xff0c;CSDN云计算独家全网首发&#xff1b;更多关于【云X 案例征集】的相关信息&#xff0c;点击了解详情丨挖掘展现更多优秀案例&#xff0c;为不同行业领域带来启迪&#xff0c;进而推动整个“云行业”的健康发展。​​​​“2004年到20…

如何“神还原”数据中心? 阿里联合NTU打造了工业级精度的仿真沙盘!

如何保障数据中心的稳定运行&#xff0c;是多年来一直困扰业界的难题。机房环境如果发生未预期变化&#xff0c;可能造成难以估计的损失。所以我们希望能构建一个“变更沙盘”&#xff0c;在真实变更之前&#xff0c;操作人员可以先在沙盘中进行试变更&#xff0c;若变更效果在…

RabbitMQ 手动签收

下面这基础地方都必须设置&#xff0c;不然无效 // 同一时刻服务器只会发一条消息给消费者channel.basicQos(1); // 消息的标识&#xff0c;false只确认当前一个消息收到&#xff0c;true确认所有consumer获得的消息 channel.basicAck(message.getMessageProperties().getDeli…

把16进制转换为ascii字符c语言,ASCII转16进制C语言

满意答案u2gseftj278推荐于 2016.03.01采纳率&#xff1a;56% 等级&#xff1a;11已帮助&#xff1a;14340人以前引别人的&#xff0c;自己懒得再写了呵呵。原理就是这样的&#xff0c;你可以直接用的//函 数 名&#xff1a;AscToHex()//功能描述&#xff1a;把ASCII转换为1…

四大维度全景揭秘阿里巴巴智能对话开发平台

在阿里巴巴智能服务事业部的X蜂会上&#xff0c;小蜜北京团队的高级算法专家李永彬&#xff08;水德&#xff09;分享了小蜜智能对话开发平台的构建&#xff0c;围绕平台来源、设计理念、核心技术、业务落地情况四大维度讲述了一个较为完整的智能任务型对话开发平台的全景。以下…

2019年技术盘点云数据库篇(二):阿里云携手MongoDB率先上线4.2数据库 云上数据库已是大势所趋...

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 刘丹出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;随着技术的飞速发展&#xff0c;云数据库在云计算的大背景下&#xff0c;作为一种新兴的共享基础架构方法逐渐发展起来&#xff0c;它极大地增强了数据…

Oracle 查看表空间的大小及使用情况sql语句

SELECT a.tablespace_name "表空间名称", total / (1024 * 1024) "表空间大小(M)", free / (1024 * 1024) "表空间剩余大小(M)", (total - free) / (1024 * 1024 ) "表空间使用大小(M)", total / (1024 * 1024 * 1024) "表空…

高可用、弹性动态的金融级移动架构在蚂蚁金服的演进之路

本文基于重岳在 2018 年 Arch Summit 北京站的分享内容进行总结&#xff0c;希望通过本篇文章介绍近些年来支付宝在移动端架构的上演进和思考&#xff0c;期冀能给读者们带来些许帮助。 支付宝作为国民级应用&#xff0c;当前全球用户已经超过 10 亿&#xff0c;提供了超过 200…

Android代码混淆方法,Android 代码混淆零基础入门

内容提要本篇文章主要有三个部分&#xff0c;让读者读完后能自己写规则混淆项目对Android代码怎么开启混淆做一个简单的介绍。对混淆规则做一个简单介绍&#xff1b;在混淆过后Crash日志反推代码工具retrace.bat、可视化反推工具GUI说明。对混淆的一个简单介绍&#xff1a;Andr…

oracle查询当前用户名下所有表

--SONARQUBE为用户名&#xff0c;用户名必须是大写 SELECT * from all_tables where ownerSONARQUBE;--查看当前登录的用户的表: SELECT table_name from user_tables;

JUC包中的分而治之策略-为提高性能而生

一、前言 本次分享我们来共同探讨JUC包中一些有意思的类&#xff0c;包含AtomicLong & LongAdder,ThreadLocalRandom原理。 二、AtomicLong & LongAdder 2.1 AtomicLong 类 AtomicLong是JUC包提供的原子性操作类&#xff0c;其内部通过CAS保证了对计数的原子性更新…

galaxy s8 android pc,手机秒变PC!三星Galaxy S8桌面模式曝光

据外媒报道&#xff0c;三星旗舰手机Galaxy S8/S8 Plus在外观上以及硬件配置上已经没有了悬念。不过一些小的改进或者是新功能还是让人对Galaxy S8充满期待。日前&#xff0c;传闻中的Galaxy S8桌面模式终于被曝光。三星Galaxy S8桌面模式曝光(图片来自kkj)报道称&#xff0c;G…

2020年进军 AI,想年薪 40 万,没这个能力不行

前几天&#xff0c;《百度沸点&#xff1a;2019年度科技热词》来了&#xff01;百度沸点&#xff1a;2019年度科技热词 AI排名第一2019年可以说是AI全面落地和商用的一年&#xff0c;产业智能化成为各个行业重点关注的发展方向&#xff0c;交通、工业、农业、医疗等主流行业无一…

重磅公开!阿里语音识别模型端核心技术,让你“听”见未来

语音识别技术作为人工智能技术中的重要组成部分&#xff0c;成为影响人机交互的核心组件之一&#xff0c;从各种智能家用IoT设备的语音交互能力&#xff0c;到公共服务、智慧政务等场合的应用&#xff0c;语音识别技术正在影响着人们生活的方方面面。 本文将全面介绍阿里云语音…

linux搭建SonarQube代码质量平台_Oracle 最新详细版本

文章目录一、最低配置要求1. JDK版本要求2. 数据库版本要求3. 支持浏览器版本二、软件下载安装2.1. 软件列表总览2.2. jdk11下载2.3. sonarqube下载2.4. sonar-scanner-cli2.5. Oracle 驱动三、安装实战3.1. JDK sonar-scanner3.2. sonarqube3.3. oracle驱动3.4. 启动sonar3.4.…

2018年AI和ML(NLP、计算机视觉、强化学习)技术总结和2019年趋势(上)

1、简介&#xff1a; 过去几年一直是人工智能爱好者和机器学习专业人士最幸福的时光。因为这些技术已经发展成为主流&#xff0c;并且正在影响着数百万人的生活。各国现在都有专门的人工智能规划和预算&#xff0c;以确保在这场比赛中保持优势。 数据科学从业人员也是如此&am…

2018最佳GAN论文回顾(下)

继上一篇《2018最佳GAN论文回顾&#xff08;上&#xff09;》&#xff0c;我又继续介绍了一个对于GAN的基于样式的生成器体系结构的新论文&#xff0c;提出了一个新的模型来应对这种挑战。 一种用于生成式对抗网络的基于生成器体系结构的方式&#xff08;A Style-Based Genera…

云+X案例展 | 民生类:浪潮云打卡人间仙境张家界

本案例由浪潮投递并参与评选&#xff0c;CSDN云计算独家全网首发&#xff1b;更多关于【云X 案例征集】的相关信息&#xff0c;点击了解详情丨挖掘展现更多优秀案例&#xff0c;为不同行业领域带来启迪&#xff0c;进而推动整个“云行业”的健康发展。“仙凡共界武陵门&#xf…