【笔记】Spring Cloud Gateway 实现 gRPC 代理

Spring Cloud Gateway 在 3.1.x 版本中增加了针对 gRPC 的网关代理功能支持,本片文章描述一下如何实现相关支持.本文主要基于 Spring Cloud Gateway 的 官方文档 进行一个实践练习。有兴趣的可以翻看官方文档。

在这里插入图片描述
由于 Grpc 是基于 HTTP2 协议进行传输的,因此 Srping Cloud Gateway 在支持了 HTTP2 的基础上天然支持对 Grpc 服务器的代理,只需要在现有代理基础上针对 grpc 协议进行一些处理即可。

以下为实现步骤,这里提供了示例代码,可以按需取用.

生成服务器证书

由于 Grpc 协议使用了 Http2 作为通信协议, Http2 在正常情况下是基于 TLS 层上进行通信的,这就要求我们需要配置服务器证书,这里为了测试,使用脚本生成了一套 CA 证书:

#!/bin/bash
# 指定生成证书的目录
dir=$(dirname "$0")/../resources/x509
[ -d "$dir" ] && find "$dir" -type f -exec rm -rf {} \;
mkdir -p "$dir"
pushd "$dir" || exit
# 生成.key  私钥文件 和 csr 证书签名请求文件
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout ca.key -out ca.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Cloud/CN=ghimi.top"
# 生成自签名 .crt 证书文件
openssl x509 -req -in ca.csr -key ca.key -out ca.crt -days 3650
# 生成服务器私钥文件 和 csr 证书请求文件(私钥签名文件)
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout server.key -out server.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Blog/CN=blog.ghimi.top"
# 3. 生成 server 证书,由 ca证书颁发
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1,IP:::1"))
# 将 crt 证书转换为 pkcs12 格式,生成 server.p12 文件,密码 123456
openssl pkcs12 -export -in server.crt -inkey server.key -CAfile ca.crt \
-password pass:123456 -name server -out server.p12
# 导出服务器证书和证书私钥为 java keystore 格式 server.jks 为最终的导出结果 密码 123456
keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks \
-srcstoretype pkcs12 -deststoretype jks -srcalias server -destalias server \
-deststorepass 123456 -srcstorepass 123456
# 将 ca 证书导入到 server.jks 中
keytool -importcert -keystore server.jks -file ca.crt -alias ca -storepass 123456 -noprompt
popd || exit

构建 Grpc 服务

首先我们需要创建一个 Maven 工程,并编写 gRPC 相关的服务器代码:

添加 gRPC 所需要的相关依赖:

<!-- grpc 关键依赖-->
io.grpc:grpc-netty-shaded:jar:1.64.0:runtime -- module io.grpc.netty.shaded [auto]
io.grpc:grpc-protobuf:jar:1.64.0:compile -- module io.grpc.protobuf [auto]
io.grpc:grpc-stub:jar:1.64.0:compile -- module io.grpc.stub [auto]
io.grpc:grpc-netty:jar:1.64.0:compile -- module io.grpc.netty [auto]

用 protobuf 生成一个 Java gRPC模板:

syntax = "proto3";
option java_multiple_files = true;
option java_package = "service";message HelloReq {string name = 1;
}
message HelloResp {string greeting = 1;
}
service HelloService {rpc hello(HelloReq) returns (HelloResp);
}

然后在 pom.xml 添加 prptobuf 生成插件:

<!-- project.build.plugins -->
<plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><protocArtifact>com.google.protobuf:protoc:3.25.1:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.64.0:exe:${os.detected.classifier}</pluginArtifact><!--设置grpc生成代码到指定路径--><!--<outputDirectory>${project.build.sourceDirectory}</outputDirectory>--><!--生成代码前是否清空目录--><clearOutputDirectory>true</clearOutputDirectory></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions>
</plugin>

注意在指定 protoc 的版本时要和上面 grpc 依赖的 protobuf 版本保持一致,否则可能会出现类找不到的报错。
在这里插入图片描述
然后执行执行 Maven 命令生成 Protobuf 对应的 Java 代码:

mvn protobuf:compile protobuf:compile-custom

之后就可以基于生成的 Protobuf Java 代码编写一个 gRPC Server 了 :

public static void main(String[] args) throws IOException, InterruptedException {TlsServerCredentials.Builder tlsBuilder = TlsServerCredentials.newBuilder();File serverCert = new ClassPathResource("/x509/server.crt").getFile();File serverKey = new ClassPathResource("/x509/server.key").getFile();File caCert = new ClassPathResource("/x509/ca.crt").getFile();ServerCredentials credentials  = tlsBuilder.trustManager(caCert).keyManager(serverCert, serverKey).build();// ServerCredentials credentials = InsecureServerCredentials.create(); // 不建议使用,非常坑Server server = Grpc.newServerBuilderForPort(443, credentials).addService(new HelloImpl()).build();server.start().awaitTermination();
}static class HelloImpl extends HelloServiceGrpc.HelloServiceImplBase {@Overridepublic void hello(HelloReq request, StreamObserver<HelloResp> responseObserver) {String msg = "hello " + request.getName() + " from server";System.out.println("server received a req,reply: " + msg);HelloResp res = HelloResp.newBuilder().setGreeting(msg).build();responseObserver.onNext(res);responseObserver.onCompleted();}
}

尝试启动 GrpcServer ,检查端口是否已被监听,当前端口绑定在 443 上,这里 GrpcServer 的服务器证书一定要配置.

编写 GrpcClient 代码:

public static void main(String[] args) throws InterruptedException, IOException {// 当服务器配置了证书时需要指定 ca 证书TlsChannelCredentials.Builder tlsBuilder = TlsChannelCredentials.newBuilder();File caCert = new ClassPathResource("/x509/ca.crt").getFile();ChannelCredentials credentials = tlsBuilder.trustManager(caCert).build();// 不做服务器证书验证时使用这个// ChannelCredentials credentials = InsecureChannelCredentials.create();ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("127.0.0.1:7443", credentials);ManagedChannel channel = builder.build();HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);service.HelloReq.Builder reqBuilder = service.HelloReq.newBuilder();HelloResp resp = stub.hello(reqBuilder.setName("ghimi").build());System.out.printf("success greeting from server: %s", resp.getGreeting());channel.shutdown().awaitTermination(5, TimeUnit.MINUTES);
}

执行 GrpcClient,调用 443 端口的 GrpcServer 查看执行效果:
在这里插入图片描述
现在我们就可以开发 Spring Cloud Gateway 了,首先添加依赖,我这里添加了 spring-cloud-starter-gateway:3.1.9 版本(为了适配 Java8,已经升级 Java11 的可以提升至更高版本)。

org.springframework.cloud:spring-cloud-starter-gateway:3.1.9

编写 GrpcGateway 启动类:

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

先不做配置尝试运行一下,看下是否能够正常运行:
在这里插入图片描述
可以看到成功监听到了 8080 端口,这是 Spring Cloud Gateway 的默认监听端口,现在我们在 /src/main/resources/ 目录下添加 application.yml 配置,配置代理 grpc 端口:

server:port: 7443 #端口号http2:enabled: truessl:enabled: truekey-store: classpath:x509/server.p12key-store-password: 123456key-store-type: pkcs12key-alias: server
spring:application:name: scg_grpccloud:gateway: #网关路由配置httpclient:ssl:use-insecure-trust-manager: true
#          trustedX509Certificates:
#            - classpath:x509/ca.crtroutes:- id: user-grpc #路由 id,没有固定规则,但唯一,建议与服务名对应uri: https://[::1]:443 #匹配后提供服务的路由地址predicates:#以下是断言条件,必选全部符合条件- Path=/**               #断言,路径匹配 注意:Path 中 P 为大写- Header=Content-Type,application/grpcfilters:- AddResponseHeader=X-Request-header, header-value

添加 application.yml 后,重启 Spring Cloud Gateway 尝试用 GrpcClient 调用 7443 代理端口,可以看到请求成功:
在这里插入图片描述

报错分析

GrpcServer 和 GrpcClient 如果都配置了 InsecureServerCredentials 的情况下, GrpcClient 可以直接调用 GrpcServer 成功:

GrpcServer

TlsServerCredentials.Builder tlsBuilder = TlsServerCredentials.newBuilder();
ServerCredentials credentials = InsecureServerCredentials.create(); // 配置通过 h2c(http2 clear text) 协议访问
Server server = Grpc.newServerBuilderForPort(443, credentials).addService(new HelloImpl()).build();
server.start().awaitTermination();

GrpcClient

ChannelCredentials credentials = InsecureChannelCredentials.create(); // 通过 h2c 协议访问 GrpcServer
tlsBuilder.requireFakeFeature();
ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("127.0.0.1:443", credentials);
ManagedChannel channel = builder.build();
HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);
service.HelloReq.Builder reqBuilder = service.HelloReq.newBuilder();
HelloResp resp = stub.hello(reqBuilder.setName("ghimi").build());
System.out.printf("success greeting from server: %s\n", resp.getGreeting());
channel.shutdown().awaitTermination(5, TimeUnit.MINUTES);

此时使用 GrpcClient 调用 GrpcServer ,可以调用成功:
在这里插入图片描述

但是,如果中间添加了 Spring Cloud Gateway 的话, Grpc Server 和 Grpc Client 就都不能使用 InsecureCredentials 了, Spring Cloud Gateway 在这种场景下无论与 client 还是和 server 通信都会由于不识别的协议格式而报错:
在这里插入图片描述

如果 GrpcServer 没有配置服务器证书而是使用了 InsecureServerCredentials.create() ,GrpcClient 虽然不使用证书访问能够直接验证成功,但是如果中间通过 GrpcGateway 的话这种机制就有可能出现问题,因为 GrpcGateway 与 GrpcServer 之间的通信是基于 Http2 的,而非 Grpc 特定的协议,在 GrpcServer 没有配置服务器证书的情况下处理的包可能会导致 GrpcGateway 无法识别,但是如果 GrpcServer 配置了证书后 GrpcGateway 就能够正常验证了。

GrpcServer 在没有配置证书的情况下通过 Srping Cloud Gateway 的方式进行代理,并且 Spring Cloud Gateway 的 spring.cloud.gateway.http-client.ssl.use-insecure-trust-manager=true 的场景下 GrpcClient 访问 Spring Cloud Gateway 会报错:

GrpcClient 报错信息:

Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN: HTTP status code 500
invalid content-type: application/json
headers: Metadata(:status=500,x-request-header=header-value,content-type=application/json,content-length=146)
DATA-----------------------------
{"timestamp":"2024-06-28T13:18:03.455+00:00","path":"/HelloService/hello","status":500,"error":"Internal Server Error","requestId":"31f8f577/1-8"}at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:268)at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:249)at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:167)at service.HelloServiceGrpc$HelloServiceBlockingStub.hello(HelloServiceGrpc.java:160)at com.example.GrpcClient.main(GrpcClient.java:30)

报错信息解析,GrpcClient 报错结果来自于 Spring Cloud Gateway ,返回结果为不识别的返回内容 invalid content-type: application/json 这是由于 Spring Cloud Gateway 返回了的报错信息是 application/json 格式的,但是 GrpcClient 通过 grpc 协议通信,因此会将错误格式错误直接返回而非正确解析错误信息.

GrpcGateway 报错信息:

io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 00001204000000000000037fffffff000400100000000600002000000004080000000000000f0001at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1313) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1313) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1383) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]

这里就是的报错信息是 GrpcGateway 无法正确解析来自 GrpcServer 的 http2 的包信息而产生的报错.这是由于 GrpcGateway 与 GrpcServer 在 h2c(Http2 Clean Text) 协议上的通信格式存在差异,从而引发报错.

最后是来自 GrpcServer 的报错:

628, 2024 9:18:03 下午 io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
信息: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030302650100026103036f6977c824c322105c600bd1dbat io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:109)at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:321)at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:247)at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:453)

这里就是 GrpcServer 与 GrpcGateway 通信过程由于协议包无法识别导致通信终止,从而引发报错.

报错场景2

在 Spring Cloud Gateway 的 application.yml 中同时配置了 use-insecure-trust-manager: truetrustedX509Certificates 导致的报错:

spring:cloud:gateway: #网关路由配置httpclient:ssl:use-insecure-trust-manager: truetrustedX509Certificates:- classpath:x509/ca.crt

use-insecure-trust-manager: true 表示在于 GrpcServer 通信的过程中不会验证服务器证书,这样如果证书存在什么问题的情况下就不会引发报错了,但是在同时配置了 use-insecure-trust-manager: truetrustedX509Certificates 的情况下 use-insecure-trust-manager: true 选项是不生效的,Spring Cloud Gateway 会还是尝试通过配置的 ca 证书去验证服务器证书,从而引发报错,因此不要同时配置 use-insecure-trust-manager: truetrustedX509Certificates这两个选项。

# 同时配置了 `use-insecure-trust-manager: true` 和 `trustedX509Certificates` 后服务证书校验失败报错
javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 0:0:0:0:0:0:0:1 foundat java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130) ~[na:na]Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:

客户端通常情况下只需要配置 ca 证书,用于验证服务器证书,但是验证服务器证书这一步是可以跳过的,在一些场景下服务器证书的校验比较严格的时候容易出问题,此时可以选择不进行服务器证书校验,在 Spring Cloud Gateway 代理访问 GrpcServer 时,可以为 Spring Cloud Gateway 配置 use-insecure-trust-manager: true 来取消对 GrpcServer 的强验证。

No subject alternative names matching IP address 0:0:0:0:0:0:0:1 found

这个问题就是在校验服务器证书时,由于服务器证书校验失败导致的报错了,通常情况下, client 会校验服务器的FQDN域名信息是否与请求的连接一致:

# 请求服务器证书
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout server.key -out server.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Blog/CN=blog.ghimi.top"

上面是在使用命令生成服务器证书时配置的信息,其中 CN=blog.ghimi.top 就是我配置的域名信息,这就要求我的 GrpcServer 的 ip 地址绑定了这个域名,然后 GrpcClient 通过这个域名访问:

ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("blog.ghimi.top:7443", credentials);

在这种情况下 GrpcClient 会拿服务器返回的证书与当前连接信息进行比较,如果一致则服务器验证成功,否则验证失败并抛出异常.

在 GrpcServer 只有 ip 地址没有域名的情况下,基于域名的验证就不生效了,此时去做证书验证就一定会报错:

# 同时配置了 `use-insecure-trust-manager: true` 和 `trustedX509Certificates` 后服务证书校验失败报错
javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 0:0:0:0:0:0:0:1 foundat java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130) ~[na:na]Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:

报错信息中提到的 subject alternative names 就是在域名失效后的另外一种验证手段,他要求ca在签发服务器证书时向服务器证书中添加一段附加信息,这个信息中可以添加证书的可信 ip 地址:

# 通过 ca 证书颁发服务器证书
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1"))

上面脚本中的 IP:127.0.0.1 就是添加的可信地址,我们可以同时添加多个服务器地址,以上面的报错为例,我们只需要在生成服务器证书的时候添加 ::1 的本地 ipv6 地址即可修复该错误:

openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1,IP:::1"))

报错场景4 使用 pkcs12 配置了多张自签名 ca 证书识别失效问题

解决方案,改为使用 Java Keystore 格式的证书即可修复.

参考资料

  • Spring Cloud Gateway and gRPC
  • spring-cloud-gateway-grpc
  • gRPC-Spring-Boot-Starter 文档
  • rx-java
  • Working with Certificates and SSL
  • 介绍一下 X.509 数字证书中的扩展项 subjectAltName

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

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

相关文章

新手教程系列 -- SQLAlchemy对同一张表联表两次

在开发过程中&#xff0c;我们经常会遇到对同一张表进行多次联表查询的需求。比如在查询航线时&#xff0c;我们希望将起飞和降落的机场名称代入结果中。为了实现这一目标&#xff0c;机场名称统一存放在 AirPort 表中。下面&#xff0c;我们将介绍如何通过 SQLAlchemy 实现这一…

财务RPA与数字化转型——财务RPA如何促进企业的数字化转型

在数字化时代&#xff0c;企业面临着推动创新、提高效率的巨大挑战。RPA财务机器人作为智慧财务不可或缺的新动能&#xff0c;不仅能够优化财务流程&#xff0c;还能够在整个企业中引领数字化变革。本文金智维将深入探讨财务RPA如何成为企业数字化转型的战略利器&#xff0c;为…

visual studio 2022配置和使用jsoncpp

下载 jsoncpp下载位置&#xff1a; GitHub - open-source-parsers/jsoncpp: A C library for interacting with JSON. 编译库 1、下载完成之后解压 2、在解压文件的makefiles文件下有个vs71&#xff0c;在vs71中有visual studio项目&#xff0c;不过这里的项目是visual stud…

-bash: /snap/bin/docker: 没有那个文件或目录

-bash: /snap/bin/docker: 没有那个文件或目录 解决办法 export PATH$PATH:/usr/bin/docker然后&#xff0c;重新加载配置文件 source ~/.bashrc

AI是如何与快充技术结合的?

针对AI技术在快充领域的运用&#xff0c;我们可以进一步深入探讨AI如何与快充技术结合&#xff0c;提升充电效率和用户体验。以下是一些具体的AI技术在快充领域的应用场景&#xff1a; 一、智能充电算法 学习充电模式&#xff1a;AI算法可以学习用户的充电习惯&#xff0c;比…

浅谈API生态建设:API安全策略的6项原则

API作为连接系统与应用的桥梁&#xff0c;在助力实现高效业务流程的同时&#xff0c;也不可避免出现资产管理困难、敏感数据泄漏风险骤增等安全问题。前段时间&#xff0c;安全公司Fastly公布了一项重磅调查报告&#xff0c;报告中显示95%的企业在过去1年中遭遇过API安全问题。…

模型预测控制:设定点跟踪(Set Point Tracking)

模型预测控制&#xff1a;设定点跟踪&#xff08;Set Point Tracking&#xff09; 模型预测控制&#xff08;Model Predictive Control, MPC&#xff09;不仅可以用于系统稳定性问题&#xff0c;还可以用于设定点跟踪问题&#xff08;Set Point Tracking&#xff09;&#xff…

计算机网络 —— 路由协议:RIP、OSPF、BGP、MPLS

路由协议 1. 定义2. IGP2.1 RIP2.2 OSPF 3. BGP4. MPLS 1. 定义 互联网中需要通过路由将数据发送至目标主机。 路由器根据**路由控制表(RoutingTable)**转发数据包&#xff0c;它根据所收到的数据包中目标主机的IP地址与路由控制表的比较得出下一个应该接收的路由器。 &…

前端通过ResizeObserver来监听dom大小动态渲染echarts

export const GlobalResizeObserver (function () {const ATTR_NAME global-resizeobserver-keyconst attrValueToCallback {}function antiShake(fn, delay, immediate false) {let timer null//不能用箭头函数return function () {//在时间内重复调用的时候需要清空之前…

常见的反爬手段和解决思路(爬虫与反爬虫)

常见的反爬手段和解决思路&#xff08;爬虫与反爬虫&#xff09; 学习目标1 服务器反爬的原因2 服务器长反什么样的爬虫&#xff08;1&#xff09;十分低级的应届毕业生&#xff08;2&#xff09;十分低级的创业小公司&#xff08;3&#xff09;不小心写错了没人去停止的失控小…

firewalld(2)安装、配置文件、规则查询

安装firewalld 我使用的操作系统是debian 12,并没有安装firewalld。 通过apt install firewalld安装firewalld firewalld 本身是一个服务(firewalld.service),可以通过 systemctl 进行启动、停止和重启,而iptables 本身并不是一个服务,而是一个用户空间工具,被用来配置底…

计算机人说学校-北京理工大学-计算机方向

1. 专长、特点、特色 北京理工大学&#xff08;北理工&#xff09;的计算机专业同样具有显著的优势和特点&#xff1a; 学术水平高&#xff1a;作为一所985高校&#xff0c;北理工在计算机科学与技术以及人工智能领域都有着较高的学术水平和教学资源。研究方向广泛&#xff1…

并发编程基础概念

相关概念 并行 并行是指同一个时刻&#xff0c;多个任务同时进行。只有在多核CPU下才会发生。 并发 并发是指单个CPU在不同任务之间来换切换工作&#xff0c;但是同一时刻只有一个任务在工作。由于CPU的切换速度很快&#xff0c;给人的感受是多个任务在一起运行。 串行 串行…

一年前 LLM AGI 碎片化思考与回顾系列⑧ · 穿越SystemⅡ未知之境

阅读提示&#xff1a; 本篇系列内容的是建立于自己过去一年在以LLM为代表的AIGC快速发展浪潮中结合学术界与产业界创新与进展的一些碎片化思考并记录最终沉淀完成&#xff0c;在内容上&#xff0c;与不久前刚刚完稿的那篇10万字文章 「融合RL与LLM思想&#xff0c;探寻世界模型…

期末C语言易错知识点整理

1.在定义多维数组时&#xff0c;除了最左边的维度&#xff0c;其余的维度必须明确指定大小 2.int m[1][4]{4}; 定义的是一个 1 行 4 列的二维数组&#xff0c;初始化时提供了一个元素 4&#xff0c;其余元素默认初始化为 0&#xff0c;因此是正确的。 3.二维数组 a[3][6] 中的索…

PKG打包sqlite3项目,如何添加node_sqlite3.node依赖

项目地址&#xff1a;https://github.com/helson-lin/pkg_sqlite 在ffandown项目内&#xff0c;由于项目使用了sqlite3&#xff0c;在跨平台打包的时候&#xff0c;除了本机外其他平台打包之后运行缺少node_sqlite3.node依赖。 为了解决问题&#xff0c;百度了很久&#xff0c…

分享一个导出数据到 Excel 的解决方案

前言 许多业务场景下需要处理和分析大量的数据&#xff0c;而 Excel 是广泛使用的文件格式&#xff0c;几乎所有人都能打开和查看 Excel 文件&#xff0c;因此将数据库中的原始数据处理后&#xff0c;导出到 Excel 是一个很常见的功能&#xff0c;对于数据管理、分析、备份、展…

【代码随想录】【算法训练营】【第45天】 [198]打家劫舍 [213]打家劫舍II [337]打家劫舍III

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 45&#xff0c;周五&#xff0c;坚持不了一点~ 题目详情 [198] 打家劫舍 题目描述 198 打家劫舍 解题思路 前提&#xff1a;相邻两房屋不能连续盗窃 思路&#xff1a;动态规划, dp[i]: [0,…

【C语言】--操作符详解

&#x1f32d;个人主页: 起名字真南 &#x1f37f;个人专栏:【数据结构初阶】 【C语言】 目录 1 算术操作符1.1 和 -1.2 *1.3 /1.4 % 2 赋值操作符 &#xff1a;2.1 复合赋值符 3 单目操作符3.1 和- - 4 强制类型转换5 printf 和 scanf5.1 printf5.1.1 基本用法5.1.2 占位符5.…

Navicat连接Oracle出现Oracle library is not loaded的解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 使用Navicat链接Oracle的时候,出现如下提示:Oracle library is not loaded. 截图如下所示: 2. 原理分析 通常是由于缺少必需的 Oracle 客户端库或环境变量未正确配置所致 还有一种情况是 32位与64位的不匹配:Navica…