From 8a7f6a25ac6a97d7c889bad42e1458ba73d43fa2 Mon Sep 17 00:00:00 2001
From: 00fly <00fly@noreply.gitcode.com>
Date: Sat, 26 Oct 2024 20:57:10 +0800
Subject: [PATCH] =?UTF-8?q?add=20CSDN=E5=8D=9A=E5=AE=A2=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E9=98=85=E8=AF=BB=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../.gitignore" | 7 +
.../Dockerfile" | 16 ++
.../README.md" | 19 ++
.../docker/docker-compose.yml" | 21 ++
.../docker/restart.sh" | 2 +
.../docker/stop.sh" | 2 +
.../pom.xml" | 181 ++++++++++++++++++
.../main/java/com/fly/SimpleApplication.java" | 15 ++
.../com/fly/core/config/Knife4jConfig.java" | 62 ++++++
.../com/fly/core/config/WebClientConfig.java" | 18 ++
.../com/fly/core/config/WebMvcConfig.java" | 85 ++++++++
.../fly/core/runner/WebStartedRunner.java" | 33 ++++
.../com/fly/core/utils/JsonBeanUtils.java" | 107 +++++++++++
.../fly/core/utils/SpringContextUtils.java" | 103 ++++++++++
.../java/com/fly/demo/entity/Article.java" | 13 ++
.../java/com/fly/demo/entity/BlogData.java" | 9 +
.../java/com/fly/demo/entity/Record.java" | 11 ++
.../com/fly/demo/service/DataService.java" | 43 +++++
.../java/com/fly/demo/sse/SSEServer.java" | 102 ++++++++++
.../com/fly/demo/web/DataPushController.java" | 73 +++++++
.../src/main/resources/application-dev.yml" | 6 +
.../src/main/resources/application.yml" | 22 +++
.../src/main/resources/static/error/404.html" | 31 +++
.../src/main/resources/static/index.html" | 80 ++++++++
.../src/test/java/com/fly/AlgorithmTest.java" | 106 ++++++++++
.../java/com/fly/http/WebClientTest.java" | 69 +++++++
.../src/test/resources/log4j2.xml" | 28 +++
27 files changed, 1264 insertions(+)
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/.gitignore"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/Dockerfile"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/README.md"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/docker-compose.yml"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/restart.sh"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/stop.sh"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/pom.xml"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/SimpleApplication.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/Knife4jConfig.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebClientConfig.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebMvcConfig.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/runner/WebStartedRunner.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/JsonBeanUtils.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/SpringContextUtils.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Article.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/BlogData.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Record.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/service/DataService.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/sse/SSEServer.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/web/DataPushController.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application-dev.yml"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application.yml"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/error/404.html"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/index.html"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/AlgorithmTest.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/http/WebClientTest.java"
create mode 100644 "CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/resources/log4j2.xml"
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/.gitignore" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/.gitignore"
new file mode 100644
index 0000000..9ac5367
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/.gitignore"
@@ -0,0 +1,7 @@
+target/
+logs/
+.classpath
+.project
+.settings
+.factorypath
+upload/
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/Dockerfile" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/Dockerfile"
new file mode 100644
index 0000000..7022708
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/Dockerfile"
@@ -0,0 +1,16 @@
+#基础镜像
+FROM adoptopenjdk/openjdk8-openj9:alpine-slim
+
+RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
+ echo 'Asia/Shanghai' >/etc/timezone
+
+#引入运行包
+COPY target/*.jar /app.jar
+
+#指定交互端口
+EXPOSE 8081
+
+CMD ["--server.port=8081"]
+
+#项目的启动方式
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xshareclasses", "-Xquickstart", "-jar", "/app.jar"]
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/README.md" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/README.md"
new file mode 100644
index 0000000..29df1a1
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/README.md"
@@ -0,0 +1,19 @@
+# csdn-reader
+
+csdn博客文章阅读器web版本
+
+镜像已经上传到公开镜像仓库
+
+# 一、使用技术
+| 技术 | 说明 |
+|--|--|
+| SSE |服务端向客户端推送数据 |
+| json | 数据解析 |
+| WebClient | 访问http接口 |
+|cache注解 | 缓存url数据|
+|openj9| IBM高性能企业级虚拟机,大幅减少容器运行内存|
+
+
+# 二、功能展示
+
+点击链接 [http://124.71.129.204:8081](http://124.71.129.204:8081) 访问。
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/docker-compose.yml" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/docker-compose.yml"
new file mode 100644
index 0000000..7f86e74
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/docker-compose.yml"
@@ -0,0 +1,21 @@
+version: '3.7'
+services:
+ csdn-reader:
+ image: registry.cn-shanghai.aliyuncs.com/00fly/csdn-reader:1.0.0
+ container_name: csdn-reader
+ deploy:
+ resources:
+ limits:
+ cpus: '1.0'
+ memory: 200M
+ reservations:
+ cpus: '0.05'
+ memory: 200M
+ ports:
+ - 8081:8081
+ restart: on-failure
+ logging:
+ driver: json-file
+ options:
+ max-size: '5m'
+ max-file: '1'
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/restart.sh" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/restart.sh"
new file mode 100644
index 0000000..fd0d951
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/restart.sh"
@@ -0,0 +1,2 @@
+#!/bin/bash
+docker-compose down && docker-compose up -d && docker stats
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/stop.sh" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/stop.sh"
new file mode 100644
index 0000000..28bdde6
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/docker/stop.sh"
@@ -0,0 +1,2 @@
+#!/bin/bash
+docker-compose down
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/pom.xml" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/pom.xml"
new file mode 100644
index 0000000..b6bed33
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/pom.xml"
@@ -0,0 +1,181 @@
+
+
+ 4.0.0
+ com.fly
+ csdn-reader
+ 1.0.0
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.4.RELEASE
+
+
+
+
+ UTF-8
+ registry.cn-shanghai.aliyuncs.com
+ 1.8
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+ com.github.xiaoymin
+ knife4j-spring-boot-starter
+ 2.0.8
+
+
+
+
+ com.lmax
+ disruptor
+ 3.4.2
+
+
+
+ commons-io
+ commons-io
+ 2.5
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+
+
+ public
+ aliyun nexus
+ https://maven.aliyun.com/repository/public/
+
+ true
+
+
+
+
+
+ public
+ aliyun nexus
+ https://maven.aliyun.com/repository/public/
+
+ true
+
+
+ false
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+ 0.40.3
+
+
+ package
+
+ build
+ push
+
+
+
+
+
+
+
+
+
+ ${docker.hub}
+
+
+ ${docker.hub}/00fly/${project.artifactId}:${project.version}
+
+ ${project.basedir}
+
+
+
+
+
+
+
+
+ src/main/java
+
+ **/*.java
+
+
+
+ src/main/resources
+
+ **/**
+
+ false
+
+
+
+
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/SimpleApplication.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/SimpleApplication.java"
new file mode 100644
index 0000000..5a450a3
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/SimpleApplication.java"
@@ -0,0 +1,15 @@
+package com.fly;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+
+@EnableCaching
+@SpringBootApplication
+public class SimpleApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(SimpleApplication.class, args);
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/Knife4jConfig.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/Knife4jConfig.java"
new file mode 100644
index 0000000..0b01668
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/Knife4jConfig.java"
@@ -0,0 +1,62 @@
+package com.fly.core.config;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+
+import io.swagger.annotations.ApiOperation;
+import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
+
+/**
+ * Knife4jConfig
+ *
+ */
+@EnableKnife4j
+@Configuration
+@EnableSwagger2WebMvc
+@ConditionalOnWebApplication
+@Import(BeanValidatorPluginsConfiguration.class)
+public class Knife4jConfig
+{
+ /**
+ * 开发、测试环境接口文档打开
+ *
+ * @return
+ * @see [类、类#方法、类#成员]
+ */
+ @Bean
+ Docket createRestApi()
+ {
+ return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
+ .enable(true)
+ .select()
+ .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+ .paths(PathSelectors.any()) // 包下的类,生成接口文档
+ .build()
+ .securitySchemes(security());
+ }
+
+ private ApiInfo apiInfo()
+ {
+ return new ApiInfoBuilder().title("数据接口API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();
+ }
+
+ private List security()
+ {
+ return Collections.singletonList(new ApiKey("token", "token", "header"));
+ }
+}
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebClientConfig.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebClientConfig.java"
new file mode 100644
index 0000000..67e10d6
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebClientConfig.java"
@@ -0,0 +1,18 @@
+package com.fly.core.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * 配置WebClient
+ */
+@Configuration
+public class WebClientConfig
+{
+ @Bean
+ WebClient webClient()
+ {
+ return WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebMvcConfig.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebMvcConfig.java"
new file mode 100644
index 0000000..126c662
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/config/WebMvcConfig.java"
@@ -0,0 +1,85 @@
+package com.fly.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ *
+ * mvc配置
+ *
+ * @author 00fly
+ * @version [版本号, 2021年4月23日]
+ * @see [相关类/方法]
+ * @since [产品/模块版本]
+ */
+@Configuration
+@ConditionalOnWebApplication
+public class WebMvcConfig implements WebMvcConfigurer
+{
+ @Override
+ public void configureMessageConverters(final List> converters)
+ {
+ converters.add(stringHttpMessageConverter());
+ converters.add(mappingJackson2HttpMessageConverter());
+ }
+
+ @Override
+ public void configureContentNegotiation(final ContentNegotiationConfigurer configurer)
+ {
+ configurer.defaultContentType(MediaType.APPLICATION_JSON);
+ configurer.ignoreUnknownPathExtensions(false);
+ configurer.favorPathExtension(true);
+ configurer.favorParameter(false);
+ final Map mediaTypes = new ConcurrentHashMap<>(3);
+ mediaTypes.put("atom", MediaType.APPLICATION_ATOM_XML);
+ mediaTypes.put("html", MediaType.TEXT_HTML);
+ mediaTypes.put("json", MediaType.APPLICATION_JSON);
+ configurer.mediaTypes(mediaTypes);
+ }
+
+ @Bean
+ StringHttpMessageConverter stringHttpMessageConverter()
+ {
+ return new StringHttpMessageConverter();
+ }
+
+ @Bean
+ MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter()
+ {
+ final MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
+ final List list = new ArrayList<>();
+ list.add(MediaType.APPLICATION_JSON);
+ list.add(MediaType.APPLICATION_XML);
+ list.add(MediaType.TEXT_PLAIN);
+ list.add(MediaType.TEXT_HTML);
+ list.add(MediaType.TEXT_XML);
+ messageConverter.setSupportedMediaTypes(list);
+ return messageConverter;
+ }
+
+ /**
+ * 等价于mvc中
+ * 等价于mvc中
+ *
+ * @param registry
+ */
+ @Override
+ public void addViewControllers(final ViewControllerRegistry registry)
+ {
+ registry.addViewController("/").setViewName("redirect:index");
+ registry.addViewController("/index").setViewName("index.html");
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/runner/WebStartedRunner.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/runner/WebStartedRunner.java"
new file mode 100644
index 0000000..2ef0561
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/runner/WebStartedRunner.java"
@@ -0,0 +1,33 @@
+package com.fly.core.runner;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+import com.fly.core.utils.SpringContextUtils;
+
+@Component
+@Configuration
+@ConditionalOnWebApplication
+public class WebStartedRunner
+{
+ @Bean
+ @ConditionalOnWebApplication
+ CommandLineRunner init()
+ {
+ return args -> {
+ if (SystemUtils.IS_OS_WINDOWS)// 防止非windows系统报错,启动失败
+ {
+ String url = SpringContextUtils.getServerBaseURL();
+ if (StringUtils.containsNone(url, "-")) // junit port:-1
+ {
+ Runtime.getRuntime().exec("cmd /c start /min " + url);
+ }
+ }
+ };
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/JsonBeanUtils.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/JsonBeanUtils.java"
new file mode 100644
index 0000000..2a0e169
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/JsonBeanUtils.java"
@@ -0,0 +1,107 @@
+package com.fly.core.utils;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * JsonBean转换工具
+ *
+ * @author 00fly
+ *
+ */
+public class JsonBeanUtils
+{
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * bean转json字符串
+ *
+ * @param bean
+ * @return
+ * @throws IOException
+ */
+ public static String beanToJson(Object bean)
+ throws IOException
+ {
+ return beanToJson(bean, false);
+ }
+
+ /**
+ * bean转json字符串
+ *
+ * @param bean
+ * @param pretty 是否格式美化
+ * @return
+ * @throws IOException
+ */
+ public static String beanToJson(Object bean, boolean pretty)
+ throws IOException
+ {
+ String jsonText = objectMapper.writeValueAsString(bean);
+ if (pretty)
+ {
+ return objectMapper.readTree(jsonText).toPrettyString();
+ }
+ return objectMapper.readTree(jsonText).toString();
+ }
+
+ /**
+ * json字符串转bean
+ *
+ * @param jsonText
+ * @return
+ * @throws IOException
+ */
+ public static T jsonToBean(String jsonText, Class clazz)
+ throws IOException
+ {
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
+ return objectMapper.readValue(jsonText, clazz);
+ }
+
+ /**
+ * json字符串转bean
+ *
+ * @param jsonText
+ * @param clazz
+ * @param ingoreError 是否忽略无法识别字段
+ * @return
+ * @throws IOException
+ */
+ public static T jsonToBean(String jsonText, Class clazz, boolean ingoreError)
+ throws IOException
+ {
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, !ingoreError);
+ return objectMapper.readValue(jsonText, clazz);
+ }
+
+ /**
+ * json字符串转bean
+ *
+ * @param jsonText
+ * @return
+ * @throws IOException
+ */
+ public static T jsonToBean(String jsonText, JavaType javaType)
+ throws IOException
+ {
+ return objectMapper.readValue(jsonText, javaType);
+ }
+
+ /**
+ * json字符串转bean
+ *
+ * @param jsonText
+ * @return
+ * @throws IOException
+ */
+ public static T jsonToBean(String jsonText, TypeReference typeRef)
+ throws IOException
+ {
+ return objectMapper.readValue(jsonText, typeRef);
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/SpringContextUtils.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/SpringContextUtils.java"
new file mode 100644
index 0000000..6601c01
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/core/utils/SpringContextUtils.java"
@@ -0,0 +1,103 @@
+package com.fly.core.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Spring Context 工具类
+ */
+@Slf4j
+@Component
+public class SpringContextUtils implements ApplicationContextAware
+{
+ private static ApplicationContext applicationContext;
+
+ private static String SERVER_BASE_URL = null;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException
+ {
+ log.info("###### execute setApplicationContext ######");
+ SpringContextUtils.applicationContext = applicationContext;
+ }
+
+ public static ApplicationContext getApplicationContext()
+ {
+ return applicationContext;
+ }
+
+ public static T getBean(Class clazz)
+ {
+ Assert.notNull(applicationContext, "applicationContext is null");
+ return applicationContext.getBean(clazz);
+ }
+
+ /**
+ * execute @PostConstruct May be SpringContextUtils not inited, throw NullPointerException
+ *
+ * @return
+ */
+ public static String getActiveProfile()
+ {
+ Assert.notNull(applicationContext, "applicationContext is null");
+ String[] profiles = applicationContext.getEnvironment().getActiveProfiles();
+ return StringUtils.join(profiles, ",");
+ }
+
+ /**
+ * can use in @PostConstruct
+ *
+ * @param context
+ * @return
+ */
+ public static String getActiveProfile(ApplicationContext context)
+ {
+ Assert.notNull(context, "context is null");
+ String[] profiles = context.getEnvironment().getActiveProfiles();
+ return StringUtils.join(profiles, ",");
+ }
+
+ /**
+ * get web服务基准地址,一般为 http://${ip}:${port}/${contentPath}
+ *
+ * @return
+ * @throws UnknownHostException
+ * @see [类、类#方法、类#成员]
+ */
+ public static String getServerBaseURL()
+ throws UnknownHostException
+ {
+ ServletContext servletContext = getBean(ServletContext.class);
+ Assert.notNull(servletContext, "servletContext is null");
+ if (SERVER_BASE_URL == null)
+ {
+ String ip = InetAddress.getLocalHost().getHostAddress();
+ SERVER_BASE_URL = "http://" + ip + ":" + getProperty("server.port") + servletContext.getContextPath();
+ }
+ return SERVER_BASE_URL;
+ }
+
+ /**
+ * getProperty
+ *
+ * @param key eg:server.port
+ * @return
+ * @see [类、类#方法、类#成员]
+ */
+ public static String getProperty(String key)
+ {
+ return applicationContext.getEnvironment().getProperty(key, "");
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Article.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Article.java"
new file mode 100644
index 0000000..cea2116
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Article.java"
@@ -0,0 +1,13 @@
+package com.fly.demo.entity;
+
+import lombok.Data;
+
+@Data
+public class Article
+{
+ String title;
+
+ String url;
+
+ Long viewCount;
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/BlogData.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/BlogData.java"
new file mode 100644
index 0000000..4d6123f
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/BlogData.java"
@@ -0,0 +1,9 @@
+package com.fly.demo.entity;
+
+import lombok.Data;
+
+@Data
+public class BlogData
+{
+ private Record data;
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Record.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Record.java"
new file mode 100644
index 0000000..4cf98d3
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/entity/Record.java"
@@ -0,0 +1,11 @@
+package com.fly.demo.entity;
+
+import java.util.List;
+
+import lombok.Data;
+
+@Data
+public class Record
+{
+ private List list;
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/service/DataService.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/service/DataService.java"
new file mode 100644
index 0000000..aaa8fb9
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/service/DataService.java"
@@ -0,0 +1,43 @@
+package com.fly.demo.service;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fly.core.utils.JsonBeanUtils;
+import com.fly.demo.entity.Article;
+import com.fly.demo.entity.BlogData;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * DataService
+ */
+@Slf4j
+@Service
+public class DataService
+{
+ @Autowired
+ private WebClient webClient;
+
+ /**
+ * 获取url数据列表
+ *
+ * @return
+ * @throws IOException
+ */
+ @Cacheable(cacheNames = "data", key = "'articles'", sync = true)
+ public List getArticles()
+ throws IOException
+ {
+ log.info("★★★★★★★★ getData from webApi ★★★★★★★★");
+ String resp = webClient.get().uri("https://00fly.online/upload/data.json").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class).block();
+ return JsonBeanUtils.jsonToBean(resp, BlogData.class, true).getData().getList();
+ }
+}
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/sse/SSEServer.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/sse/SSEServer.java"
new file mode 100644
index 0000000..ac714b7
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/sse/SSEServer.java"
@@ -0,0 +1,102 @@
+package com.fly.demo.sse;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Server-Sent Events
+ * https://blog.csdn.net/hhl18730252820/article/details/126244274
+ */
+@Slf4j
+public class SSEServer
+{
+ private static List sseEmitters = new CopyOnWriteArrayList<>();
+
+ public static SseEmitter connect()
+ {
+ SseEmitter sseEmitter = new SseEmitter(0L); // 设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常
+
+ // 注册回调
+ sseEmitter.onCompletion(completionCallBack(sseEmitter));
+ sseEmitter.onError(errorCallBack(sseEmitter));
+ sseEmitter.onTimeout(timeOutCallBack(sseEmitter));
+ sseEmitters.add(sseEmitter);
+ log.info("###### create new sse connect, count: {}", sseEmitters.size());
+ return sseEmitter;
+ }
+
+ public static void batchSendMessage(Number message)
+ {
+ sseEmitters.forEach(it -> {
+ try
+ {
+ it.send(message, MediaType.APPLICATION_JSON);
+ }
+ catch (IOException e)
+ {
+ log.error("send message error: {}", e.getMessage());
+ remove(it);
+ }
+ });
+ }
+
+ /**
+ * 指定name,发送message
+ * @param name
+ * @param message 普通字符串或json数据
+ */
+ public static void batchSendMessage(String name, String message)
+ {
+ sseEmitters.forEach(it -> {
+ try
+ {
+ it.send(SseEmitter.event().name(name).data(message));
+ }
+ catch (IOException e)
+ {
+ log.error("send message error: {}", e.getMessage());
+ remove(it);
+ }
+ });
+ }
+
+ public static void remove(SseEmitter s)
+ {
+ if (sseEmitters.contains(s))
+ {
+ sseEmitters.remove(s);
+ log.info("###### remove SseEmitter, count: {}", sseEmitters.size());
+ }
+ }
+
+ private static Runnable completionCallBack(SseEmitter s)
+ {
+ return () -> {
+ log.info("结束连接");
+ remove(s);
+ };
+ }
+
+ private static Runnable timeOutCallBack(SseEmitter s)
+ {
+ return () -> {
+ log.info("连接超时");
+ remove(s);
+ };
+ }
+
+ private static Consumer errorCallBack(SseEmitter s)
+ {
+ return throwable -> {
+ log.error("连接异常");
+ remove(s);
+ };
+ }
+}
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/web/DataPushController.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/web/DataPushController.java"
new file mode 100644
index 0000000..0b59f11
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/java/com/fly/demo/web/DataPushController.java"
@@ -0,0 +1,73 @@
+package com.fly.demo.web;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import com.fly.core.utils.JsonBeanUtils;
+import com.fly.demo.entity.Article;
+import com.fly.demo.service.DataService;
+import com.fly.demo.sse.SSEServer;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Controller
+@Api(tags = "DataPush接口")
+public class DataPushController
+{
+ long i = 0L;
+
+ @Autowired
+ DataService dataService;
+
+ @CrossOrigin
+ @ApiOperation("SSE初始化")
+ @GetMapping("/sse/connect/{userId}")
+ public SseEmitter connect(@PathVariable String userId)
+ {
+ return SSEServer.connect();
+ }
+
+ /**
+ * 初始化触发
+ */
+ @PostConstruct
+ private void init()
+ {
+ log.info("Server-Sent Events start");
+ new ScheduledThreadPoolExecutor(2).scheduleAtFixedRate(() -> {
+ i = (i + 1) % 21;
+ SSEServer.batchSendMessage(i * 5);
+ if (i < 1)
+ {
+ try
+ {
+ // 随机选择2个,返回访问量小的
+ List articles = dataService.getArticles();
+ int length = articles.size();
+ Article article001 = articles.get(RandomUtils.nextInt(0, length));
+ Article article002 = articles.get(RandomUtils.nextInt(0, length));
+ Article article = article001.getViewCount() < article002.getViewCount() ? article001 : article002;
+ SSEServer.batchSendMessage("json", JsonBeanUtils.beanToJson(article, false));
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }, 2000, 1000, TimeUnit.MILLISECONDS);
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application-dev.yml" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application-dev.yml"
new file mode 100644
index 0000000..f2430ae
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application-dev.yml"
@@ -0,0 +1,6 @@
+#设置日志级别
+logging:
+ level:
+ org:
+ springframework:
+ web: INFO
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application.yml" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application.yml"
new file mode 100644
index 0000000..da81d84
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/application.yml"
@@ -0,0 +1,22 @@
+server:
+ port: 8080
+ servlet:
+ context-path: /
+ session:
+ timeout: 1800
+spring:
+ cache:
+ type: simple
+
+ servlet:
+ multipart:
+ max-file-size: 10MB
+ max-request-size: 100MB
+ profiles:
+ active:
+ - dev
+
+#设置日志级别
+logging:
+ level:
+ root: INFO
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/error/404.html" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/error/404.html"
new file mode 100644
index 0000000..a099b7c
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/error/404.html"
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ 404(找不到页面)
+
+
+
+
+
+
+
+
+
+
+
+
+
404!
+
很抱歉,没有找到这个页面!
+
+
返回首页
+
+
+
+
+
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/index.html" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/index.html"
new file mode 100644
index 0000000..3049d3c
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/main/resources/static/index.html"
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+CSDN-Reader
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/AlgorithmTest.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/AlgorithmTest.java"
new file mode 100644
index 0000000..6c3f425
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/AlgorithmTest.java"
@@ -0,0 +1,106 @@
+package com.fly;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.util.StopWatch;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 模拟测试访问量算法具有补偿功能
+ */
+@Slf4j
+public class AlgorithmTest
+{
+ @Test
+ public void test001()
+ {
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ int length = 50;
+ List list = new ArrayList<>(length);
+ IntStream.range(0, length).forEach(n -> list.add(new Visit(RandomStringUtils.randomAlphanumeric(10), RandomUtils.nextLong(0, 1000))));
+ AtomicInteger count1 = new AtomicInteger(0);
+
+ // 写法1
+ Collections.sort(list, Comparator.comparing(Visit::getCount));
+ list.stream().forEach(v -> log.info("{}. {}", count1.incrementAndGet(), v));
+
+ // 写法2,与追加后的数据输出比较,注意细微区别
+ // list.stream().sorted(Comparator.comparing(Visit::getCount)).forEach(v -> log.info("{}. {}", count1.incrementAndGet(), v));
+
+ long sum1 = list.stream().mapToLong(Visit::getCount).sum();
+
+ log.info("******随机选择2条,对count较小的执行追加******");
+ LongStream.range(0, 30000).forEach(n -> {
+ List randomList = new ArrayList<>(2);
+ IntStream.range(0, 2).forEach(i -> randomList.add(list.get(RandomUtils.nextInt(0, length))));
+ Visit visit = randomList.stream().sorted(Comparator.comparing(Visit::getCount)).findFirst().get();
+ visit.setCount(visit.getCount() + 1);
+ });
+ AtomicInteger count2 = new AtomicInteger(0);
+ list.stream().forEach(v -> log.info("{}. {}", count2.incrementAndGet(), v));
+ long sum2 = list.stream().mapToLong(Visit::getCount).sum();
+ stopWatch.stop();
+ log.info("SUM: {} ---> {}, COST {} ms", sum1, sum2, stopWatch.getLastTaskTimeMillis());
+ }
+
+ @Test
+ public void test002()
+ {
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ int length = 50;
+ List list = new ArrayList<>(length);
+ IntStream.range(0, length).forEach(n -> list.add(new Visit(RandomStringUtils.randomAlphanumeric(10), RandomUtils.nextLong(0, 1000))));
+ AtomicInteger count1 = new AtomicInteger(0);
+
+ // 写法1
+ Collections.sort(list, Comparator.comparing(Visit::getCount));
+ list.stream().forEach(v -> log.info("{}. {}", count1.incrementAndGet(), v));
+
+ // 写法2,与追加后的数据输出比较,注意细微区别
+ // list.stream().sorted(Comparator.comparing(Visit::getCount)).forEach(v -> log.info("{}. {}", count1.incrementAndGet(), v));
+
+ long sum1 = list.stream().mapToLong(Visit::getCount).sum();
+
+ log.info("******随机选择2条,对count较小的执行追加******");
+ LongStream.range(0, 30000).forEach(n -> {
+ Visit visit001 = list.get(RandomUtils.nextInt(0, 50));
+ Visit visit002 = list.get(RandomUtils.nextInt(0, 50));
+ Visit visit = (visit001.getCount() > visit002.getCount() ? visit002 : visit001);
+ visit.setCount(visit.getCount() + 1);
+ });
+ AtomicInteger count2 = new AtomicInteger(0);
+ list.stream().forEach(v -> log.info("{}. {}", count2.incrementAndGet(), v));
+ long sum2 = list.stream().mapToLong(Visit::getCount).sum();
+ stopWatch.stop();
+ log.info("SUM: {} ---> {}, COST {} ms", sum1, sum2, stopWatch.getLastTaskTimeMillis());
+ }
+}
+
+@Data
+@AllArgsConstructor
+class Visit
+{
+ /**
+ * 链接地址
+ */
+ private String url;
+
+ /**
+ * 访问量
+ */
+ private Long count;
+}
\ No newline at end of file
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/http/WebClientTest.java" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/http/WebClientTest.java"
new file mode 100644
index 0000000..7e03083
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/java/com/fly/http/WebClientTest.java"
@@ -0,0 +1,69 @@
+package com.fly.http;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fly.core.utils.JsonBeanUtils;
+import com.fly.demo.entity.Article;
+import com.fly.demo.entity.BlogData;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * https://blog.csdn.net/zzhongcy/article/details/105412842
+ *
+ */
+@Slf4j
+@DisplayName("WebClient测试")
+public class WebClientTest
+{
+ private static WebClient webClient;
+
+ @BeforeAll
+ public static void init()
+ {
+ // 默认底层使用Netty
+ log.info("--- init ---");
+ webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
+
+ // 内置支持Jetty反应性HttpClient实现,手工编码指定
+ // webClient = WebClient.builder().baseUrl("https://blog.csdn.net").clientConnector(new JettyClientHttpConnector()).build();
+ }
+
+ @Test
+ @DisplayName("getUrls请求")
+ public void testGetUrls()
+ throws InterruptedException, IOException
+ {
+ AtomicInteger count = new AtomicInteger(0);
+ getBlogData().getData()
+ .getList()
+ .stream()
+ .map(Article::getUrl)
+ .forEach(url -> webClient.get()
+ .uri(url)
+ .acceptCharset(StandardCharsets.UTF_8)
+ .accept(MediaType.TEXT_HTML)
+ .retrieve()
+ .bodyToMono(String.class)
+ .subscribe(r -> log.info("process complted: {}. {}", count.incrementAndGet(), url), e -> log.error("{}. ====> {}", count.incrementAndGet(), e.getMessage())));
+ log.info("异步请求已提交...");
+ TimeUnit.SECONDS.sleep(10); // 重要,等待异步调用完成
+ }
+
+ private BlogData getBlogData()
+ throws IOException
+ {
+ log.info("★★★★★★★★ getData from webApi ★★★★★★★★");
+ String resp = webClient.get().uri("https://00fly.online/upload/data.json").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class).block();
+ return JsonBeanUtils.jsonToBean(resp, BlogData.class, true);
+ }
+}
diff --git "a/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/resources/log4j2.xml" "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/resources/log4j2.xml"
new file mode 100644
index 0000000..3c510f8
--- /dev/null
+++ "b/CSDN\345\215\232\345\256\242\350\207\252\345\212\250\351\230\205\350\257\273\345\231\250/src/test/resources/log4j2.xml"
@@ -0,0 +1,28 @@
+
+
+
+ %xwEx
+ %5p
+ yyyy-MM-dd HH:mm:ss.SSS
+ %clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{%pid}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
+ %d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} %pid --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--
Gitee