Spring Cloud Gateway + Nacos 灰度发布

前言

本文将会使用 SpringCloud Gateway 网关组件配合 Nacos 实现灰度发布(金丝雀发布)

环境搭建

创建子模块服务提供者 provider,网关模块 gateway

父项目

pom.xml 配置

<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.example</groupId>  <artifactId>spring-gateway-demo</artifactId>  <version>0.0.1-SNAPSHOT</version>  <packaging>pom</packaging>  <name>spring-gateway-demo</name>  <description>spring-gateway-demo</description>  <properties>  <java.version>11</java.version>  <maven.compiler.source>11</maven.compiler.source>  <maven.compiler.target>11</maven.compiler.target>  <maven.compiler.plugin>3.8.1</maven.compiler.plugin>  <spring-boot.version>2.3.7.RELEASE</spring-boot.version>  <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>  <spring-cloud.version>Hoxton.SR9</spring-cloud.version>  <spring-cloud-starter-alibaba-nacos-config>2.2.0.RELEASE</spring-cloud-starter-alibaba-nacos-config>  </properties>  <modules>  <module>provider</module>  <module>gateway</module>  </modules>  <dependencies>  <dependency>  <groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>  </dependency>  <dependency>  <groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>  </dependency>  </dependencies>  <dependencyManagement>  <dependencies>  <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-dependencies</artifactId>  <version>${spring-cloud.version}</version>  <type>pom</type>  <scope>import</scope>  </dependency>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-dependencies</artifactId>  <version>${spring-boot.version}</version>  <type>pom</type>  <scope>import</scope>  </dependency>  <dependency>  <groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-alibaba-dependencies</artifactId>  <version>${spring-cloud-alibaba.version}</version>  <type>pom</type>  <scope>import</scope>  </dependency>  </dependencies>  </dependencyManagement>  
</project>

服务提供者 provider

这里我们计划引入 nacos, 所以先创建一个 nacos 配置文件 dataId 为 provider.properties, 这里用默认的命名空间 public, 默认分组 DEFAULT_GROUP

version=2

provider 的 pom 配置依赖

<dependencies>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-web</artifactId>  </dependency>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-test</artifactId>  <scope>test</scope>  <exclusions>  <exclusion>  <groupId>org.junit.vintage</groupId>  <artifactId>junit-vintage-engine</artifactId>  </exclusion>  </exclusions>  </dependency>  
</dependencies>

application.yml

server:  port: 9001  
spring:  application:  name: provider  cloud:  nacos:  config:  server-addr: 127.0.0.1:8848  discovery:  server-addr: 127.0.0.1:8848

启动类上添加 @EnableDiscoveryClient 注解

@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}}

然后添加测试 controller

@RefreshScope
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate Environment env;@Value("${version:0}")private String version;/*** http://localhost:9001/test/port* @return*/@GetMapping("/port")public Object port() {return String.format("port=%s, version=%s", env.getProperty("local.server.port"), version);}
}

注意,这里配置 nacos 的时候需要配置下面两个文件 provider.propertiesprovider,然后实际配置最终 nacos 是采用的 provider 文件,否则后端控制台就会持续输出 400 错误,可能是新版本问题,其他版本暂时不清楚(后面网关配置也是同理)

image.png

后端控制台输出,也可以看出需要两个

[fixed-localhost_8848] [subscribe] provider.properties+DEFAULT_GROUP
[fixed-localhost_8848] [add-listener] ok, tenant=, dataId=provider.properties, group=DEFAULT_GROUP, cnt=1
[fixed-localhost_8848] [subscribe] provider+DEFAULT_GROUP
[fixed-localhost_8848] [add-listener] ok, tenant=, dataId=provider, group=DEFAULT_GROUP, cnt=1

Nacos 版本:2.3.0-BETA

网关 gateway

gateway 服务的 pom 依赖配置如下:

    <dependencies>  <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-gateway</artifactId>  </dependency>  <dependency>  <groupId>org.projectlombok</groupId>  <artifactId>lombok</artifactId>  <optional>true</optional>  </dependency>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-test</artifactId>  <scope>test</scope>  <exclusions>  <exclusion>  <groupId>org.junit.vintage</groupId>  <artifactId>junit-vintage-engine</artifactId>  </exclusion>  </exclusions>  </dependency>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-actuator</artifactId>  </dependency>  </dependencies>  

application.yml

# 应用服务 WEB 访问端口  
server:  port: 9000  
# 应用名称  
spring:  application:  name: gateway  cloud:  nacos:  config:  server-addr: 127.0.0.1:8848  discovery:  server-addr: 127.0.0.1:8848  gateway:  routes: # http://127.0.0.1:9000/actuator/gateway/routes  - id: provider  # 路由 ID,保持唯一  uri: lb://provider # uri指目标服务地址,lb代表从注册中心获取服务  predicates:  - Path=/provider/**  # http://127.0.0.1:9000/provider/port 会转发到 http://localhost:9001/provider/port, 和预期不符合, 需要StripPrefix来处理  filters:  - StripPrefix=1 # StripPrefix=1就代表截取路径的个数为1, 这样请求 http://127.0.0.1:9000/provider/test/port 会转发到 http://localhost:9001/test/port  
management:  endpoint:  gateway:  enabled: true  endpoints:  web:  exposure:  include: gateway

同样启动类上添加 @EnableDiscoveryClient 注解

查看所有路由:/actuator/gateway/routes
查看指定路由(GET):/actuator/gateway/routes/{id}
查看全局过滤器:/actuator/gateway/globalfilters
查看路由过滤器:/actuator/gateway/routefilters
POST 方式刷新路由缓存:/actuator/gateway/refresh

测试

curl http://127.0.0.1:9001/test/port
port=9001, version=2
curl http://127.0.0.1:9000/provider/test/port
port=9001, version=2

动态路由

实现动态路由有两种方式,一个是改写 RouteDefinitionRepository(实测失败),一个是基于 nacos 的监听器给 RouteDefinitionRepository 动态更新值。实现逻辑大同小异

Spring Cloud Gateway 中加载路由信息分别由以下几个类负责
1、PropertiesRouteDefinitionLocator:从配置文件中读取路由信息 (如 YML、Properties 等)
2、RouteDefinitionRepository:从存储器中读取路由信息 (如内存、配置中心、Redis、MySQL 等)
3、DiscoveryClientRouteDefinitionLocator:从注册中心中读取路由信息(如 Nacos、Eurka、Zookeeper 等)

下面使用 RouteDefinitionRepository 配置动态路由

gateway-router.json

[{"id": "provider","predicates": [{"name": "Path","args": {"_genkey_0": "/provider/**"}}],"filters": [{"name": "StripPrefix","args": {"_genkey_0": "1"}}],"uri": "lb://provider","order": 0
}]

NacosRouteDefinitionRepository 配置类

@Component  
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository, ApplicationEventPublisherAware {  private static final Logger log = LoggerFactory.getLogger(NacosRouteDefinitionRepository.class);  @Autowired  private NacosConfigManager nacosConfigManager;  // 更新路由信息需要的  private ApplicationEventPublisher applicationEventPublisher;  private String dataId = "gateway-router.json";  private String group = "DEFAULT_GROUP";  @Value("${spring.cloud.nacos.config.server-addr}")  private String serverAddr;  private ObjectMapper objectMapper = new ObjectMapper();  @PostConstruct  public void dynamicRouteByNacosListener() {  try {  nacosConfigManager.getConfigService().addListener(dataId, group, new Listener() {  public void receiveConfigInfo(String configInfo) {  log.info("自动更新配置...\r\n{}", configInfo);  applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));  }  public Executor getExecutor() {  return null;  }  });  } catch (NacosException e) {  e.printStackTrace();  }  }  @Override  public Flux<RouteDefinition> getRouteDefinitions() {  try {  String configInfo = nacosConfigManager.getConfigService().getConfig(dataId, group, 5000);  List<RouteDefinition> gatewayRouteDefinitions = objectMapper.readValue(configInfo, new TypeReference<List<RouteDefinition>>() {  });  return Flux.fromIterable(gatewayRouteDefinitions);  } catch (NacosException e) {  e.printStackTrace();  } catch (JsonMappingException e) {  e.printStackTrace();  } catch (JsonProcessingException e) {  e.printStackTrace();  }  return Flux.fromIterable(Lists.newArrayList());  }  @Override  public Mono<Void> save(Mono<RouteDefinition> route) {  return null;  }  @Override  public Mono<Void> delete(Mono<String> routeId) {  return null;  }  @Override  public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {  this.applicationEventPublisher = applicationEventPublisher;  }  
}

然后重启网关,访问 http://127.0.0.1:9000/actuator/gateway/routes ,查看是否生效

[{"predicate": "Paths: [/provider/**], match trailing slash: true","route_id": "provider","filters": ["[[StripPrefix parts = 1], order = 1]"],"uri": "lb://provider","order": 0}
]

灰度发布

首先需要明白灰度的场景, 因为有不同版本的服务需要共存, 所以新的节点升级的时候必然代码及配置会存在差别, 所以我们根据这种差别来判断服务版本是新版本还是线上稳定版本。这里我们用 prod 和 gray 来标识 2 个版本。

实现的整体思路:

  1. 编写带版本号的灰度路由(负载均衡策略)
  2. 编写自定义 filter
  3. nacos 服务配置需要灰度发布的服务的元数据信息以及权重(在服务 jar 中配置)

注意, 应该先修改 nacos 配置实现动态路由, 然后再升级灰度节点. 本案例只是简单示例灰度原理。

下面进行网关配置

依赖配置

首先排除掉默认的 ribbon 依赖

        <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><exclusions><exclusion><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></exclusion></exclusions></dependency>

引入官方新的负载均衡包

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>

负载均衡策略

public class VersionGrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;private String serviceId;private final AtomicInteger position;public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {HttpHeaders headers = (HttpHeaders) request.getContext();ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return ((Flux) supplier.get()).next().map(list -> processInstanceResponse((List<ServiceInstance>) list, headers));}private Response<ServiceInstance> processInstanceResponse(List<ServiceInstance> instances, HttpHeaders headers) {if (instances.isEmpty()) {return new EmptyResponse();} else {String reqVersion = headers.getFirst("version");if (StringUtils.isEmpty(reqVersion)) {return processRibbonInstanceResponse(instances);}List<ServiceInstance> serviceInstances = instances.stream().filter(instance -> reqVersion.equals(instance.getMetadata().get("version"))).collect(Collectors.toList());if (serviceInstances.size() > 0) {return processRibbonInstanceResponse(serviceInstances);} else {return processRibbonInstanceResponse(instances);}}}/*** 负载均衡器* 参考 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse** @author javadaily*/private Response<ServiceInstance> processRibbonInstanceResponse(List<ServiceInstance> instances) {int pos = Math.abs(this.position.incrementAndGet());ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);}
}

过滤器加载负载均衡

public class GrayReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {  private static final Log log = LogFactory.getLog(ReactiveLoadBalancerClientFilter.class);  private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;  private final LoadBalancerClientFactory clientFactory;  private LoadBalancerProperties properties;  public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {  this.clientFactory = clientFactory;  this.properties = properties;  }  @Override  public int getOrder() {  return LOAD_BALANCER_CLIENT_FILTER_ORDER;  }  @Override  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);  String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);  if (url != null && ("grayLb".equals(url.getScheme()) || "grayLb".equals(schemePrefix))) {  ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);  if (log.isTraceEnabled()) {  log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);  }  return this.choose(exchange).doOnNext((response) -> {  if (!response.hasServer()) {  throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());  } else {  URI uri = exchange.getRequest().getURI();  String overrideScheme = null;  if (schemePrefix != null) {  overrideScheme = url.getScheme();  }  DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance((ServiceInstance) response.getServer(), overrideScheme);  URI requestUrl = this.reconstructURI(serviceInstance, uri);  if (log.isTraceEnabled()) {  log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);  }  exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);  }  }).then(chain.filter(exchange));  } else {  return chain.filter(exchange);  }  }  protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {  return LoadBalancerUriTools.reconstructURI(serviceInstance, original);  }  private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {  URI uri = (URI) exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);  VersionGrayLoadBalancer loadBalancer = new VersionGrayLoadBalancer(clientFactory.getLazyProvider(uri.getHost(), ServiceInstanceListSupplier.class), uri.getHost());  if (loadBalancer == null) {  throw new NotFoundException("No loadbalancer available for " + uri.getHost());  } else {  return loadBalancer.choose(this.createRequest(exchange));  }  }  private Request createRequest(ServerWebExchange exchange) {  HttpHeaders headers = exchange.getRequest().getHeaders();  Request<HttpHeaders> request = new DefaultRequest<>(headers);  return request;  }  
}

注入过滤器

@Configuration
public class GrayGatewayReactiveLoadBalancerClientAutoConfiguration {@Bean@ConditionalOnMissingBean({GrayReactiveLoadBalancerClientFilter.class})public GrayReactiveLoadBalancerClientFilter grayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {return new GrayReactiveLoadBalancerClientFilter(clientFactory, properties);}
}

发布灰度服务

生产环境配置文件 application-prod.yml

server:  port: 9002  
spring:  application:  name: provider  cloud:  nacos:  config:  server-addr: 127.0.0.1:8848  discovery:  metadata:  version: prod  server-addr: 127.0.0.1:8848

灰度环境配置文件 application-gray.yml

server:  port: 9003  
spring:  application:  name: provider  cloud:  nacos:  config:  server-addr: 127.0.0.1:8848  discovery:  metadata:  version: gray  server-addr: 127.0.0.1:8848

idea 启动参数指定配置文件

image.png

同时注意配置两个环境的 nacos 文件(prod version: 4,gray version: 5

image.png

测试

然后分别启动三个服务:9000 端口网关 gateway 服务,9002 端口生产环境 provider-prod 服务,9003 端口灰度环境 provider-gray 服务

E:\Nacos\nacos>curl http://127.0.0.1:9000/provider/test/port
port=9003, version=5
E:\Nacos\nacos>curl -X GET -H "version:prod" http://127.0.0.1:9000/provider/test/port
port=9003, version=5
E:\Nacos\nacos>curl -X GET -H "version:gray" http://127.0.0.1:9000/provider/test/port
port=9002, version=4

仓库地址

如果对于文章中代码有疑问,可以直接查看下方作者仓库

仓库地址:ReturnTmp/spring-gateway-demo: 网关配置 + 灰度发布 + 配置中心 示例仓库 (github.com)

参考链接

  • Spring Cloud Gateway+nacos灰度发布 - 掘金 (juejin.cn)
  • SpringCloud gateway Actuator - shigp1 - 博客园 (cnblogs.com)
  • Spring Cloud Gateway监控 周立的博客
  • 前端灰度发布落地方案-腾讯云开发者社区-腾讯云 (tencent.com)
  • 后端 - 灰度发布架构设计!值得品鉴

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

【Spring实战】22 Spring Actuator 入门

文章目录 1. 定义2. 功能3. 依赖4. 配置5. 常用的应用场景1&#xff09;环境监控2&#xff09;运维管理3&#xff09;性能优化 结论 Spring Actuator 是 Spring 框架的一个模块&#xff0c;为开发人员提供了一套强大的监控和管理功能。本文将深入探讨 Spring Actuator 的定义、…

【HTML5】第1章 HTML5入门

学习目标 了解网页基本概念&#xff0c;能够说出网页的构成以及网页相关名词的含义 熟悉Web标准&#xff0c;能够归纳Web标准的构成。 了解浏览器&#xff0c;能够说出各主流浏览器的特点。 了解HTML5技术&#xff0c;能够知道HTML5发展历程、优势以及浏览器对HTML5的支持情…

【QT】QStandardItemModel类的应用介绍

目录 1 概述 2 常用方法 3 QStandardItemModel的使用 3.1 界面设计与主窗口类定义 3.2 系统初始化 3.3 从文本文件导入数据 3.4 数据修改 3.5 单元格格式设置 3.6 数据另存为文件 1 概述 QStandardItemModel是标准的以项数据&#xff08;itemdata&#xff09;为基础的…

FPGA项目(14)——基于FPGA的数字秒表设计

1.功能设计 设计内容及要求: 1.秒表最大计时范围为99分59. 99秒 2.6位数码管显示&#xff0c;分辨率为0.01秒 3.具有清零、启动计时、暂停及继续计时等功能 4.控制操作按键不超过二个。 2.设计思路 所采用的时钟为50M&#xff0c;先对时钟进行分频&#xff0c;得到100HZ频率…

CSS 放大翻转动画

<template><div class="container" @mouseenter="startAnimation" @mouseleave="stopAnimation"><!-- 旋方块 --><div class="box" :class="{ rotate-scale-up-hor: isAnimating }"><!-- 元素内…

macosx编译qgroundcontrol源码(Qt6.7)

1.克隆源码: clone --recursive http://github.com/mavlink/qgroundcontrol.git 克隆成功 3.编译 编译环境要求: 编译方法: 使用QtCreator编译 使用命令行编译 打开QGroundControl.pro并编译IOS版本 旧版本使用Qt 5.15.2 run qmake 新版本使用Qt 6.6或者更高 IOS工程输出要…

mysql5.7安装-windows安装版本

下载地址 官网地址:https://www.mysql.com/官网下载地址:https://dev.mysql.com/downloads/mysql/阿里云镜像站下载:https://mirrors.aliyun.com/mysql/华为云镜像站地址:https://mirrors.huaweicloud.com/home华为云镜像站下载:https://mirrors.huaweicloud.com/mysql/Downlo…

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON)

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON) 1 时钟管理单元(CMU)2 集群配置模块(CCM)3 时基单元(TBU)4 监控单元(MON)5 总结由前文的各篇内容,开发者已经知道如何使用GTM的大部分功能,在这些功能中,都需要一个信息就是fGTM 的数据,我们在前…

技术查漏补缺(1)Logback

一、下定义&#xff1a;Logback是一个开源的日志组件 二、Logback的maven <!--这个依赖直接包含了 logback-core 以及 slf4j-api的依赖--> <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><v…

excel统计分析——两因素无重复方差分析

参考资料&#xff1a;生物统计学 从严格意义上讲&#xff0c;两因素试验都应当设置重复观测值&#xff0c;以便检验交互作用是否真实存在&#xff0c;对试验误差有更准确的估计&#xff0c;从而提高检验效率。但根据专业知识或先前的试验已经证明两个因素不存在交互作用时&…

算法每日一题: 被列覆盖的最多行数 | 二进制 - 状态压缩

大家好&#xff0c;我是星恒 今天的题目又是一道有关二进制的题目&#xff0c;有我们之前做的那道 参加考试的最大学生数的 感觉&#xff0c;哈哈&#xff0c;当然&#xff0c;比那道题简单多了&#xff0c;这道题感觉主要的考点就是二进制&#xff0c;大家可以好好总结一下这道…

JVM加载class文件的原理机制

1、JVM 简介 JVM 是我们Javaer 的最基本功底了&#xff0c;刚开始学Java 的时候&#xff0c;一般都是从“Hello World ”开始的&#xff0c;然后会写个复杂点class &#xff0c;然后再找一些开源框架&#xff0c;比如Spring &#xff0c;Hibernate 等等&#xff0c;再然后就开发…

【数值分析】非线性方程求根,牛顿法,牛顿下山法,matlab实现

4. 牛顿法 收敛时牛顿法的收敛速度是二阶的&#xff0c;不低于二阶。如果函数有重根&#xff0c;牛顿法一般不是二阶收敛的。 x k 1 x k − f ( x k ) f ′ ( x k ) x_{k1}x_k- \frac{f(x_k)}{f(x_k)} xk1​xk​−f′(xk​)f(xk​)​ matlab实现 %% 牛顿迭代例子 f (x) x…

JRTClient打开谷歌

网站默认已经启动https访问&#xff0c;这时候JRTClient发布wss需要浏览器信任证书才能访问打印。为此在JRTClient内部发布了HTTPS服务&#xff0c;有时候浏览器信任的证书会丢失或者被清理掉&#xff0c;这时候需要手工信任下&#xff0c;当然用JRTBrowser就不用信任证书&…

市场复盘总结 20240105

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 最常用的二种方法: 方法一:指标选股找强势股 select * from dbo.ResultAll where 入选类型 like %指标选股% and 入选日期=20240105;方法二:趋势选股…

深入理解内存检测工具:一文解析内存检测工具的全貌与使用技巧

C和C是非常不安全且容易出错的编程语言&#xff0c;Address Sanitizer是由Google开发的一种工具&#xff0c;用于检测内存访问错误&#xff0c;如使用后释放&#xff08;use-after-free&#xff09;和内存泄漏。它已集成到GCC版本> 4.8中&#xff0c;可用于C和C代码。Addres…

QT5.14 实现ModbusTCP客户端 Demo

本文在QT5.14平台&#xff0c;基于QModbusClientTcp类&#xff0c;实现了客户端对单个寄存器的读写&#xff0c;用ModbusSlave做服务器做测试。 1.界面 (1)更改读按钮的名称为bt_Read (2)更改写按钮的名称为bt_Write 2.修改pro文件的第三行 greaterThan(QT_MAJOR_VERSION, 4)…

Nginx 的 gzip 压缩

目录 1. 为什么要开启gzip 压缩 2.对网站配置文件进行修改 1. 为什么要开启gzip 压缩 nginx使用gzip压缩主要是为了降低网站的带宽消耗和提升访问速度。通过对页面进行压缩&#xff0c;可以减少传输的数据量&#xff0c;从而减少网络传输的时间和带宽消耗。 当浏览器接收到压…

mysql使用load data导入数据

windows环境&#xff0c;使用bat脚本sql脚本 bat脚本 echo off ::调用数据入库sql脚本 set hour%time:~0,2% if "%time:~0,1%"" " set hour0%time:~1,1% set now%Date:~0,4%%Date:~5,2%%Date:~8,2%%hour%%Time:~3,2%%Time:~6,2% echo %now% ::数据库地址…

【面试高频算法解析】算法练习5 深度优先搜索

前言 本专栏旨在通过分类学习算法&#xff0c;使您能够牢固掌握不同算法的理论要点。通过策略性地练习精选的经典题目&#xff0c;帮助您深度理解每种算法&#xff0c;避免出现刷了很多算法题&#xff0c;还是一知半解的状态 专栏导航 二分查找回溯&#xff08;Backtracking&…