1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package com.kangaroohy.milo.configuration;
 
import com.kangaroohy.milo.pool.MiloConnectFactory;
import com.kangaroohy.milo.pool.MiloConnectPool;
import com.kangaroohy.milo.service.MiloService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
 
import javax.annotation.PreDestroy;
 
/**
 * @author mes
 * @date 2020/4/25
 * @desc milo-spring-boot-starter
 * @since 0.0.1
 */
@Configuration
@EnableConfigurationProperties(MiloProperties.class)
@ConditionalOnClass({MiloService.class, MiloConnectPool.class})
@ConditionalOnProperty(prefix = MiloProperties.PREFIX, value = "enabled", havingValue = "true", matchIfMissing = true)
@Slf4j
public class MiloAutoConfiguration {
    private final MiloProperties properties;
 
    private MiloConnectPool connectPool;
 
    public MiloAutoConfiguration(MiloProperties properties) {
        this.properties = properties;
    }
 
    @Bean(name = "miloConnectPool")
    @ConditionalOnMissingBean({MiloConnectPool.class})
    protected MiloConnectPool miloConnectPool() {
        MiloConnectFactory objectFactory = new MiloConnectFactory(this.properties);
        //设置对象池的相关参数
        GenericKeyedObjectPoolConfig<OpcUaClient> poolConfig = new GenericKeyedObjectPoolConfig<>();
 
        MiloProperties.Pool pool = properties.getPool();
        // 最大空闲数
        poolConfig.setMaxIdlePerKey(pool.getMaxIdle());
        //最小空闲,设置为2表示池内至少存放2个空闲对象(当池内有2个空闲对象时调用borrowObject去对象时会立即调用创建对象的方法保证池内有2个空闲对象)
        poolConfig.setMinIdlePerKey(pool.getMinIdle());
        //最大总数 10
        poolConfig.setMaxTotal(pool.getMaxTotal());
        // 多久执行一次对象扫描,将无用的对象销毁,默认-1不扫描
        // poolConfig.setTimeBetweenEvictionRuns(Duration.ofMinutes(1));
        // 在获取对象的时候检查有效性, 默认false
        poolConfig.setTestOnBorrow(true);
        // 在归还对象的时候检查有效性, 默认false
        poolConfig.setTestOnReturn(false);
        // 在空闲时检查有效性, 默认false
        poolConfig.setTestWhileIdle(false);
        // 最大等待时间, 默认的值为-1,表示无限等待。
//        poolConfig.setMaxWait(Duration.ofSeconds(1));
        // 是否启用后进先出, 默认true
        poolConfig.setLifo(true);
        // 连接耗尽时是否阻塞, false立即抛异常,true阻塞直到超时, 默认true
        poolConfig.setBlockWhenExhausted(true);
        // 每次逐出检查时 逐出的最大数目 默认3
        poolConfig.setNumTestsPerEvictionRun(3);
 
        //一定要关闭jmx,不然springboot启动会报已经注册了某个jmx的错误
        poolConfig.setJmxEnabled(false);
 
        //新建一个对象池,传入对象工厂和配置
        connectPool = new MiloConnectPool(objectFactory, poolConfig);
 
        initPool(pool.getInitialSize(), pool.getMaxIdle());
        return connectPool;
    }
 
    @Bean
    @ConditionalOnMissingBean(MiloService.class)
    @DependsOn("miloConnectPool")
    public MiloService miloService(MiloConnectPool miloConnectPool) {
        return new MiloService(miloConnectPool, properties);
    }
 
    /**
     * 预先加载testObject对象到对象池中
     *
     * @param initialSize 初始化连接数
     * @param maxIdle     最大空闲连接数
     */
    private void initPool(int initialSize, int maxIdle) {
        if (initialSize <= 0) {
            return;
        }
 
        properties.getConfig().forEach((key, config) -> {
            for (int i = 0; i < Math.min(initialSize, maxIdle); i++) {
                try {
                    connectPool.addObject(config);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
 
    }
 
    @PreDestroy
    public void destroy() {
        if (connectPool != null) {
            connectPool.close();
            log.info("all opcUaClients are closed");
        }
    }
}