由一次NoHttpResponseException异常,追究到Http长连接和短连接_resttemplate 报错长连接-程序员宅基地

技术标签: java原理  java  http  spring相关  

 一片春愁待酒浇,江上舟摇,楼上帘招。秋娘渡与泰娘桥,风又漂漂,雨又潇潇。何日归家洗客袍?银字笙调,心字香烧。流光容易把人抛,红了樱桃,绿了芭蕉。

——蒋捷《一剪梅.舟过吴江》

一、异常描述

调用smk短信出现NoHttpResponseException异常:

用的是公司配置好的RestTemplate对象。 

原因分析:公司配置好的RestTemplate自带链接池,下次请求时时,连接池里有的连接已经断开了;所以会出现上面的错误。

用李哥的原话:这个问题应该是对方超时把连接关了,池子里的还认为是有效的。(要确认smk那边每个请求的keepalived设置多久的,他们响应的请求头上有带,然后在设置我们这边的会比较准确)

二、解决方法

解决方法有两种,一种是将RestTemplate设置成长连接(RestTemplate配置了连接池管理器,同时也要配置keepAlive);另一种是将RestTemplate设置成短连接。

2.1 RestTemplate设置成长连接

长连接以保证高性能,RestTemplate 本身也是一个 wrapper 其底层默认是 SimpleClientHttpRequestFactory ,如果要保证长连接HttpComponentsClientHttpRequestFactory 是个更好的选择,它不仅可以控制能够建立的连接数还能细粒度的控制到某个 server 的连接数,非常方便。在默认情况下,RestTemplate 到某个 server 的最大连接数只有 2, 一般需要调的更高些,最好等于 server 的 CPU 个数,所以通常下建议使用连接池的方式处理RestTeamplate;

 

 

先来看下原来的配置:

package com.hfi.health.starters.web;

import com.hfi.health.starters.common.util.HfiLogger;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import javax.net.ssl.SSLContext;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
@EnableConfigurationProperties({HttpClientProperties.class})
public class RestTemplateAutoConfiguration
{
  private static final Logger logger = HfiLogger.create(RestTemplateAutoConfiguration.class);
  @Autowired
  private HttpClientProperties httpClientProperties;
  
  @Bean
  public RestTemplate restTemplate()
  {
    RestTemplate template = new RestTemplate(httpRequestFactory());
    template.getInterceptors().add(new LoggingReqRespInterceptor());
    
    return template;
  }
  
  @Bean
  public ClientHttpRequestFactory httpRequestFactory()
  {
    return new HttpComponentsClientHttpRequestFactory(httpClient());
  }
  
  @Bean
  public HttpClient httpClient()
  {
    Registry<ConnectionSocketFactory> registry = RegistryBuilder.create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    connectionManager.setMaxTotal(this.httpClientProperties.getMaxTotal());
    connectionManager.setDefaultMaxPerRoute(this.httpClientProperties.getDefaultMaxPerRoute());
    connectionManager.setValidateAfterInactivity(this.httpClientProperties.getValidateAfterInactivity());    

    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(this.httpClientProperties.getSocketTimeout()).setConnectTimeout(this.httpClientProperties.getConnectTimeout()).setConnectionRequestTimeout(this.httpClientProperties.getConnectionRequestTimeout()).build();
    
    HttpClientBuilder clientBuilder = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager);
    try
    {
      SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy()
      {
        public boolean isTrusted(X509Certificate[] arg0, String arg1)
          throws CertificateException
        {
          return true;
        }
      }).build();
      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1" }, null, NoopHostnameVerifier.INSTANCE);
      
      clientBuilder.setConnectionManager(connectionManager).setSSLSocketFactory(csf);
    }
    catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e)
    {
      logger.error("SSL context configuring failed, HTTPS cannot be used in RestTemplate.", e);
    }
    return clientBuilder.build();
  }
}

 改后的配置:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.hfi.health.starters.web;

import com.alibaba.fastjson.JSON;
import com.hfi.health.starters.common.util.HfiLogger;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.net.ssl.SSLContext;
import org.apache.http.HeaderElement;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
@EnableConfigurationProperties({HttpClientProperties.class})
public class RestTemplateAutoConfiguration {
    private static final Logger logger = HfiLogger.create(RestTemplateAutoConfiguration.class);
    @Autowired
    private HttpClientProperties httpClientProperties;

    public RestTemplateAutoConfiguration() {
    }

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate template = new RestTemplate(this.httpRequestFactory());
        template.getInterceptors().add(new LoggingReqRespInterceptor());
        return template;
    }

    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(this.httpClient());
    }

    @Bean
    public HttpClient httpClient() {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(this.httpClientProperties.getMaxTotal());
        connectionManager.setDefaultMaxPerRoute(this.httpClientProperties.getDefaultMaxPerRoute());
        connectionManager.setValidateAfterInactivity(this.httpClientProperties.getValidateAfterInactivity());
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(this.httpClientProperties.getSocketTimeout()).setConnectTimeout(this.httpClientProperties.getConnectTimeout()).setConnectionRequestTimeout(this.httpClientProperties.getConnectionRequestTimeout()).build();
        HttpClientBuilder clientBuilder = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager);

        try {
            SSLContext sslContext = (new SSLContextBuilder()).loadTrustMaterial((KeyStore)null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, (String[])null, NoopHostnameVerifier.INSTANCE);
            clientBuilder.setConnectionManager(connectionManager).setSSLSocketFactory(csf);
            clientBuilder.setKeepAliveStrategy(this.connectionKeepAliveStrategy());
        } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException var7) {
            logger.error("SSL context configuring failed, HTTPS cannot be used in RestTemplate.", var7);
        }

        return clientBuilder.build();
    }

    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
        return (response, context) -> {
            BasicHeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator("Keep-Alive"));

            while(true) {
                String param;
                String value;
                do {
                    do {
                        if (!it.hasNext()) {
                            HttpHost target = (HttpHost)context.getAttribute("http.target_host");
                            Optional<Entry<String, Integer>> any = ((Map)Optional.ofNullable(this.httpClientProperties.getKeepAliveTargetHost()).orElseGet(HashMap::new)).entrySet().stream().filter((e) -> {
                                return ((String)e.getKey()).equalsIgnoreCase(target.getHostName());
                            }).findAny();
                            return (Long)any.map((en) -> {
                                return (long)(Integer)en.getValue() * 1000L;
                            }).orElse(this.httpClientProperties.getKeepAliveTime() * 1000L);
                        }

                        HeaderElement he = it.nextElement();
                        logger.info("HeaderElement:{}", JSON.toJSONString(he));
                        param = he.getName();
                        value = he.getValue();
                    } while(value == null);
                } while(!"timeout".equalsIgnoreCase(param));

                try {
                    return Long.parseLong(value) * 1000L;
                } catch (NumberFormatException var8) {
                    logger.error("Error occurs while parsing timeout settings of keep-alived connection.", var8);
                }
            }
        };
    }
}

 关键代码在这里:

红框的代码是设置http的keep-alive的策略的方法;
keep-alive的作用:使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。Web服务器,基本上都支持HTTP Keep-Alive。

keep-alive使用场景:我们相当于有少数固定客户端,长时间极高频次的访问服务器,启用keep-alive非常合适。

2.2 RestTemplate设置成短连接

 

二、HTTP协议中的长连接和短连接(keep-alive状态)

2.1 HTTP Keep-Alive

在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。

使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。

但是,keep-alive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。

2.2 keepalvie timeout

Httpd守护进程,一般都提供了keep-alive timeout时间设置参数。比如nginx的keepalive_timeout,和Apache的KeepAliveTimeout。这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。

 此处参考文章:HTTP Keep-Alive是什么?如何工作?

2.3 长连接与短连接

  • 长连接:client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收。这种方式下由于通讯连接一直存在。此种方式常用于P2P通信。
  • 短连接:Client方与server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此方式常用于一点对多点通讯。C/S通信。

2.4 长连接与短连接的操作过程

短连接的操作步骤是:
建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接

长连接的操作步骤是:
建立连接——数据传输...(保持连接)...数据传输——关闭连接

 2.5 长连接与短连接的使用时机

短连接多用于操作频繁,点对点的通讯,而且连接数不能太多的情况。每个TCP连接的建立都需要三次握手,每个TCP连接的断开要四次握手。

如果每次操作都要建立连接然后再操作的话处理速度会降低,所以每次操作后,下次操作时直接发送数据就可以了,不用再建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,频繁的socket创建也是对资源的浪费。

Web网站的http服务一般都用短连接,因为长连接对于服务器来说要耗费一定的资源。像web网站这么频繁的成千上万甚至上亿客户端的连接用短连接更省一些资源。试想如果都用长连接,而且同时用成千上万的用户,每个用户都占有一个连接的话,可想而知服务器的压力有多大。所以并发量大,但是每个用户又不需频繁操作的情况下需要短连接。

总之:长连接和短连接的选择要根据需求而定。
长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。

2.6 HTTP协议长连接、短连接总结

长连接与短连接的不同主要在于client和server采取的关闭策略不同。短连接在建立连接以后只进行一次数据传输就关闭连接,而长连接在建立连接以后会进行多次数据数据传输直至关闭连接(长连接中关闭连接通过Connection:closed头部字段)。

2.7 二者关闭策略的不同,就产生了长连接的优点

  • 通过开启、关闭更少的TCP连接,节约CPU时间和内存
  • 通过减少TCP开启引起的包的数目,降低网络阻塞。

二者所应用的具体场景不同。短连接多用于操作频繁、点对点的通讯,且连接数不能太多的情况。数据库的连接则采用长连接。

此小节参考文章:HTTP协议中的长连接与短连接

三、TCP的keepalive和HTTP的keepalive之间的区别

两者是完全不同的概念,只是凑巧名字相同。
tcp的keepalive指的是:周期性的去检查链接是否有效(working),经过一个时钟周期(keepalive_timeout)之后发送一个空的探测报文来检查;
HTTP的keepalive 表示:是否允许在同一次TCP链接中进行多次的HTTP请求,从而减少tcp链接建立和断开造成的开销

参考文章:HTTP协议中的长连接和短连接(keep-alive状态)

四、如何配置HTTP自定义KeepAlive策略

4.1RestTemlate自定义KeepAlive策略示例

/**
 * SpringBoot启动类
 */
@SpringBootApplication
@Slf4j
public class CustomerServiceApplication implements ApplicationRunner {
   @Autowired
   private RestTemplate restTemplate;
 
   public static void main(String[] args) {
      new SpringApplicationBuilder()
            .sources(CustomerServiceApplication.class)
            .bannerMode(Banner.Mode.OFF)
            .web(WebApplicationType.NONE)
            .run(args);
   }
 
   @Bean
   public HttpComponentsClientHttpRequestFactory requestFactory() {
      PoolingHttpClientConnectionManager connectionManager =
            new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
      connectionManager.setMaxTotal(200);
      connectionManager.setDefaultMaxPerRoute(20);
 
      CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .evictIdleConnections(30, TimeUnit.SECONDS)
            .disableAutomaticRetries()
            // 有 Keep-Alive 认里面的值,没有的话永久有效
            //.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
            // 换成自定义的
            .setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
            .build();
 
      HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory(httpClient);
 
      return requestFactory;
   }
 
   @Bean
   public RestTemplate restTemplate(RestTemplateBuilder builder) {
//    return new RestTemplate();
 
      return builder
            .setConnectTimeout(Duration.ofMillis(100))
            .setReadTimeout(Duration.ofMillis(500))
            .requestFactory(this::requestFactory)
            .build();
   }
 
   @Override
   public void run(ApplicationArguments args) throws Exception {
      URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:8080/coffee/?name={name}")
            .build("mocha");
      RequestEntity<Void> req = RequestEntity.get(uri)
//          .accept(MediaType.APPLICATION_XML)
            .build();
      ResponseEntity<String> resp = restTemplate.exchange(req, String.class);
      log.info("Response Status: {}, Response Headers: {}", resp.getStatusCode(), resp.getHeaders().toString());
      log.info("Coffee: {}", resp.getBody());
   }
}
 
 
/**
 * KeepAlive策略
 */
public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
    private final long DEFAULT_SECONDS = 30;
 
    @Override
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
                .stream()
                .filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
                        && StringUtils.isNumeric(h.getValue()))
                .findFirst()
                .map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
                .orElse(DEFAULT_SECONDS) * 1000;
    }
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/moneywenxue/article/details/109787242

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文