map) {
18 | String htmlResource = HtmlUtils.getUrlHtml(IpAddress.SELECT_IP_ADDRESS_URL+url);
19 | String format = String.format("IP address of %s is ", url);
20 | return String.format("%s %s\n", Objects.requireNonNull(HtmlUtils.parseHtmlGetIpAddress(htmlResource)).replace(format,""),url);
21 | }
22 |
23 | @Override
24 | public String defaultValue() {
25 | return null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/config/GenContentTheadPoolExecutorConfig.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.config;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | * @author MaXiaoFa
9 | */
10 | public class GenContentTheadPoolExecutorConfig {
11 |
12 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
13 |
14 | private static final int corePoolSize = 0;
15 | private static final int maximumPoolSize = CPU_COUNT * 2 + 1;
16 | private static final int keepAliveTime = 10;
17 |
18 | public static ThreadPoolExecutor threadPoolExecutor(){
19 | ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
20 | executor.setMaximumPoolSize(maximumPoolSize);
21 | executor.setCorePoolSize(corePoolSize);
22 | executor.setKeepAliveTime(keepAliveTime, TimeUnit.SECONDS);
23 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
24 | return executor;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/async/worker/DependWrapper.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.async.worker;
2 |
3 |
4 | import com.maxiaofa.hosts.async.wrapper.WorkerWrapper;
5 |
6 | /**
7 | * 对依赖的wrapper的封装
8 | * @author wuweifeng wrote on 2019-12-20
9 | * @version 1.0
10 | */
11 | public class DependWrapper {
12 | private WorkerWrapper, ?> dependWrapper;
13 | /**
14 | * 是否该依赖必须完成后才能执行自己.
15 | * 因为存在一个任务,依赖于多个任务,是让这多个任务全部完成后才执行自己,还是某几个执行完毕就可以执行自己
16 | * 如
17 | * 1
18 | * ---3
19 | * 2
20 | * 或
21 | * 1---3
22 | * 2---3
23 | * 这两种就不一样,上面的就是必须12都完毕,才能3
24 | * 下面的就是1完毕就可以3
25 | */
26 | private boolean must = true;
27 |
28 | public DependWrapper(WorkerWrapper, ?> dependWrapper, boolean must) {
29 | this.dependWrapper = dependWrapper;
30 | this.must = must;
31 | }
32 |
33 | public DependWrapper() {
34 | }
35 |
36 | public WorkerWrapper, ?> getDependWrapper() {
37 | return dependWrapper;
38 | }
39 |
40 | public void setDependWrapper(WorkerWrapper, ?> dependWrapper) {
41 | this.dependWrapper = dependWrapper;
42 | }
43 |
44 | public boolean isMust() {
45 | return must;
46 | }
47 |
48 | public void setMust(boolean must) {
49 | this.must = must;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "DependWrapper{" +
55 | "dependWrapper=" + dependWrapper +
56 | ", must=" + must +
57 | '}';
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.utils;
2 |
3 | import com.maxiaofa.hosts.RunHosts;
4 |
5 | import java.io.*;
6 | import java.util.Scanner;
7 | import java.util.logging.Logger;
8 |
9 | /**
10 | * @author MaXiaoFa
11 | */
12 | public class FileUtils {
13 |
14 | private static final Logger log = Logger.getLogger(RunHosts.class.getName());
15 |
16 | public static String read(File file){
17 | StringBuilder content = new StringBuilder();
18 | try (Scanner sc = new Scanner(new FileReader(file.getName()))) {
19 | while (sc.hasNextLine()) {
20 | content.append(sc.nextLine())
21 | .append("\n");
22 | }
23 | } catch (FileNotFoundException e) {
24 | e.printStackTrace();
25 | }
26 | return content.toString();
27 | }
28 |
29 | public static void write(File file,String content){
30 | FileOutputStream fos = null;
31 | try {
32 | if(!file.exists()){
33 | boolean newFile = file.createNewFile();
34 | if(newFile)log.info("文件不存在正在创建文件...");
35 | }
36 | fos = new FileOutputStream(file);
37 | fos.write(content.getBytes());
38 | } catch (IOException e) {
39 | e.printStackTrace();
40 | }finally{
41 | try {
42 | if(null!=fos)fos.close();
43 | } catch (IOException e) {
44 | e.printStackTrace();
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/async/worker/WorkResult.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.async.worker;
2 |
3 | /**
4 | * 执行结果
5 | */
6 | public class WorkResult {
7 | /**
8 | * 执行的结果
9 | */
10 | private V result;
11 | /**
12 | * 结果状态
13 | */
14 | private ResultState resultState;
15 | private Exception ex;
16 |
17 | public WorkResult(V result, ResultState resultState) {
18 | this(result, resultState, null);
19 | }
20 |
21 | public WorkResult(V result, ResultState resultState, Exception ex) {
22 | this.result = result;
23 | this.resultState = resultState;
24 | this.ex = ex;
25 | }
26 |
27 | public static WorkResult defaultResult() {
28 | return new WorkResult<>(null, ResultState.DEFAULT);
29 | }
30 |
31 | @Override
32 | public String toString() {
33 | return "WorkResult{" +
34 | "result=" + result +
35 | ", resultState=" + resultState +
36 | ", ex=" + ex +
37 | '}';
38 | }
39 |
40 | public Exception getEx() {
41 | return ex;
42 | }
43 |
44 | public void setEx(Exception ex) {
45 | this.ex = ex;
46 | }
47 |
48 | public V getResult() {
49 | return result;
50 | }
51 |
52 | public void setResult(V result) {
53 | this.result = result;
54 | }
55 |
56 | public ResultState getResultState() {
57 | return resultState;
58 | }
59 |
60 | public void setResultState(ResultState resultState) {
61 | this.resultState = resultState;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/.github/workflows/updateHosts.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Github-Hosts
5 |
6 | on:
7 | push:
8 | schedule:
9 | - cron: '0 0 * * *'
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 |
18 | - name: Set up JDK 21
19 | uses: actions/setup-java@v3
20 | with:
21 | java-version: '21'
22 | distribution: 'temurin'
23 | cache: maven
24 |
25 | - name: Build with Maven
26 | run: mvn -B package --file pom.xml
27 |
28 | - name: Run jar Create hosts
29 | run: java -jar target/github-hosts-1.1.jar
30 |
31 | - name: Clear target
32 | run: rm -rf target/
33 |
34 | - name: Commit
35 | id: commit
36 | run: |
37 | git config --global user.email "github-actions[bot]@users.noreply.github.com"
38 | git config --global user.name "GitHub Actions [Bot]"
39 | git add .
40 | if git diff-index --quiet HEAD --; then
41 | echo "No changes to commit"
42 | else
43 | git commit -m "update hosts and readme"
44 | fi
45 |
46 | - name: Check on failures
47 | if: steps.commit.outputs.status == 'failure'
48 | run: exit 1
49 |
50 | - name: Push changes
51 | uses: ad-m/github-push-action@v0.6.0
52 | with:
53 | github_token: ${{ secrets.GITHUB_TOKEN }}
54 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/utils/HtmlUtils.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.utils;
2 |
3 | import org.jsoup.Jsoup;
4 | import org.jsoup.nodes.Document;
5 | import org.jsoup.nodes.Element;
6 |
7 | import java.io.BufferedReader;
8 | import java.io.IOException;
9 | import java.io.InputStreamReader;
10 | import java.net.URL;
11 | import java.net.URLConnection;
12 | import java.nio.charset.StandardCharsets;
13 |
14 | /**
15 | * @author MaXiaoFa
16 | */
17 | public class HtmlUtils {
18 |
19 | public static String parseHtmlGetIpAddress(String html){
20 | Document parseHtml = Jsoup.parse(html);
21 |
22 | Element ipAddress = parseHtml.getElementById("divString0");
23 | return ipAddress!=null ? ipAddress.html() : null;
24 | }
25 |
26 | public static String getUrlHtml(String url) {
27 | StringBuilder buffer=new StringBuilder();
28 | InputStreamReader isr=null;
29 | try {
30 | URL urlObj = new URL(url);
31 |
32 | URLConnection uc = urlObj.openConnection();
33 |
34 | isr =new InputStreamReader(uc.getInputStream(), StandardCharsets.UTF_8);
35 | BufferedReader reader =new BufferedReader(isr);
36 |
37 | String line;
38 | while ((line=reader.readLine())!=null) {
39 | buffer.append(line).append("\n");
40 | }
41 | } catch (Exception e) {
42 | e.printStackTrace();
43 | } finally{
44 | try{
45 | if(null!=isr)isr.close();
46 | } catch(IOException e){
47 | e.printStackTrace();
48 | }
49 | }
50 | return buffer.toString();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/async/executor/timer/SystemClock.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.async.executor.timer;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.ScheduledExecutorService;
5 | import java.util.concurrent.ThreadFactory;
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.atomic.AtomicLong;
8 |
9 | /**
10 | * 用于解决高并发下System.currentTimeMillis卡顿
11 | * @author lry
12 | */
13 | public class SystemClock {
14 |
15 | private final int period;
16 |
17 | private final AtomicLong now;
18 |
19 | private static class InstanceHolder {
20 | private static final SystemClock INSTANCE = new SystemClock(1);
21 | }
22 |
23 | private SystemClock(int period) {
24 | this.period = period;
25 | this.now = new AtomicLong(System.currentTimeMillis());
26 | scheduleClockUpdating();
27 | }
28 |
29 | private static SystemClock instance() {
30 | return InstanceHolder.INSTANCE;
31 | }
32 |
33 | private void scheduleClockUpdating() {
34 | ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
35 | Thread thread = new Thread(runnable, "System Clock");
36 | thread.setDaemon(true);
37 | return thread;
38 | });
39 | scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
40 | }
41 |
42 | private long currentTimeMillis() {
43 | return now.get();
44 | }
45 |
46 | /**
47 | * 用来替换原来的System.currentTimeMillis()
48 | */
49 | public static long now() {
50 | return instance().currentTimeMillis();
51 | }
52 | }
--------------------------------------------------------------------------------
/hosts:
--------------------------------------------------------------------------------
1 | #Github Hosts Start
2 | #Update Time: 2025-04-12
3 | #Project Address: https://github.com/maxiaof/github-hosts
4 | #Update URL: https://raw.githubusercontent.com/maxiaof/github-hosts/master/hosts
5 | 140.82.113.26 alive.github.com
6 | 140.82.113.26 live.github.com
7 | 185.199.111.154 github.githubassets.com
8 | 140.82.112.21 central.github.com
9 | 185.199.110.133 desktop.githubusercontent.com
10 | 185.199.109.133 camo.githubusercontent.com
11 | 185.199.111.133 github.map.fastly.net
12 | 146.75.121.194 github.global.ssl.fastly.net
13 | 140.82.121.4 gist.github.com
14 | 185.199.110.153 github.io
15 | 140.82.121.3 github.com
16 | 192.0.66.2 github.blog
17 | 140.82.121.5 api.github.com
18 | 185.199.109.133 raw.githubusercontent.com
19 | 185.199.110.133 user-images.githubusercontent.com
20 | 185.199.110.133 favicons.githubusercontent.com
21 | 185.199.109.133 avatars5.githubusercontent.com
22 | 185.199.111.133 avatars4.githubusercontent.com
23 | 185.199.109.133 avatars3.githubusercontent.com
24 | 185.199.108.133 avatars2.githubusercontent.com
25 | 185.199.111.133 avatars1.githubusercontent.com
26 | 185.199.111.133 avatars0.githubusercontent.com
27 | 185.199.109.133 avatars.githubusercontent.com
28 | 140.82.121.10 codeload.github.com
29 | 3.5.22.135 github-cloud.s3.amazonaws.com
30 | 52.217.122.33 github-com.s3.amazonaws.com
31 | 54.231.128.25 github-production-release-asset-2e65be.s3.amazonaws.com
32 | 3.5.28.154 github-production-user-asset-6210df.s3.amazonaws.com
33 | 3.5.2.206 github-production-repository-file-5c1aeb.s3.amazonaws.com
34 | 185.199.109.153 githubstatus.com
35 | 140.82.113.17 github.community
36 | 51.137.3.17 github.dev
37 | 140.82.113.21 collector.github.com
38 | 13.107.42.16 pipelines.actions.githubusercontent.com
39 | 185.199.109.133 media.githubusercontent.com
40 | 185.199.111.133 cloud.githubusercontent.com
41 | 185.199.109.133 objects.githubusercontent.com
42 | #Github Hosts End
43 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/constants/GithubUrl.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.constants;
2 |
3 | /**
4 | * @author MaXiaoFa
5 | */
6 | public class GithubUrl {
7 |
8 | public final static String[] GITHUB_URL = new String[]{
9 | "alive.github.com",
10 | "live.github.com",
11 | "github.githubassets.com",
12 | "central.github.com",
13 | "desktop.githubusercontent.com",
14 | "assets-cdn.github.com",
15 | "camo.githubusercontent.com",
16 | "github.map.fastly.net",
17 | "github.global.ssl.fastly.net",
18 | "gist.github.com",
19 | "github.io",
20 | "github.com",
21 | "github.blog",
22 | "api.github.com",
23 | "raw.githubusercontent.com",
24 | "user-images.githubusercontent.com",
25 | "favicons.githubusercontent.com",
26 | "avatars5.githubusercontent.com",
27 | "avatars4.githubusercontent.com",
28 | "avatars3.githubusercontent.com",
29 | "avatars2.githubusercontent.com",
30 | "avatars1.githubusercontent.com",
31 | "avatars0.githubusercontent.com",
32 | "avatars.githubusercontent.com",
33 | "codeload.github.com",
34 | "github-cloud.s3.amazonaws.com",
35 | "github-com.s3.amazonaws.com",
36 | "github-production-release-asset-2e65be.s3.amazonaws.com",
37 | "github-production-user-asset-6210df.s3.amazonaws.com",
38 | "github-production-repository-file-5c1aeb.s3.amazonaws.com",
39 | "githubstatus.com",
40 | "github.community",
41 | "github.dev",
42 | "collector.github.com",
43 | "pipelines.actions.githubusercontent.com",
44 | "media.githubusercontent.com",
45 | "cloud.githubusercontent.com",
46 | "objects.githubusercontent.com"};
47 | }
48 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.maxiaofa.github-hosts
8 | github-hosts
9 | 1.1
10 |
11 |
12 | 21
13 | 21
14 |
15 |
16 |
17 |
18 |
19 |
20 | org.jsoup
21 | jsoup
22 | 1.15.3
23 |
24 |
25 |
26 |
27 |
28 |
29 | maven-assembly-plugin
30 |
31 | false
32 |
33 | jar-with-dependencies
34 |
35 |
36 |
37 | com.maxiaofa.hosts.RunHosts
38 |
39 |
40 |
41 |
42 |
43 | make-assembly
44 | package
45 |
46 | single
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub-Hosts
2 |
3 | ## 一、介绍
4 | 通过修改Hosts解决国内Github经常抽风访问不到
5 |
6 | ---
7 |
8 | ## 二、使用方法
9 |
10 | ### 2.1 复制下面的内容
11 | ```bash
12 | #Github Hosts Start
13 | #Update Time: 2025-04-12
14 | #Project Address: https://github.com/maxiaof/github-hosts
15 | #Update URL: https://raw.githubusercontent.com/maxiaof/github-hosts/master/hosts
16 | 140.82.113.26 alive.github.com
17 | 140.82.113.26 live.github.com
18 | 185.199.111.154 github.githubassets.com
19 | 140.82.112.21 central.github.com
20 | 185.199.110.133 desktop.githubusercontent.com
21 | 185.199.109.133 camo.githubusercontent.com
22 | 185.199.111.133 github.map.fastly.net
23 | 146.75.121.194 github.global.ssl.fastly.net
24 | 140.82.121.4 gist.github.com
25 | 185.199.110.153 github.io
26 | 140.82.121.3 github.com
27 | 192.0.66.2 github.blog
28 | 140.82.121.5 api.github.com
29 | 185.199.109.133 raw.githubusercontent.com
30 | 185.199.110.133 user-images.githubusercontent.com
31 | 185.199.110.133 favicons.githubusercontent.com
32 | 185.199.109.133 avatars5.githubusercontent.com
33 | 185.199.111.133 avatars4.githubusercontent.com
34 | 185.199.109.133 avatars3.githubusercontent.com
35 | 185.199.108.133 avatars2.githubusercontent.com
36 | 185.199.111.133 avatars1.githubusercontent.com
37 | 185.199.111.133 avatars0.githubusercontent.com
38 | 185.199.109.133 avatars.githubusercontent.com
39 | 140.82.121.10 codeload.github.com
40 | 3.5.22.135 github-cloud.s3.amazonaws.com
41 | 52.217.122.33 github-com.s3.amazonaws.com
42 | 54.231.128.25 github-production-release-asset-2e65be.s3.amazonaws.com
43 | 3.5.28.154 github-production-user-asset-6210df.s3.amazonaws.com
44 | 3.5.2.206 github-production-repository-file-5c1aeb.s3.amazonaws.com
45 | 185.199.109.153 githubstatus.com
46 | 140.82.113.17 github.community
47 | 51.137.3.17 github.dev
48 | 140.82.113.21 collector.github.com
49 | 13.107.42.16 pipelines.actions.githubusercontent.com
50 | 185.199.109.133 media.githubusercontent.com
51 | 185.199.111.133 cloud.githubusercontent.com
52 | 185.199.109.133 objects.githubusercontent.com
53 | #Github Hosts End
54 |
55 | ```
56 | 最后更新时间:`2025-04-12`
57 |
58 | ## 2.2 修改 hosts 文件
59 | hosts 文件在不同系统位置不一,详情如下:
60 | - Windows 系统:`C:\Windows\System32\drivers\etc\hosts`。
61 | - Mac(苹果电脑)系统:`/etc/hosts`。
62 | - Linux 系统:`/etc/hosts`。
63 |
64 | 修改方法,把2.1的内容复制到文本末尾:
65 |
66 | 1. Windows 使用记事本。
67 | 2. Linux、Mac 使用 Root 权限:`sudo vi /etc/hosts`。
68 |
69 | #### 2.3 激活生效
70 | 大部分情况下是直接生效,如未生效可尝试下面的办法,刷新 DNS:
71 |
72 | 1. Windows:在 CMD 窗口输入:`ipconfig /flushdns`
73 | 2. Mac 命令:`sudo killall -HUP mDNSResponder`
74 | 3. Linux 命令:`sudo nscd restart`
75 |
76 | **Tips:** 如以上刷新不好使,请重启尝试
77 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/RunHosts.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts;
2 |
3 | import com.maxiaofa.hosts.async.executor.Async;
4 | import com.maxiaofa.hosts.async.wrapper.WorkerWrapper;
5 | import com.maxiaofa.hosts.config.GenContentTheadPoolExecutorConfig;
6 | import com.maxiaofa.hosts.constants.GithubUrl;
7 | import com.maxiaofa.hosts.utils.FileUtils;
8 | import com.maxiaofa.hosts.worker.GetIpAddressWorker;
9 |
10 | import java.io.File;
11 | import java.time.LocalDate;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.concurrent.ExecutionException;
15 | import java.util.logging.Logger;
16 | import java.util.regex.Matcher;
17 | import java.util.regex.Pattern;
18 |
19 |
20 | /**
21 | * @author MaXiaoFa
22 | */
23 | public class RunHosts {
24 |
25 | private static final Logger log = Logger.getLogger(RunHosts.class.getName());
26 |
27 | public static void main(String[] args) throws ExecutionException, InterruptedException {
28 | log.info("正在获取最新IP Address...");
29 |
30 | File file = new File(System.getProperty("user.dir") + "/hosts");
31 |
32 | LocalDate updateTime = LocalDate.now();
33 | StringBuilder content = new StringBuilder();
34 |
35 | content.append("#Github Hosts Start\n");
36 | content.append(String.format("#Update Time: %s\n", updateTime));
37 | content.append("#Project Address: https://github.com/maxiaof/github-hosts\n");
38 | content.append("#Update URL: https://raw.githubusercontent.com/maxiaof/github-hosts/master/hosts\n");
39 |
40 | List> workerWrapperList = new ArrayList<>();
41 |
42 | for (int i = 0; i < GithubUrl.GITHUB_URL.length; i++) {
43 | GetIpAddressWorker getIpAddressWorker = new GetIpAddressWorker();
44 | WorkerWrapper build = new WorkerWrapper.Builder()
45 | .worker(getIpAddressWorker)
46 | .param(GithubUrl.GITHUB_URL[i])
47 | .build();
48 | workerWrapperList.add(build);
49 | }
50 |
51 | Async.beginWork(10000, GenContentTheadPoolExecutorConfig.threadPoolExecutor(), workerWrapperList.toArray(new WorkerWrapper[]{}));
52 |
53 | workerWrapperList.forEach(workerWrapper -> {
54 | String result = workerWrapper.getWorkResult().getResult();
55 | if(result != null){
56 | String pattern = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
57 |
58 | Pattern r = Pattern.compile(pattern);
59 | Matcher m = r.matcher(result);
60 |
61 | if(m.find()){
62 | content.append(result);
63 | }
64 | }
65 | });
66 |
67 | Async.shutDown();
68 |
69 | content.append("#Github Hosts End\n");
70 | FileUtils.write(file,content.toString());
71 |
72 | log.info("正在更新README文件...");
73 | updateReadme(updateTime, content.toString());
74 |
75 | log.info("操作完成,正在关闭...");
76 | }
77 |
78 | private static void updateReadme(LocalDate data,String hosts){
79 | File readmeTemplate = new File(System.getProperty("user.dir") + "/README_TEMPLATE.md");
80 | String readmeTemplateContent = FileUtils.read(readmeTemplate);
81 |
82 | String updateReadmeContent = readmeTemplateContent.replace("{[update_time]}", data.toString()).replace("{[hosts]}", hosts);
83 |
84 | File readme = new File(System.getProperty("user.dir") + "/README.md");
85 | FileUtils.write(readme,updateReadmeContent);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/async/executor/Async.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.async.executor;
2 |
3 |
4 | import com.maxiaofa.hosts.async.callback.DefaultGroupCallback;
5 | import com.maxiaofa.hosts.async.callback.IGroupCallback;
6 | import com.maxiaofa.hosts.async.wrapper.WorkerWrapper;
7 |
8 | import java.util.*;
9 | import java.util.concurrent.*;
10 | import java.util.stream.Collectors;
11 |
12 | /**
13 | * 类入口,可以根据自己情况调整core线程的数量
14 | * @author wuweifeng wrote on 2019-12-18
15 | * @version 1.0
16 | */
17 | public class Async {
18 | /**
19 | * 默认不定长线程池
20 | */
21 | private static final ThreadPoolExecutor COMMON_POOL = (ThreadPoolExecutor) Executors.newCachedThreadPool();
22 | /**
23 | * 注意,这里是个static,也就是只能有一个线程池。用户自定义线程池时,也只能定义一个
24 | */
25 | private static ExecutorService executorService;
26 |
27 | /**
28 | * 出发点
29 | */
30 | public static boolean beginWork(long timeout, ExecutorService executorService, List workerWrappers) throws ExecutionException, InterruptedException {
31 | if(workerWrappers == null || workerWrappers.size() == 0) {
32 | return false;
33 | }
34 | //保存线程池变量
35 | Async.executorService = executorService;
36 | //定义一个map,存放所有的wrapper,key为wrapper的唯一id,value是该wrapper,可以从value中获取wrapper的result
37 | Map forParamUseWrappers = new ConcurrentHashMap<>();
38 | CompletableFuture[] futures = new CompletableFuture[workerWrappers.size()];
39 | for (int i = 0; i < workerWrappers.size(); i++) {
40 | WorkerWrapper wrapper = workerWrappers.get(i);
41 | futures[i] = CompletableFuture.runAsync(() -> wrapper.work(executorService, timeout, forParamUseWrappers), executorService);
42 | }
43 | try {
44 | CompletableFuture.allOf(futures).get(timeout, TimeUnit.MILLISECONDS);
45 | return true;
46 | } catch (TimeoutException e) {
47 | Set set = new HashSet<>();
48 | totalWorkers(workerWrappers, set);
49 | for (WorkerWrapper wrapper : set) {
50 | wrapper.stopNow();
51 | }
52 | return false;
53 | }
54 | }
55 |
56 | /**
57 | * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL
58 | */
59 | public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException {
60 | if(workerWrapper == null || workerWrapper.length == 0) {
61 | return false;
62 | }
63 | List workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toList());
64 | return beginWork(timeout, executorService, workerWrappers);
65 | }
66 |
67 | /**
68 | * 同步阻塞,直到所有都完成,或失败
69 | */
70 | public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException {
71 | return beginWork(timeout, COMMON_POOL, workerWrapper);
72 | }
73 |
74 | public static void beginWorkAsync(long timeout, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) {
75 | beginWorkAsync(timeout, COMMON_POOL, groupCallback, workerWrapper);
76 | }
77 |
78 | /**
79 | * 异步执行,直到所有都完成,或失败后,发起回调
80 | */
81 | public static void beginWorkAsync(long timeout, ExecutorService executorService, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) {
82 | if (groupCallback == null) {
83 | groupCallback = new DefaultGroupCallback();
84 | }
85 | IGroupCallback finalGroupCallback = groupCallback;
86 | if (executorService != null) {
87 | executorService.submit(() -> {
88 | try {
89 | boolean success = beginWork(timeout, executorService, workerWrapper);
90 | if (success) {
91 | finalGroupCallback.success(Arrays.asList(workerWrapper));
92 | } else {
93 | finalGroupCallback.failure(Arrays.asList(workerWrapper), new TimeoutException());
94 | }
95 | } catch (ExecutionException | InterruptedException e) {
96 | e.printStackTrace();
97 | finalGroupCallback.failure(Arrays.asList(workerWrapper), e);
98 | }
99 | });
100 | } else {
101 | COMMON_POOL.submit(() -> {
102 | try {
103 | boolean success = beginWork(timeout, COMMON_POOL, workerWrapper);
104 | if (success) {
105 | finalGroupCallback.success(Arrays.asList(workerWrapper));
106 | } else {
107 | finalGroupCallback.failure(Arrays.asList(workerWrapper), new TimeoutException());
108 | }
109 | } catch (ExecutionException | InterruptedException e) {
110 | e.printStackTrace();
111 | finalGroupCallback.failure(Arrays.asList(workerWrapper), e);
112 | }
113 | });
114 | }
115 |
116 | }
117 |
118 | /**
119 | * 总共多少个执行单元
120 | */
121 | @SuppressWarnings("unchecked")
122 | private static void totalWorkers(List workerWrappers, Set set) {
123 | set.addAll(workerWrappers);
124 | for (WorkerWrapper wrapper : workerWrappers) {
125 | if (wrapper.getNextWrappers() == null) {
126 | continue;
127 | }
128 | List wrappers = wrapper.getNextWrappers();
129 | totalWorkers(wrappers, set);
130 | }
131 |
132 | }
133 |
134 | /**
135 | * 关闭线程池
136 | */
137 | public static void shutDown() {
138 | shutDown(executorService);
139 | }
140 |
141 | /**
142 | * 关闭线程池
143 | */
144 | public static void shutDown(ExecutorService executorService) {
145 | if (executorService != null) {
146 | executorService.shutdown();
147 | } else {
148 | COMMON_POOL.shutdown();
149 | }
150 | }
151 |
152 | public static String getThreadCount() {
153 | return "activeCount=" + COMMON_POOL.getActiveCount() +
154 | " completedCount " + COMMON_POOL.getCompletedTaskCount() +
155 | " largestCount " + COMMON_POOL.getLargestPoolSize();
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | 木兰宽松许可证, 第2版
2 |
3 | 木兰宽松许可证, 第2版
4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2
5 |
6 |
7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
8 |
9 | 0. 定义
10 |
11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
12 |
13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
14 |
15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
16 |
17 | “法人实体”是指提交贡献的机构及其“关联实体”。
18 |
19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
20 |
21 | 1. 授予版权许可
22 |
23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
24 |
25 | 2. 授予专利许可
26 |
27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
28 |
29 | 3. 无商标许可
30 |
31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
32 |
33 | 4. 分发限制
34 |
35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
36 |
37 | 5. 免责声明与责任限制
38 |
39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
40 |
41 | 6. 语言
42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
43 |
44 | 条款结束
45 |
46 | 如何将木兰宽松许可证,第2版,应用到您的软件
47 |
48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
49 |
50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
51 |
52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
53 |
54 | 3, 请将如下声明文本放入每个源文件的头部注释中。
55 |
56 | Copyright (c) [Year] [name of copyright holder]
57 | [Software Name] is licensed under Mulan PSL v2.
58 | You can use this software according to the terms and conditions of the Mulan PSL v2.
59 | You may obtain a copy of Mulan PSL v2 at:
60 | http://license.coscl.org.cn/MulanPSL2
61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
62 | See the Mulan PSL v2 for more details.
63 |
64 |
65 | Mulan Permissive Software License,Version 2
66 |
67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2)
68 | January 2020 http://license.coscl.org.cn/MulanPSL2
69 |
70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
71 |
72 | 0. Definition
73 |
74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
75 |
76 | Contribution means the copyrightable work licensed by a particular Contributor under this License.
77 |
78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
79 |
80 | Legal Entity means the entity making a Contribution and all its Affiliates.
81 |
82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
83 |
84 | 1. Grant of Copyright License
85 |
86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
87 |
88 | 2. Grant of Patent License
89 |
90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
91 |
92 | 3. No Trademark License
93 |
94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
95 |
96 | 4. Distribution Restriction
97 |
98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
99 |
100 | 5. Disclaimer of Warranty and Limitation of Liability
101 |
102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
103 |
104 | 6. Language
105 |
106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
107 |
108 | END OF THE TERMS AND CONDITIONS
109 |
110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
111 |
112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
113 |
114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
115 |
116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
117 |
118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file.
119 |
120 |
121 | Copyright (c) [Year] [name of copyright holder]
122 | [Software Name] is licensed under Mulan PSL v2.
123 | You can use this software according to the terms and conditions of the Mulan PSL v2.
124 | You may obtain a copy of Mulan PSL v2 at:
125 | http://license.coscl.org.cn/MulanPSL2
126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
127 | See the Mulan PSL v2 for more details.
128 |
--------------------------------------------------------------------------------
/src/main/java/com/maxiaofa/hosts/async/wrapper/WorkerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.maxiaofa.hosts.async.wrapper;
2 |
3 | import com.maxiaofa.hosts.async.callback.DefaultCallback;
4 | import com.maxiaofa.hosts.async.callback.ICallback;
5 | import com.maxiaofa.hosts.async.callback.IWorker;
6 | import com.maxiaofa.hosts.async.exception.SkippedException;
7 | import com.maxiaofa.hosts.async.executor.timer.SystemClock;
8 | import com.maxiaofa.hosts.async.worker.DependWrapper;
9 | import com.maxiaofa.hosts.async.worker.ResultState;
10 | import com.maxiaofa.hosts.async.worker.WorkResult;
11 |
12 | import java.util.*;
13 | import java.util.concurrent.CompletableFuture;
14 | import java.util.concurrent.ExecutorService;
15 | import java.util.concurrent.TimeUnit;
16 | import java.util.concurrent.atomic.AtomicInteger;
17 |
18 | /**
19 | * 对每个worker及callback进行包装,一对一
20 | *
21 | * @author wuweifeng wrote on 2019-11-19.
22 | */
23 | public class WorkerWrapper {
24 | /**
25 | * 该wrapper的唯一标识
26 | */
27 | private String id;
28 | /**
29 | * worker将来要处理的param
30 | */
31 | private T param;
32 | private IWorker worker;
33 | private ICallback callback;
34 | /**
35 | * 在自己后面的wrapper,如果没有,自己就是末尾;如果有一个,就是串行;如果有多个,有几个就需要开几个线程
36 | * -------2
37 | * 1
38 | * -------3
39 | * 如1后面有2、3
40 | */
41 | private List> nextWrappers;
42 | /**
43 | * 依赖的wrappers,有2种情况,1:必须依赖的全部完成后,才能执行自己 2:依赖的任何一个、多个完成了,就可以执行自己
44 | * 通过must字段来控制是否依赖项必须完成
45 | * 1
46 | * -------3
47 | * 2
48 | * 1、2执行完毕后才能执行3
49 | */
50 | private List dependWrappers;
51 | /**
52 | * 标记该事件是否已经被处理过了,譬如已经超时返回false了,后续rpc又收到返回值了,则不再二次回调
53 | * 经试验,volatile并不能保证"同一毫秒"内,多线程对该值的修改和拉取
54 | *
55 | * 1-finish, 2-error, 3-working
56 | */
57 | private AtomicInteger state = new AtomicInteger(0);
58 | /**
59 | * 该map存放所有wrapper的id和wrapper映射
60 | */
61 | private Map forParamUseWrappers;
62 | /**
63 | * 也是个钩子变量,用来存临时的结果
64 | */
65 | private volatile WorkResult workResult = WorkResult.defaultResult();
66 | /**
67 | * 是否在执行自己前,去校验nextWrapper的执行结果
68 | * 1 4
69 | * -------3
70 | * 2
71 | * 如这种在4执行前,可能3已经执行完毕了(被2执行完后触发的),那么4就没必要执行了。
72 | * 注意,该属性仅在nextWrapper数量<=1时有效,>1时的情况是不存在的
73 | */
74 | private volatile boolean needCheckNextWrapperResult = true;
75 |
76 | private static final int FINISH = 1;
77 | private static final int ERROR = 2;
78 | private static final int WORKING = 3;
79 | private static final int INIT = 0;
80 |
81 | private WorkerWrapper(String id, IWorker worker, T param, ICallback callback) {
82 | if (worker == null) {
83 | throw new NullPointerException("async.worker is null");
84 | }
85 | this.worker = worker;
86 | this.param = param;
87 | this.id = id;
88 | //允许不设置回调
89 | if (callback == null) {
90 | callback = new DefaultCallback<>();
91 | }
92 | this.callback = callback;
93 | }
94 |
95 | /**
96 | * 开始工作
97 | * fromWrapper代表这次work是由哪个上游wrapper发起的
98 | */
99 | private void work(ExecutorService executorService, WorkerWrapper fromWrapper, long remainTime, Map forParamUseWrappers) {
100 | this.forParamUseWrappers = forParamUseWrappers;
101 | //将自己放到所有wrapper的集合里去
102 | forParamUseWrappers.put(id, this);
103 | long now = SystemClock.now();
104 | //总的已经超时了,就快速失败,进行下一个
105 | if (remainTime <= 0) {
106 | fastFail(INIT, null);
107 | beginNext(executorService, now, remainTime);
108 | return;
109 | }
110 | //如果自己已经执行过了。
111 | //可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了
112 | if (getState() == FINISH || getState() == ERROR) {
113 | beginNext(executorService, now, remainTime);
114 | return;
115 | }
116 |
117 | //如果在执行前需要校验nextWrapper的状态
118 | if (needCheckNextWrapperResult) {
119 | //如果自己的next链上有已经出结果或已经开始执行的任务了,自己就不用继续了
120 | if (!checkNextWrapperResult()) {
121 | fastFail(INIT, new SkippedException());
122 | beginNext(executorService, now, remainTime);
123 | return;
124 | }
125 | }
126 |
127 | //如果没有任何依赖,说明自己就是第一批要执行的
128 | if (dependWrappers == null || dependWrappers.size() == 0) {
129 | fire();
130 | beginNext(executorService, now, remainTime);
131 | return;
132 | }
133 |
134 | /*如果有前方依赖,存在两种情况
135 | 一种是前面只有一个wrapper。即 A -> B
136 | 一种是前面有多个wrapper。A C D -> B。需要A、C、D都完成了才能轮到B。但是无论是A执行完,还是C执行完,都会去唤醒B。
137 | 所以需要B来做判断,必须A、C、D都完成,自己才能执行 */
138 |
139 | //只有一个依赖
140 | if (dependWrappers.size() == 1) {
141 | doDependsOneJob(fromWrapper);
142 | beginNext(executorService, now, remainTime);
143 | } else {
144 | //有多个依赖时
145 | doDependsJobs(executorService, dependWrappers, fromWrapper, now, remainTime);
146 | }
147 |
148 | }
149 |
150 |
151 | public void work(ExecutorService executorService, long remainTime, Map forParamUseWrappers) {
152 | work(executorService, null, remainTime, forParamUseWrappers);
153 | }
154 |
155 | /**
156 | * 总控制台超时,停止所有任务
157 | */
158 | public void stopNow() {
159 | if (getState() == INIT || getState() == WORKING) {
160 | fastFail(getState(), null);
161 | }
162 | }
163 |
164 | /**
165 | * 判断自己下游链路上,是否存在已经出结果的或已经开始执行的
166 | * 如果没有返回true,如果有返回false
167 | */
168 | private boolean checkNextWrapperResult() {
169 | //如果自己就是最后一个,或者后面有并行的多个,就返回true
170 | if (nextWrappers == null || nextWrappers.size() != 1) {
171 | return getState() == INIT;
172 | }
173 | WorkerWrapper nextWrapper = nextWrappers.get(0);
174 | boolean state = nextWrapper.getState() == INIT;
175 | //继续校验自己的next的状态
176 | return state && nextWrapper.checkNextWrapperResult();
177 | }
178 |
179 | /**
180 | * 进行下一个任务
181 | */
182 | private void beginNext(ExecutorService executorService, long now, long remainTime) {
183 | //花费的时间
184 | long costTime = SystemClock.now() - now;
185 | if (nextWrappers == null) {
186 | return;
187 | }
188 | if (nextWrappers.size() == 1) {
189 | nextWrappers.get(0).work(executorService, WorkerWrapper.this, remainTime - costTime, forParamUseWrappers);
190 | return;
191 | }
192 | CompletableFuture[] futures = new CompletableFuture[nextWrappers.size()];
193 | for (int i = 0; i < nextWrappers.size(); i++) {
194 | int finalI = i;
195 | futures[i] = CompletableFuture.runAsync(() -> nextWrappers.get(finalI)
196 | .work(executorService, WorkerWrapper.this, remainTime - costTime, forParamUseWrappers), executorService);
197 | }
198 | try {
199 | CompletableFuture.allOf(futures).get(remainTime - costTime, TimeUnit.MILLISECONDS);
200 | } catch (Exception e) {
201 | e.printStackTrace();
202 | }
203 | }
204 |
205 | private void doDependsOneJob(WorkerWrapper dependWrapper) {
206 | if (ResultState.TIMEOUT == dependWrapper.getWorkResult().getResultState()) {
207 | workResult = defaultResult();
208 | fastFail(INIT, null);
209 | } else if (ResultState.EXCEPTION == dependWrapper.getWorkResult().getResultState()) {
210 | workResult = defaultExResult(dependWrapper.getWorkResult().getEx());
211 | fastFail(INIT, null);
212 | } else {
213 | //前面任务正常完毕了,该自己了
214 | fire();
215 | }
216 | }
217 |
218 | private synchronized void doDependsJobs(ExecutorService executorService, List dependWrappers, WorkerWrapper fromWrapper, long now, long remainTime) {
219 | //如果当前任务已经完成了,依赖的其他任务拿到锁再进来时,不需要执行下面的逻辑了。
220 | if (getState() != INIT) {
221 | return;
222 | }
223 | boolean nowDependIsMust = false;
224 | //创建必须完成的上游wrapper集合
225 | Set mustWrapper = new HashSet<>();
226 | for (DependWrapper dependWrapper : dependWrappers) {
227 | if (dependWrapper.isMust()) {
228 | mustWrapper.add(dependWrapper);
229 | }
230 | if (dependWrapper.getDependWrapper().equals(fromWrapper)) {
231 | nowDependIsMust = dependWrapper.isMust();
232 | }
233 | }
234 |
235 | //如果全部是不必须的条件,那么只要到了这里,就执行自己。
236 | if (mustWrapper.size() == 0) {
237 | if (ResultState.TIMEOUT == fromWrapper.getWorkResult().getResultState()) {
238 | fastFail(INIT, null);
239 | } else {
240 | fire();
241 | }
242 | beginNext(executorService, now, remainTime);
243 | return;
244 | }
245 |
246 | //如果存在需要必须完成的,且fromWrapper不是必须的,就什么也不干
247 | if (!nowDependIsMust) {
248 | return;
249 | }
250 |
251 | //如果fromWrapper是必须的
252 | boolean existNoFinish = false;
253 | boolean hasError = false;
254 | //先判断前面必须要执行的依赖任务的执行结果,如果有任何一个失败,那就不用走action了,直接给自己设置为失败,进行下一步就是了
255 | for (DependWrapper dependWrapper : mustWrapper) {
256 | WorkerWrapper workerWrapper = dependWrapper.getDependWrapper();
257 | WorkResult tempWorkResult = workerWrapper.getWorkResult();
258 | //为null或者isWorking,说明它依赖的某个任务还没执行到或没执行完
259 | if (workerWrapper.getState() == INIT || workerWrapper.getState() == WORKING) {
260 | existNoFinish = true;
261 | break;
262 | }
263 | if (ResultState.TIMEOUT == tempWorkResult.getResultState()) {
264 | workResult = defaultResult();
265 | hasError = true;
266 | break;
267 | }
268 | if (ResultState.EXCEPTION == tempWorkResult.getResultState()) {
269 | workResult = defaultExResult(workerWrapper.getWorkResult().getEx());
270 | hasError = true;
271 | break;
272 | }
273 |
274 | }
275 | //只要有失败的
276 | if (hasError) {
277 | fastFail(INIT, null);
278 | beginNext(executorService, now, remainTime);
279 | return;
280 | }
281 |
282 | //如果上游都没有失败,分为两种情况,一种是都finish了,一种是有的在working
283 | //都finish的话
284 | if (!existNoFinish) {
285 | //上游都finish了,进行自己
286 | fire();
287 | beginNext(executorService, now, remainTime);
288 | return;
289 | }
290 | }
291 |
292 | /**
293 | * 执行自己的job.具体的执行是在另一个线程里,但判断阻塞超时是在work线程
294 | */
295 | private void fire() {
296 | //阻塞取结果
297 | workResult = workerDoJob();
298 | }
299 |
300 | /**
301 | * 快速失败
302 | */
303 | private boolean fastFail(int expect, Exception e) {
304 | //试图将它从expect状态,改成Error
305 | if (!compareAndSetState(expect, ERROR)) {
306 | return false;
307 | }
308 |
309 | //尚未处理过结果
310 | if (checkIsNullResult()) {
311 | if (e == null) {
312 | workResult = defaultResult();
313 | } else {
314 | workResult = defaultExResult(e);
315 | }
316 | }
317 |
318 | callback.result(false, param, workResult);
319 | return true;
320 | }
321 |
322 | /**
323 | * 具体的单个worker执行任务
324 | */
325 | private WorkResult workerDoJob() {
326 | //避免重复执行
327 | if (!checkIsNullResult()) {
328 | return workResult;
329 | }
330 | try {
331 | //如果已经不是init状态了,说明正在被执行或已执行完毕。这一步很重要,可以保证任务不被重复执行
332 | if (!compareAndSetState(INIT, WORKING)) {
333 | return workResult;
334 | }
335 |
336 | callback.begin();
337 |
338 | //执行耗时操作
339 | V resultValue = worker.action(param, forParamUseWrappers);
340 |
341 | //如果状态不是在working,说明别的地方已经修改了
342 | if (!compareAndSetState(WORKING, FINISH)) {
343 | return workResult;
344 | }
345 |
346 | workResult.setResultState(ResultState.SUCCESS);
347 | workResult.setResult(resultValue);
348 | //回调成功
349 | callback.result(true, param, workResult);
350 |
351 | return workResult;
352 | } catch (Exception e) {
353 | //避免重复回调
354 | if (!checkIsNullResult()) {
355 | return workResult;
356 | }
357 | fastFail(WORKING, e);
358 | return workResult;
359 | }
360 | }
361 |
362 | public WorkResult getWorkResult() {
363 | return workResult;
364 | }
365 |
366 | public List> getNextWrappers() {
367 | return nextWrappers;
368 | }
369 |
370 | public void setParam(T param) {
371 | this.param = param;
372 | }
373 |
374 | private boolean checkIsNullResult() {
375 | return ResultState.DEFAULT == workResult.getResultState();
376 | }
377 |
378 | private void addDepend(WorkerWrapper, ?> workerWrapper, boolean must) {
379 | addDepend(new DependWrapper(workerWrapper, must));
380 | }
381 |
382 | private void addDepend(DependWrapper dependWrapper) {
383 | if (dependWrappers == null) {
384 | dependWrappers = new ArrayList<>();
385 | }
386 | //如果依赖的是重复的同一个,就不重复添加了
387 | for (DependWrapper wrapper : dependWrappers) {
388 | if (wrapper.equals(dependWrapper)) {
389 | return;
390 | }
391 | }
392 | dependWrappers.add(dependWrapper);
393 | }
394 |
395 | private void addNext(WorkerWrapper, ?> workerWrapper) {
396 | if (nextWrappers == null) {
397 | nextWrappers = new ArrayList<>();
398 | }
399 | //避免添加重复
400 | for (WorkerWrapper wrapper : nextWrappers) {
401 | if (workerWrapper.equals(wrapper)) {
402 | return;
403 | }
404 | }
405 | nextWrappers.add(workerWrapper);
406 | }
407 |
408 | private void addNextWrappers(List> wrappers) {
409 | if (wrappers == null) {
410 | return;
411 | }
412 | for (WorkerWrapper, ?> wrapper : wrappers) {
413 | addNext(wrapper);
414 | }
415 | }
416 |
417 | private void addDependWrappers(List dependWrappers) {
418 | if (dependWrappers == null) {
419 | return;
420 | }
421 | for (DependWrapper wrapper : dependWrappers) {
422 | addDepend(wrapper);
423 | }
424 | }
425 |
426 | private WorkResult defaultResult() {
427 | workResult.setResultState(ResultState.TIMEOUT);
428 | workResult.setResult(worker.defaultValue());
429 | return workResult;
430 | }
431 |
432 | private WorkResult defaultExResult(Exception ex) {
433 | workResult.setResultState(ResultState.EXCEPTION);
434 | workResult.setResult(worker.defaultValue());
435 | workResult.setEx(ex);
436 | return workResult;
437 | }
438 |
439 |
440 | private int getState() {
441 | return state.get();
442 | }
443 |
444 | public String getId() {
445 | return id;
446 | }
447 |
448 | private boolean compareAndSetState(int expect, int update) {
449 | return this.state.compareAndSet(expect, update);
450 | }
451 |
452 | private void setNeedCheckNextWrapperResult(boolean needCheckNextWrapperResult) {
453 | this.needCheckNextWrapperResult = needCheckNextWrapperResult;
454 | }
455 |
456 | @Override
457 | public boolean equals(Object o) {
458 | if (this == o) {
459 | return true;
460 | }
461 | if (o == null || getClass() != o.getClass()) {
462 | return false;
463 | }
464 | WorkerWrapper, ?> that = (WorkerWrapper, ?>) o;
465 | return needCheckNextWrapperResult == that.needCheckNextWrapperResult &&
466 | Objects.equals(param, that.param) &&
467 | Objects.equals(worker, that.worker) &&
468 | Objects.equals(callback, that.callback) &&
469 | Objects.equals(nextWrappers, that.nextWrappers) &&
470 | Objects.equals(dependWrappers, that.dependWrappers) &&
471 | Objects.equals(state, that.state) &&
472 | Objects.equals(workResult, that.workResult);
473 | }
474 |
475 | @Override
476 | public int hashCode() {
477 | return Objects.hash(param, worker, callback, nextWrappers, dependWrappers, state, workResult, needCheckNextWrapperResult);
478 | }
479 |
480 | public static class Builder {
481 | /**
482 | * 该wrapper的唯一标识
483 | */
484 | private String id = UUID.randomUUID().toString();
485 | /**
486 | * worker将来要处理的param
487 | */
488 | private W param;
489 | private IWorker worker;
490 | private ICallback callback;
491 | /**
492 | * 自己后面的所有
493 | */
494 | private List> nextWrappers;
495 | /**
496 | * 自己依赖的所有
497 | */
498 | private List dependWrappers;
499 | /**
500 | * 存储强依赖于自己的wrapper集合
501 | */
502 | private Set> selfIsMustSet;
503 |
504 | private boolean needCheckNextWrapperResult = true;
505 |
506 | public Builder worker(IWorker worker) {
507 | this.worker = worker;
508 | return this;
509 | }
510 |
511 | public Builder param(W w) {
512 | this.param = w;
513 | return this;
514 | }
515 |
516 | public Builder id(String id) {
517 | if (id != null) {
518 | this.id = id;
519 | }
520 | return this;
521 | }
522 |
523 | public Builder needCheckNextWrapperResult(boolean needCheckNextWrapperResult) {
524 | this.needCheckNextWrapperResult = needCheckNextWrapperResult;
525 | return this;
526 | }
527 |
528 | public Builder callback(ICallback callback) {
529 | this.callback = callback;
530 | return this;
531 | }
532 |
533 | public Builder depend(WorkerWrapper, ?>... wrappers) {
534 | if (wrappers == null) {
535 | return this;
536 | }
537 | for (WorkerWrapper, ?> wrapper : wrappers) {
538 | depend(wrapper);
539 | }
540 | return this;
541 | }
542 |
543 | public Builder depend(WorkerWrapper, ?> wrapper) {
544 | return depend(wrapper, true);
545 | }
546 |
547 | public Builder depend(WorkerWrapper, ?> wrapper, boolean isMust) {
548 | if (wrapper == null) {
549 | return this;
550 | }
551 | DependWrapper dependWrapper = new DependWrapper(wrapper, isMust);
552 | if (dependWrappers == null) {
553 | dependWrappers = new ArrayList<>();
554 | }
555 | dependWrappers.add(dependWrapper);
556 | return this;
557 | }
558 |
559 | public Builder next(WorkerWrapper, ?> wrapper) {
560 | return next(wrapper, true);
561 | }
562 |
563 | public Builder next(WorkerWrapper, ?> wrapper, boolean selfIsMust) {
564 | if (nextWrappers == null) {
565 | nextWrappers = new ArrayList<>();
566 | }
567 | nextWrappers.add(wrapper);
568 |
569 | //强依赖自己
570 | if (selfIsMust) {
571 | if (selfIsMustSet == null) {
572 | selfIsMustSet = new HashSet<>();
573 | }
574 | selfIsMustSet.add(wrapper);
575 | }
576 | return this;
577 | }
578 |
579 | public Builder next(WorkerWrapper, ?>... wrappers) {
580 | if (wrappers == null) {
581 | return this;
582 | }
583 | for (WorkerWrapper, ?> wrapper : wrappers) {
584 | next(wrapper);
585 | }
586 | return this;
587 | }
588 |
589 | public WorkerWrapper build() {
590 | WorkerWrapper wrapper = new WorkerWrapper<>(id, worker, param, callback);
591 | wrapper.setNeedCheckNextWrapperResult(needCheckNextWrapperResult);
592 | if (dependWrappers != null) {
593 | for (DependWrapper workerWrapper : dependWrappers) {
594 | workerWrapper.getDependWrapper().addNext(wrapper);
595 | wrapper.addDepend(workerWrapper);
596 | }
597 | }
598 | if (nextWrappers != null) {
599 | for (WorkerWrapper, ?> workerWrapper : nextWrappers) {
600 | boolean must = false;
601 | if (selfIsMustSet != null && selfIsMustSet.contains(workerWrapper)) {
602 | must = true;
603 | }
604 | workerWrapper.addDepend(wrapper, must);
605 | wrapper.addNext(workerWrapper);
606 | }
607 | }
608 |
609 | return wrapper;
610 | }
611 |
612 | }
613 | }
614 |
--------------------------------------------------------------------------------