springboot获取不到客户端ip问题排查

news2024/10/7 3:24:45

一、现象

springboot从2.0.2升级到 2.5.7后线上环境无法通过request.getHeader("x-forwarded-for")获取客户端ip地址,测试环境正常,开发环境也异常

二、结论

springboot 2.5.7版本中CloudPlatform多了Kubernetes platform的类型识别,如果使用的是内嵌的tomcat,在k8s环境中会自动添加了tomcat的RemoteIpValve,线上环境的httpHeader(x-forwarded-for)只有一个,没有代理ip信息,按RemoteIpValve的逻辑,x-forwarded-for头信息会被删除。

三、排查流程

1、抓包看请求与现象

dev环境抓包

可以看到抓包中有x-forwarded-for,但是一个内网ip

2、本地调试

使用develop分支本地模拟dev环境请求,debug发现可以获取到ip地址

3、搜索springboot丢失x-forwarded-for的原因

搜索到文章loveyu.org/5951.html

看到了删除header的调用 removeHeader方法,但没有写是哪个类,搜索代码发现是内嵌tomcat的RemoteIpValve类中调用,大致看了下逻辑其中有删除header x-forwarded-for的代码,但打断点debug发现不会走到RemoteIpValve中

4、查看RemoteIpValve的执行逻辑

查看上面文章提到的配置:server.forward-headers-strategy

通过搜索发现,是在spring的配置类org.springframework.boot.autoconfigure.web.ServerProperties中

 /**
  * Strategy for handling X-Forwarded-* headers.
  */
 private ForwardHeadersStrategy forwardHeadersStrategy;
复制代码

get方法会在TomcatWebServerFactoryCustomizer类的getOrDeduceUseForwardHeaders方法中调用

 private void customizeRemoteIpValve(ConfigurableTomcatWebServerFactory factory) {
    Remoteip remoteIpProperties = this.serverProperties.getTomcat().getRemoteip();
    String protocolHeader = remoteIpProperties.getProtocolHeader();
    String remoteIpHeader = remoteIpProperties.getRemoteIpHeader();
    // For back compatibility the valve is also enabled if protocol-header is set
    if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)
          || getOrDeduceUseForwardHeaders()) {
       //通过配置添加RemoteIpValve
       RemoteIpValve valve = new RemoteIpValve();
       valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader : "X-Forwarded-Proto");
       if (StringUtils.hasLength(remoteIpHeader)) {
          valve.setRemoteIpHeader(remoteIpHeader);
       }
       // The internal proxies default to a list of "safe" internal IP addresses
       valve.setInternalProxies(remoteIpProperties.getInternalProxies());
       try {
          valve.setHostHeader(remoteIpProperties.getHostHeader());
       }
       catch (NoSuchMethodError ex) {
          // Avoid failure with war deployments to Tomcat 8.5 before 8.5.44 and
          // Tomcat 9 before 9.0.23
       }
       valve.setPortHeader(remoteIpProperties.getPortHeader());
       valve.setProtocolHeaderHttpsValue(remoteIpProperties.getProtocolHeaderHttpsValue());
       // ... so it's safe to add this valve by default.
       factory.addEngineValves(valve);
    }
 }
  
 private boolean getOrDeduceUseForwardHeaders() {
    if (this.serverProperties.getForwardHeadersStrategy() == null) {
       CloudPlatform platform = CloudPlatform.getActive(this.environment);
       return platform != null && platform.isUsingForwardHeaders();
    }
    return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
 }
复制代码

getOrDeduceUseForwardHeaders方法逻辑

1、如果没有配置forwardHeadersStrategy则判断目前的环境

org.springframework.boot.cloud.CloudPlatform#getActive

 public static CloudPlatform getActive(Environment environment) {
    if (environment != null) {
       for (CloudPlatform cloudPlatform : values()) {
          if (cloudPlatform.isActive(environment)) {
             return cloudPlatform;
          }
       }
    }
    return null;
 }
复制代码

CloudPlatform枚举类中可以看到比之前的springboot版本多了KUBERNETES的枚举,也就是在k8s环境CloudPlatform.getActive(this.environment)返回的不为空,isUsingForwardHeaders返回也为true

 public boolean isUsingForwardHeaders() {
    return true;
 }
复制代码

2、如果配置了则判断是否为NATIVE

新版本通过k8s环境判断getOrDeduceUseForwardHeaders方法返回true

getOrDeduceUseForwardHeaders返回为true,在customizeRemoteIpValve方法中就会添加RemoteIpValve

5、为什么测试环境没事,dev和线上都有问题

通过arthus查看dev和测试环境的调用栈,发现调用栈不同

dev调用栈

 `---ts=2022-11-14 20:45:50;thread_name=http-nio-8080-exec-1;id=5b;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@3d24753a
     `---[1.786075ms] org.apache.catalina.valves.RemoteIpValve:invoke()
         +---[0.54% 0.009643ms ] org.apache.catalina.connector.Request:getRemoteAddr() #613
         +---[0.17% 0.002994ms ] org.apache.catalina.connector.Request:getRemoteHost() #614
         +---[0.19% 0.003369ms ] org.apache.catalina.connector.Request:getScheme() #615
         +---[0.15% 0.002714ms ] org.apache.catalina.connector.Request:isSecure() #616
         +---[0.17% 0.003068ms ] org.apache.catalina.connector.Request:getServerName() #617
         +---[0.15% 0.002709ms ] org.apache.catalina.valves.RemoteIpValve:isChangeLocalName() #618
         +---[0.16% 0.002895ms ] org.apache.catalina.connector.Request:getServerPort() #619
         +---[0.22% 0.003994ms ] org.apache.catalina.connector.Request:getLocalPort() #620
         +---[0.18% 0.00325ms ] org.apache.catalina.connector.Request:getHeader() #621
         +---[0.16% 0.002793ms ] org.apache.catalina.connector.Request:getHeader() #622
         +---[0.21% 0.00372ms ] org.apache.catalina.connector.Request:getHeaders() #632
         +---[0.18% 0.003182ms ] org.apache.catalina.valves.RemoteIpValve:commaDelimitedListToStringArray() #640
         +---[0.14% 0.002488ms ] org.apache.catalina.connector.Request:getHeader() #700
         +---[0.13% 0.002356ms ] org.apache.catalina.connector.Request:getHeader() #716
         +---[0.28% 0.004932ms ] org.apache.catalina.connector.Request:setAttribute() #736
         +---[0.14% 0.002555ms ] org.apache.juli.logging.Log:isDebugEnabled() #738
         +---[0.10% 0.001796ms ] org.apache.catalina.connector.Request:getRemoteAddr() #756
         +---[0.15% 0.002711ms ] org.apache.catalina.connector.Request:setAttribute() #755
         +---[0.13% 0.002321ms ] org.apache.catalina.connector.Request:getRemoteAddr() #758
         +---[0.12% 0.002186ms ] org.apache.catalina.connector.Request:setAttribute() #757
         +---[0.11% 0.001972ms ] org.apache.catalina.connector.Request:getRemoteHost() #760
         +---[0.12% 0.002058ms ] org.apache.catalina.connector.Request:setAttribute() #759
         +---[0.19% 0.003334ms ] org.apache.catalina.connector.Request:getProtocol() #762
         +---[0.12% 0.00218ms ] org.apache.catalina.connector.Request:setAttribute() #761
         +---[0.12% 0.002192ms ] org.apache.catalina.connector.Request:getServerName() #764
         +---[0.12% 0.00212ms ] org.apache.catalina.connector.Request:setAttribute() #763
         +---[0.11% 0.002021ms ] org.apache.catalina.connector.Request:getServerPort() #766
         +---[0.14% 0.002423ms ] org.apache.catalina.connector.Request:setAttribute() #765
         +---[0.39% 0.007047ms ] org.apache.catalina.valves.RemoteIpValve:getNext() #769
         +---[79.19% 1.414379ms ] org.apache.catalina.Valve:invoke() #769
         +---[0.23% 0.00409ms ] org.apache.catalina.connector.Request:setRemoteAddr() #771
         +---[0.16% 0.002866ms ] org.apache.catalina.connector.Request:setRemoteHost() #772
         +---[0.15% 0.002725ms ] org.apache.catalina.connector.Request:setSecure() #773
         +---[0.20% 0.00363ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #774
         +---[0.19% 0.003448ms ] org.apache.coyote.Request:scheme() #774
         +---[0.14% 0.002545ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #774
         +---[0.16% 0.002889ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #775
         +---[0.19% 0.00342ms ] org.apache.coyote.Request:serverName() #775
         +---[0.17% 0.00297ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #775
         +---[0.22% 0.003904ms ] org.apache.catalina.valves.RemoteIpValve:isChangeLocalName() #776
         +---[0.20% 0.00363ms ] org.apache.catalina.connector.Request:setServerPort() #779
         +---[0.17% 0.003065ms ] org.apache.catalina.connector.Request:setLocalPort() #780
         +---[0.16% 0.002877ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #782
         +---[0.18% 0.003204ms ] org.apache.coyote.Request:getMimeHeaders() #782
         +---[0.22% 0.003937ms ] org.apache.tomcat.util.http.MimeHeaders:removeHeader() #784
         `---[0.17% 0.003077ms ] org.apache.tomcat.util.http.MimeHeaders:removeHeader() #790
复制代码

测试环境调用栈

 `---ts=2022-11-14 21:02:40;thread_name=http-nio-8080-exec-5;id=aa;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@42f85fa4
     `---[100.85578ms] org.apache.catalina.valves.RemoteIpValve:invoke()
         +---[0.02% 0.016252ms ] org.apache.catalina.connector.Request:getRemoteAddr() #613
         +---[0.00% 0.002572ms ] org.apache.catalina.connector.Request:getRemoteHost() #614
         +---[0.00% 0.001721ms ] org.apache.catalina.connector.Request:getScheme() #615
         +---[0.00% 0.001826ms ] org.apache.catalina.connector.Request:isSecure() #616
         +---[0.00% 0.001719ms ] org.apache.catalina.connector.Request:getServerName() #617
         +---[0.00% 0.002139ms ] org.apache.catalina.valves.RemoteIpValve:isChangeLocalName() #618
         +---[0.00% 0.004452ms ] org.apache.catalina.connector.Request:getServerPort() #619
         +---[0.00% 0.002919ms ] org.apache.catalina.connector.Request:getLocalPort() #620
         +---[0.00% 0.001888ms ] org.apache.catalina.connector.Request:getHeader() #621
         +---[0.00% 0.003649ms ] org.apache.catalina.connector.Request:getHeader() #622
         +---[0.00% 0.0024ms ] org.apache.catalina.connector.Request:getHeaders() #632
         +---[0.01% 0.011289ms ] org.apache.catalina.valves.RemoteIpValve:commaDelimitedListToStringArray() #640
         +---[0.00% 0.001824ms ] org.apache.catalina.connector.Request:setRemoteAddr() #667
         +---[0.00% 0.001824ms ] org.apache.catalina.connector.Request:getConnector() #668
         +---[0.00% 0.002051ms ] org.apache.catalina.connector.Connector:getEnableLookups() #668
         +---[0.00% 0.001576ms ] org.apache.catalina.connector.Request:setRemoteHost() #682
         +---[0.00% 0.001515ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #686
         +---[0.00% 0.00188ms ] org.apache.coyote.Request:getMimeHeaders() #686
         +---[0.00% 0.001862ms ] org.apache.tomcat.util.http.MimeHeaders:removeHeader() #686
         +---[0.00% 0.004026ms ] org.apache.tomcat.util.buf.StringUtils:join() #694
         +---[0.00% 0.001333ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #695
         +---[0.00% 0.001517ms ] org.apache.coyote.Request:getMimeHeaders() #695
         +---[0.00% 0.001971ms ] org.apache.tomcat.util.http.MimeHeaders:setValue() #695
         +---[0.00% 0.001817ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #695
         +---[0.00% 0.00168ms ] org.apache.catalina.connector.Request:getHeader() #700
         +---[0.00% 0.003129ms ] org.apache.catalina.valves.RemoteIpValve:isForwardedProtoHeaderValueSecure() #704
         +---[0.00% 0.001654ms ] org.apache.catalina.connector.Request:setSecure() #709
         +---[0.00% 0.001344ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #710
         +---[0.00% 0.002058ms ] org.apache.coyote.Request:scheme() #710
         +---[0.00% 0.001186ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #710
         +---[0.00% 0.002904ms ] org.apache.catalina.valves.RemoteIpValve:setPorts() #711
         +---[0.00% 0.001491ms ] org.apache.catalina.connector.Request:getHeader() #716
         +---[0.00% 0.003306ms ] org.apache.tomcat.util.http.parser.Host:parse() #719
         +---[0.00% 0.001296ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #725
         +---[0.00% 0.001337ms ] org.apache.coyote.Request:serverName() #725
         +---[0.00% 0.001271ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #725
         +---[0.00% 0.001253ms ] org.apache.catalina.valves.RemoteIpValve:isChangeLocalName() #726
         +---[0.00% 0.003314ms ] org.apache.catalina.connector.Request:setAttribute() #736
         +---[0.02% 0.015683ms ] org.apache.juli.logging.Log:isDebugEnabled() #738
         +---[0.00% 0.00135ms ] org.apache.catalina.connector.Request:getRemoteAddr() #756
         +---[0.00% 0.001719ms ] org.apache.catalina.connector.Request:setAttribute() #755
         +---[0.00% 0.001249ms ] org.apache.catalina.connector.Request:getRemoteAddr() #758
         +---[0.00% 0.001294ms ] org.apache.catalina.connector.Request:setAttribute() #757
         +---[0.00% 0.00128ms ] org.apache.catalina.connector.Request:getRemoteHost() #760
         +---[0.00% 0.001485ms ] org.apache.catalina.connector.Request:setAttribute() #759
         +---[0.00% 0.002571ms ] org.apache.catalina.connector.Request:getProtocol() #762
         +---[0.00% 0.001857ms ] org.apache.catalina.connector.Request:setAttribute() #761
         +---[0.01% 0.008126ms ] org.apache.catalina.connector.Request:getServerName() #764
         +---[0.00% 0.001972ms ] org.apache.catalina.connector.Request:setAttribute() #763
         +---[0.00% 0.001452ms ] org.apache.catalina.connector.Request:getServerPort() #766
         +---[0.00% 0.001782ms ] org.apache.catalina.connector.Request:setAttribute() #765
         +---[0.00% 0.001442ms ] org.apache.catalina.valves.RemoteIpValve:getNext() #769
         +---[99.65% 100.500217ms ] org.apache.catalina.Valve:invoke() #769
         +---[0.00% 0.002653ms ] org.apache.catalina.connector.Request:setRemoteAddr() #771
         +---[0.00% 0.001491ms ] org.apache.catalina.connector.Request:setRemoteHost() #772
         +---[0.00% 0.00171ms ] org.apache.catalina.connector.Request:setSecure() #773
         +---[0.00% 0.001662ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #774
         +---[0.00% 0.00229ms ] org.apache.coyote.Request:scheme() #774
         +---[0.00% 0.001822ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #774
         +---[0.00% 0.00142ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #775
         +---[0.00% 0.002272ms ] org.apache.coyote.Request:serverName() #775
         +---[0.00% 0.001255ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #775
         +---[0.00% 0.002824ms ] org.apache.catalina.valves.RemoteIpValve:isChangeLocalName() #776
         +---[0.00% 0.001433ms ] org.apache.catalina.connector.Request:setServerPort() #779
         +---[0.00% 0.001558ms ] org.apache.catalina.connector.Request:setLocalPort() #780
         +---[0.00% 0.001666ms ] org.apache.catalina.connector.Request:getCoyoteRequest() #782
         +---[0.00% 0.001526ms ] org.apache.coyote.Request:getMimeHeaders() #782
         +---[0.00% 0.002167ms ] org.apache.tomcat.util.http.MimeHeaders:removeHeader() #784
         +---[0.00% 0.002147ms ] org.apache.tomcat.util.http.MimeHeaders:setValue() #792
         `---[0.00% 0.001765ms ] org.apache.tomcat.util.buf.MessageBytes:setString() #792
复制代码

对比代码发现dev环境的确执行了删除header的操作

简单解释X-Forwarded-For的作用

例如真正的客户端是Client1,通过代理服务器proxy1,proxy2,到达服务器,在Tomcat中执行获取客户端地址的方法:request.getRemoteAddr,获得的IP地址是proxy2的,也就是负载均衡的地址; 而如果你想要获取Client1的地址,也是可以获取到的,就是通过X-Forwarded-For字段; X-Forwarded-For:简称XFF头,它代表客户端,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。 X-Forwarded-For内置在Http协议头中,刚刚的场景X-Forwarded-For取值为:client1, proxy1, proxy2

仔细探究RemoteIpValve代码逻辑 和目前问题相关的大致功能为:解析X-Forwarded-for请求头,将其中的远端地址设置到RemoteAddr中,判断是否要删除X-Forwarded-for请求头 具体逻辑为: 对于列表中的每个ip,如果属于内网地址则跳过,否则将此ip设置为远程ip,停止循环

 //最近一跳代理地址
 final String originalRemoteAddr = request.getRemoteAddr();
 ...
 //X-Forwarded-For头信息
 final String originalRemoteIpHeader = request.getHeader(remoteIpHeader);
 //最近一跳代理地址是否为内网
 // 内网正则判断为 Pattern internalProxies = Pattern.compile(
         "10\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +
         "192\.168\.\d{1,3}\.\d{1,3}|" +
         "169\.254\.\d{1,3}\.\d{1,3}|" +
         "127\.\d{1,3}\.\d{1,3}\.\d{1,3}|" +
         "172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|" +
         "172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|" +
         "172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|" +
         "0:0:0:0:0:0:0:1|::1");
 //originalRemoteAddr 所有环境都为内网地址,返回true
 boolean isInternal = internalProxies != null &&
         internalProxies.matcher(originalRemoteAddr).matches();
 //trustedProxies为空
 if (isInternal || (trustedProxies != null &&
         trustedProxies.matcher(originalRemoteAddr).matches())) {
     String remoteIp = null;
     Deque<String> proxiesHeaderValue = new LinkedList<>();
     StringBuilder concatRemoteIpHeaderValue = new StringBuilder();
  
     for (Enumeration<String> e = request.getHeaders(remoteIpHeader); e.hasMoreElements();) {
         if (concatRemoteIpHeaderValue.length() > 0) {
             concatRemoteIpHeaderValue.append(", ");
         }
  
         concatRemoteIpHeaderValue.append(e.nextElement());
     }
     //X-Forwarded-For内的地址转换为数组
     String[] remoteIpHeaderValue = commaDelimitedListToStringArray(concatRemoteIpHeaderValue.toString());
     int idx;
     if (!isInternal) {
         proxiesHeaderValue.addFirst(originalRemoteAddr);
     }
     //从最后的地址循环
     for (idx = remoteIpHeaderValue.length - 1; idx >= 0; idx--) {
         String currentRemoteIp = remoteIpHeaderValue[idx];
         remoteIp = currentRemoteIp;
         //如果是内网地址则跳过
         if (internalProxies !=null && internalProxies.matcher(currentRemoteIp).matches()) {
             
         //trustedProxies目前配置为空
         } else if (trustedProxies != null &&
                 trustedProxies.matcher(currentRemoteIp).matches()) {
             proxiesHeaderValue.addFirst(currentRemoteIp);
         } else {
         //找到第一个不是内网地址的idx,但如果只有一个地址,则idx会变成负数
             idx--;
             break;
         }
     }
     //重新构建客户端地址的list,但idx必须不小于0
     LinkedList<String> newRemoteIpHeaderValue = new LinkedList<>();
     for (; idx >= 0; idx--) {
         String currentRemoteIp = remoteIpHeaderValue[idx];
         newRemoteIpHeaderValue.addFirst(currentRemoteIp);
     }
      
 ...
     //如果newRemoteIpHeaderValue为空则删除X-Forwarded-For
     if (newRemoteIpHeaderValue.size() == 0) {
          request.getCoyoteRequest().getMimeHeaders().removeHeader(remoteIpHeader);
     }
复制代码

总结一下逻辑

 X-Forwarded-For中的地址集合从后往前取,在至少有两个地址并且最后的地址是内网地址的情况下不会删除X-Forwarded-For请求头,如果只有一个地址无论是不是内网地址都会删除这个请求头
复制代码

看一下dev、test、线上的数据

 //test环境 172.31.0.203为内网地址
 X-Forwarded-For: 204.187.160.86, 100.118.125.137, 172.31.0.203
 //dev环境172.30.1.118为内网地址
 X-Forwarded-For: 172.30.1.118
 //线上环境 117.136.68.19为外网地址,但只有一个ip,没有代理的地址
 X-Forwarded-For: 117.136.68.19
复制代码

所以dev和线上会被删去请求头

处理

在wb-base-component-starter中添加公共配置

 server:
     forward-headers-strategy: none
复制代码

不加载tomcat的RemoteIpValve

 

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

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

相关文章

SpringCloud整合Nacos最全教程(简介及安装部署整合)

目录 一、Nacos简介 Nacos与eureka的共同点 Nacos与Eureka的区别 二、Nacos安装配置 在windows中的安装教程 1.首先将windows安装版本的zip解压&#xff1a; 2.如果8848端口被占用&#xff0c;可以修改端口 3.进入到bin目录下&#xff0c;在cmd中运行以下命令启动 4.启…

Webpack 5 超详细解读(二)

11.importLoaders 属性 问题&#xff1a; test.css的内容如下&#xff1a; .title {transition: all .5s;user-select: none; }login.css的内容如下&#xff1a; /* 导入test.css */ import ./test.css; .title {color: #12345678; }再次npm run build发现运行之后的test.c…

外汇天眼:想通过外汇交易在几个月内成为亿万富翁吗?你必须知道的七大交易法则

WikiFX 策略 -这里有七个交易规则&#xff0c;将在不同程度上让您受益。 1.交易不是儿戏 这是一项业务&#xff0c;如果没有适当的计划、战略和有效的运营&#xff0c;就不可能取得长期的成功。 2.损失不可避免 由于市场始终存在风险&#xff0c;因此在您的交易中从多头转为…

C++:项目相互依赖调用解决方法两种方法

Bmodel依赖于Amodel&#xff0c;但是Amodel又需要BModel的信息。这样就会导致相互依赖。 方法一&#xff1a;采用静态变量static 链接&#xff1a;C开发中一个解决方案里&#xff0c;两个项目的相互引用&#xff0c;相互依赖的实现方法&#xff08;解决方法&#xff09;_Capri…

P物质肽[DArg1, DTrp5, 7, 9, Leu11]

这种物质P类似物是一种非常有效的小细胞肺癌(SCLC)细胞体外生长的广谱神经肽抑制剂(IC₅₀ 5M)。此外&#xff0c;它在体外有效地抑制信号转导通路&#xff0c;并在体内显著延缓SCLC异种移植物的生长。因此&#xff0c;它可能对SCLC有治疗价值。 编号: 139994中文名称: P物质肽…

数据结构学习笔记(Ⅰ):绪论

课程链接:【旧版】1.0_开篇_数据结构在学什么_哔哩哔哩_bilibili 目录 1 数据结构的基本概念 2 算法 2.1 算法的基本概念 1.算法概念 2.算法的特性 3.好算法特质 2.2 算法的时间复杂度 2.3 算法的空间复杂度 1 数据结构的基本概念 数据&#xff1a;能输入到计算机中并…

Android StudioJNI开发之NDK环境的搭建以及添加JNI支持(图文解释 简单易懂)

有问题可以评论区留言讨论~~~ 一、NDK环境搭建 Android系统的所谓原生开发是在App中调用C/C代码&#xff0c;鉴于这两个语言具有跨平台的特性&#xff0c;如果某项功能使用C/C实现&#xff0c;就很容易在不同平台之间移植。 完整的Android环境包括三个开发工具。分别是JDK SD…

如何缩减layout电路面积?减少晶体管的数量——以全加器为例【VLSI】

如何缩减layout电路面积&#xff1f;减少晶体管的数量——以全加器为例【VLSI】What is Full adder ?全加器的设计方法1. 32T 原始表达式不经过化简的电路图2. 28个晶体管 最基本的静态互补CMOS电路的全加器静态互补CMOS静态互补CMOS的优势与劣势28T 电路图28T的棒状图Stick D…

彻底搞明白概率论:随机事件,样本空间,必然事件,不可能事件

文章目录样本空间样本点随机事件&#xff0c;必然事件&#xff0c;不可能事件参考视频样本空间 随机试验E的一切可能基本结果&#xff08;或实验过程如取法或分配法&#xff09;组成的集合称为E的样本空间&#xff0c;记为S 注意&#xff0c;对于不同的实验&#xff0c;样本空间…

【构建ML驱动的应用程序】第 3 章 :构建您的第一个端到端管道

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

FastDFS安装

fastdfs架构图 准备安装包 libfastcommon-1.0.43.tar.gz fastdfs-6.06.tar.gz fastdfs-nginx-module-1.22.tar.gz 一 、FastDFS--tracker安装 FastDFS安装环境 FastDFS是C语言开发&#xff0c;建议在linux上运行&#xff0c;本教程使用Centos7.4作为安装环境。 安装gcc …

如何使用做一个弹幕效果

效果展示 前置准备 背景图 图片素材 具体步骤 添加一个图片背景 添加一个水平滚动容器 添加一个容器 制作弹幕字体 制作弹幕动画效果 步骤分解 添加一个图片背景 拖拽 图片组件 到 编辑区 选中 图片组件 点击 检查面板 中的 样式 调整 图片组件 的 样式 添加一个水平滚动容…

餐饮机器人AB面:有人离场、有人挺进

民以食为天&#xff0c;餐饮生意是一颗“常青树”。 餐饮行业“常青”不代表没有变化&#xff0c;近年来Z世代消费者对餐饮口味、餐饮效率和餐饮安全提出更高要求。与此同时&#xff0c;在新消费、新技术、新模式的推动下&#xff0c;餐饮行业衍生出多元化趋势&#xff0c;外卖…

详解环境变量

目录前言一、什么是环境变量&#xff1f;二、查看环境变量的方法三、查看环境变量的内容四、普通变量VS环境变量五、导出环境变量六、常见的环境变量七、set命令&#xff1a;查看普通变量或环境变量八、C/C语言中main函数中的参数1、main函数的第一个和第二个参数应用&#xff…

IDEA提交本地项目到Gitee远程仓库

上一篇【Git的安装、配置、使用02【Idea对GitHub支持使用】】https://liush.blog.csdn.net/article/details/123446538 1. 新建仓库 2. IDEA登录gitee账号 3. 选择当前的项目作为本地库&#xff08;相当于初始化&#xff09; 4. 将项目添加到暂存区中 5. 将暂存区项目文件提交…

【CSS】CSS文本样式【CSS基础知识详解】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 本文章收录于专栏 【CSS】 【CSS专栏】已发布文章 &#x1f4c1;【CSS基础认知】 &#x1f4c1;【CSS选择器全解指南】 &#x1f4c1…

十万部冷知识:“澳大利亚”为什么属于亚洲球队?

在2022年卡塔尔世界杯上&#xff0c;总共有6支球队入围&#xff0c;他们分别是日本队&#xff0c;韩国队&#xff0c;沙特队&#xff0c;伊朗队&#xff0c;澳大利亚队&#xff0c;还有就是东道主卡塔尔队。但是我们知道&#xff0c;澳大利亚&#xff0c;并不是亚洲的国家&…

Flutter 项目中管理你的 Assets Texts Widgets

Flutter 项目中管理你的 Assets Texts Widgets 原文 https://medium.com/mustafatahirhussein/managing-your-assets-texts-and-widgets-professionally-a-must-read-flutter-guide-ffb93b19eff0 前言 这篇文章是 Mustafa Tahir 写的关于 Flutter 项目管理的技巧&#xff0c;可…

音视频SDP协议详解(描述会话的协议)

前言 ①SDP协议是会话描述协议&#xff08;Session Description Protocol&#xff09;的缩写&#xff0c;是一种会话描述格式&#xff0c;一种描述流媒体初始化参数的格式&#xff0c;为描述多媒体数据而设计。 文末卡片领取音视频免费资料合集 &#xff08;流媒体是指在传输过…

csdn月入过万的作者是如何练成的?

很多年前&#xff0c;我有一个成为作家的梦想。 后来从事了技术&#xff0c;觉得与作家梦越来越远了。 虽然梦想远去&#xff0c;但写字的欲望没有停止。 这些年&#xff0c;一直在有道云笔记上记录自己的工作心得&#xff0c;偶尔会来csdn上写一写。 我在csdn真正发力的时候…