Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afff036641 | ||
|
|
9944e60825 | ||
|
|
74ff0c3b44 | ||
|
|
5bb6a09ce2 | ||
|
|
3e9e7cb370 | ||
|
|
3cf6bcbe6e | ||
|
|
8f9375152e | ||
|
|
501012d9bc | ||
|
|
f2b560fa1f | ||
|
|
2b95967820 | ||
|
|
9b1fafd379 | ||
|
|
e41e346dff | ||
|
|
fff5c0121a | ||
|
|
406edaf172 | ||
|
|
b6cd8ab3a2 | ||
|
|
18653410f9 |
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon-parent</artifactId>
|
|
||||||
<version>2.2.2</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>fun.asgc</groupId>
|
|
||||||
<artifactId>job-solon-plugin</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!--quartz-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.quartz-scheduler</groupId>
|
|
||||||
<artifactId>quartz</artifactId>
|
|
||||||
<version>2.3.1</version>
|
|
||||||
</dependency>
|
|
||||||
<!--hutool-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.hutool</groupId>
|
|
||||||
<artifactId>hutool-core</artifactId>
|
|
||||||
<version>5.8.15</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public class CustomThreadFactory implements ThreadFactory {
|
|
||||||
private final ThreadGroup group;
|
|
||||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
|
||||||
private final String namePrefix;
|
|
||||||
|
|
||||||
public CustomThreadFactory(String prefix) {
|
|
||||||
SecurityManager s = System.getSecurityManager();
|
|
||||||
group = (s != null) ? s.getThreadGroup() :
|
|
||||||
Thread.currentThread().getThreadGroup();
|
|
||||||
namePrefix = prefix + "-thread-";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Thread newThread(Runnable r) {
|
|
||||||
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
|
|
||||||
if (t.isDaemon()) {
|
|
||||||
t.setDaemon(false);
|
|
||||||
}
|
|
||||||
if (t.getPriority() != Thread.NORM_PRIORITY) {
|
|
||||||
t.setPriority(Thread.NORM_PRIORITY);
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public interface IJobCallback {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行日志
|
|
||||||
* @param jobInfo
|
|
||||||
* @param param
|
|
||||||
* @param throwable
|
|
||||||
*/
|
|
||||||
void executeLog(JobInfo jobInfo, String param, Throwable throwable);
|
|
||||||
}
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public interface IJobExecutor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化
|
|
||||||
* @throws JobException
|
|
||||||
*/
|
|
||||||
void init() throws Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增job
|
|
||||||
* @param jobInfo
|
|
||||||
*/
|
|
||||||
void add(JobInfo jobInfo);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除job
|
|
||||||
* @param jobName
|
|
||||||
*/
|
|
||||||
void remove(String jobName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 触发
|
|
||||||
* @param jobName
|
|
||||||
* @param param
|
|
||||||
*/
|
|
||||||
void trigger(String jobName, String param);
|
|
||||||
}
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public interface IJobHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* job执行
|
|
||||||
* @param param
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
void execute(String param) throws Exception;
|
|
||||||
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public interface IJobSource {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有job列表
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
List<JobInfo> sourceList();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
import fun.asgc.solon.extend.job.impl.JobExecutor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.noear.solon.Solon;
|
|
||||||
import org.quartz.Job;
|
|
||||||
import org.quartz.JobExecutionContext;
|
|
||||||
import org.quartz.JobExecutionException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class JobBean implements Job {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
|
||||||
JobExecutor jobExecutor = Solon.context().getBean(JobExecutor.class);
|
|
||||||
if (null != jobExecutor) {
|
|
||||||
jobExecutor.execute(jobExecutionContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
@Accessors(chain = true)
|
|
||||||
@Data
|
|
||||||
public class JobInfo {
|
|
||||||
private String id;
|
|
||||||
private String name;
|
|
||||||
private String desc;
|
|
||||||
private String cron;
|
|
||||||
private String param;
|
|
||||||
private boolean enable;
|
|
||||||
private Map<String, Object> extension;
|
|
||||||
}
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
|
|
||||||
import fun.asgc.solon.extend.job.annotation.EnableJob;
|
|
||||||
import fun.asgc.solon.extend.job.impl.DefaultJobCallback;
|
|
||||||
import fun.asgc.solon.extend.job.impl.DefaultJobSource;
|
|
||||||
import fun.asgc.solon.extend.job.impl.JobExecutor;
|
|
||||||
import org.noear.solon.Solon;
|
|
||||||
import org.noear.solon.core.AopContext;
|
|
||||||
import org.noear.solon.core.Plugin;
|
|
||||||
import org.noear.solon.core.event.AppLoadEndEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2023/3/11
|
|
||||||
*/
|
|
||||||
public class XPluginImp implements Plugin {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(AopContext context) throws Throwable {
|
|
||||||
EnableJob enableJob = Solon.app().source().getAnnotation(EnableJob.class);
|
|
||||||
if (null == enableJob || !enableJob.value()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//应用加载完后,再启动任务
|
|
||||||
Solon.app().onEvent(AppLoadEndEvent.class, e -> {
|
|
||||||
IJobSource jobSource = context.getBean(IJobSource.class);
|
|
||||||
IJobCallback jobCallback = context.getBean(IJobCallback.class);
|
|
||||||
if (null == jobSource) {
|
|
||||||
jobSource = new DefaultJobSource();
|
|
||||||
}
|
|
||||||
if (null == jobCallback) {
|
|
||||||
jobCallback = new DefaultJobCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
JobExecutor jobExecutor = new JobExecutor();
|
|
||||||
jobExecutor.setJobSource(jobSource);
|
|
||||||
jobExecutor.setJobCallback(jobCallback);
|
|
||||||
jobExecutor.start();
|
|
||||||
|
|
||||||
context.wrapAndPut(JobExecutor.class, jobExecutor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2023/3/12
|
|
||||||
*/
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface EnableJob {
|
|
||||||
boolean value() default true;
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface JobHandler {
|
|
||||||
String name();
|
|
||||||
String desc() default "";
|
|
||||||
String cron();
|
|
||||||
String param() default "";
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job.impl;
|
|
||||||
|
|
||||||
import fun.asgc.solon.extend.job.IJobCallback;
|
|
||||||
import fun.asgc.solon.extend.job.JobInfo;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2023/3/12
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class DefaultJobCallback implements IJobCallback {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void executeLog(JobInfo jobInfo, String param, Throwable throwable) {
|
|
||||||
if (null == throwable) {
|
|
||||||
log.debug("[Solon Plugin Job] Job执行 id:{} name:{} desc:{} param:{}", jobInfo.getId(), jobInfo.getName(), jobInfo.getDesc(), param);
|
|
||||||
} else {
|
|
||||||
log.error("[Solon Plugin Job] Job执行 id:{} name:{} desc:{} param:{}", jobInfo.getId(), jobInfo.getName(), jobInfo.getDesc(), param, throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job.impl;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.IJobSource;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.JobInfo;
|
|
||||||
import org.noear.solon.Solon;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public class DefaultJobSource implements IJobSource {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<JobInfo> sourceList() {
|
|
||||||
List<IJobHandler> jobHandlerList = Solon.context().getBeansOfType(IJobHandler.class);
|
|
||||||
if (CollectionUtil.isEmpty(jobHandlerList)) {
|
|
||||||
return CollectionUtil.newArrayList();
|
|
||||||
}
|
|
||||||
List<JobInfo> jobInfoList = CollectionUtil.newArrayList();
|
|
||||||
for (IJobHandler jobHandler : jobHandlerList) {
|
|
||||||
JobHandler handler = jobHandler.getClass().getAnnotation(JobHandler.class);
|
|
||||||
if (null == handler || StrUtil.isEmpty(handler.name()) || StrUtil.isEmpty(handler.cron())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
jobInfoList.add(new JobInfo()
|
|
||||||
.setId(handler.name())
|
|
||||||
.setName(handler.name())
|
|
||||||
.setDesc(handler.desc())
|
|
||||||
.setCron(handler.cron())
|
|
||||||
.setParam(handler.param())
|
|
||||||
.setEnable(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return jobInfoList;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-175
@@ -1,175 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job.impl;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import fun.asgc.solon.extend.job.*;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import org.noear.solon.Solon;
|
|
||||||
import org.quartz.*;
|
|
||||||
import org.quartz.impl.StdSchedulerFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Job执行器
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2022/9/4
|
|
||||||
*/
|
|
||||||
public class JobExecutor implements IJobExecutor {
|
|
||||||
private IJobSource jobSource;
|
|
||||||
private ThreadPoolExecutor threadPoolExecutor;
|
|
||||||
private Map<String, JobInfo> jobInfoMap = new ConcurrentHashMap<>();
|
|
||||||
private SchedulerFactory schedulerFactory;
|
|
||||||
private Scheduler scheduler;
|
|
||||||
private Map<String, IJobHandler> jobHandlerMap = new ConcurrentHashMap<>();
|
|
||||||
private Set<String> runJobSet = CollectionUtil.newHashSet();
|
|
||||||
private IJobCallback jobCallback;
|
|
||||||
private Map<String, TriggerKey> triggerKeyMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
if (null == threadPoolExecutor) {
|
|
||||||
threadPoolExecutor = new ThreadPoolExecutor(5, 20, 10L, TimeUnit.SECONDS,
|
|
||||||
new LinkedBlockingQueue<>(), new CustomThreadFactory("SolonJob"));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IJobHandler> jobHandlerList = Solon.context().getBeansOfType(IJobHandler.class);
|
|
||||||
if (!CollectionUtil.isEmpty(jobHandlerList)) {
|
|
||||||
for (IJobHandler item : jobHandlerList) {
|
|
||||||
JobHandler jobHandler = item.getClass().getAnnotation(JobHandler.class);
|
|
||||||
if (null == jobHandler) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
jobHandlerMap.put(jobHandler.name(), item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.schedulerFactory = new StdSchedulerFactory();
|
|
||||||
this.scheduler = schedulerFactory.getScheduler();
|
|
||||||
this.init();
|
|
||||||
} catch (Exception e){
|
|
||||||
throw new RuntimeException("job初始化异常");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJobSource(IJobSource jobSource) {
|
|
||||||
this.jobSource = jobSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
|
|
||||||
this.threadPoolExecutor = threadPoolExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJobCallback(IJobCallback jobCallback) {
|
|
||||||
this.jobCallback = jobCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() throws Exception {
|
|
||||||
List<JobInfo> jobInfoList = jobSource.sourceList();
|
|
||||||
if (CollectionUtil.isEmpty(jobInfoList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JobInfo jobInfo : jobInfoList) {
|
|
||||||
add(jobInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(JobInfo jobInfo) {
|
|
||||||
if (null == jobInfo || StrUtil.isEmpty(jobInfo.getId()) || StrUtil.isEmpty(jobInfo.getName()) ||
|
|
||||||
StrUtil.isEmpty(jobInfo.getCron())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (jobInfo.getId()) {
|
|
||||||
runJobSet.add(jobInfo.getName());
|
|
||||||
jobInfoMap.put(jobInfo.getId(), jobInfo);
|
|
||||||
|
|
||||||
TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getId());
|
|
||||||
triggerKeyMap.put(jobInfo.getId(), triggerKey);
|
|
||||||
JobKey jobKey = new JobKey(jobInfo.getName());
|
|
||||||
|
|
||||||
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCron()).withMisfireHandlingInstructionDoNothing();
|
|
||||||
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
|
|
||||||
JobDetail jobDetail = JobBuilder.newJob(JobBean.class).withIdentity(jobKey).build();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (jobInfo.isEnable()) {
|
|
||||||
scheduler.scheduleJob(jobDetail, cronTrigger);
|
|
||||||
scheduler.start();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(String.format("新增job[name=%s]异常", jobInfo.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(String jobId) {
|
|
||||||
JobInfo jobInfo = jobInfoMap.get(jobId);
|
|
||||||
if (null == jobInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (jobId) {
|
|
||||||
runJobSet.remove(jobInfo.getName());
|
|
||||||
unscheduleJob(jobId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void trigger(String jobId, String param) {
|
|
||||||
doExecute(jobId, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
|
||||||
if (null == context || null == context.getTrigger()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String jobId = context.getTrigger().getKey().getName();
|
|
||||||
JobInfo jobInfo = jobInfoMap.get(jobId);
|
|
||||||
if (null == jobInfo) {
|
|
||||||
unscheduleJob(jobId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doExecute(jobId, jobInfo.getParam());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unscheduleJob(String jobId) {
|
|
||||||
TriggerKey triggerKey = triggerKeyMap.get(jobId);
|
|
||||||
if (null != triggerKey) {
|
|
||||||
try {
|
|
||||||
scheduler.unscheduleJob(triggerKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doExecute(String jobId, String param) {
|
|
||||||
JobInfo jobInfo = jobInfoMap.get(jobId);
|
|
||||||
if (null == jobInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IJobHandler jobHandler =jobHandlerMap.get(jobInfo.getName());
|
|
||||||
if (null == jobHandler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
threadPoolExecutor.submit(() -> {
|
|
||||||
try {
|
|
||||||
jobHandler.execute(param);
|
|
||||||
if (null != jobCallback) {
|
|
||||||
jobCallback.executeLog(jobInfo, param, null);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
jobCallback.executeLog(jobInfo, param, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.job;
|
|
||||||
-2
@@ -1,2 +0,0 @@
|
|||||||
solon.plugin=fun.asgc.solon.extend.job.XPluginImp
|
|
||||||
solon.plugin.priority=2
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon-parent</artifactId>
|
|
||||||
<version>2.2.2</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>fun.asgc</groupId>
|
|
||||||
<artifactId>orika-solon-plugin</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.noear</groupId>
|
|
||||||
<artifactId>solon</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!--orika-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>ma.glasnost.orika</groupId>
|
|
||||||
<artifactId>orika-core</artifactId>
|
|
||||||
<version>1.5.4</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.orika;
|
|
||||||
|
|
||||||
import ma.glasnost.orika.CustomConverter;
|
|
||||||
import ma.glasnost.orika.Mapper;
|
|
||||||
import ma.glasnost.orika.MapperFacade;
|
|
||||||
import ma.glasnost.orika.MapperFactory;
|
|
||||||
import ma.glasnost.orika.impl.DefaultMapperFactory;
|
|
||||||
import ma.glasnost.orika.metadata.ClassMapBuilder;
|
|
||||||
import org.noear.solon.core.AopContext;
|
|
||||||
import org.noear.solon.core.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: aoshiguchen
|
|
||||||
* @date: 2023/3/11
|
|
||||||
*/
|
|
||||||
public class XPluginImp implements Plugin {
|
|
||||||
@Override
|
|
||||||
public void start(AopContext context) throws Throwable {
|
|
||||||
DefaultMapperFactory factory = new DefaultMapperFactory.Builder().build();
|
|
||||||
context.subBeansOfType(CustomConverter.class, bean -> {
|
|
||||||
factory.getConverterFactory().registerConverter(bean);
|
|
||||||
});
|
|
||||||
context.subBeansOfType(Mapper.class, bean -> {
|
|
||||||
factory.registerMapper(bean);
|
|
||||||
});
|
|
||||||
context.subBeansOfType(ClassMapBuilder.class, bean -> {
|
|
||||||
factory.registerClassMap((ClassMapBuilder<? extends Object, ? extends Object>) bean);
|
|
||||||
});
|
|
||||||
context.wrapAndPut(MapperFactory.class, factory);
|
|
||||||
context.wrapAndPut(MapperFacade.class, factory.getMapperFacade());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
package fun.asgc.solon.extend.orika;
|
|
||||||
-2
@@ -1,2 +0,0 @@
|
|||||||
solon.plugin=fun.asgc.solon.extend.orika.XPluginImp
|
|
||||||
solon.plugin.priority=1
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -16,6 +16,7 @@ import './icons' // icon
|
|||||||
import './errorLog'// error log
|
import './errorLog'// error log
|
||||||
import './permission' // permission control
|
import './permission' // permission control
|
||||||
import './mock' // simulation data
|
import './mock' // simulation data
|
||||||
|
import './plugins/baiduhm' // 百度统计
|
||||||
|
|
||||||
import * as filters from './filters' // global filters
|
import * as filters from './filters' // global filters
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const _hmt = _hmt || [];
|
||||||
|
(function() {
|
||||||
|
const hm = document.createElement("script");
|
||||||
|
hm.src = "https://hm.baidu.com/hm.js?173e771eef816c412396d2cb4fe2d632";
|
||||||
|
const s = document.getElementsByTagName("script")[0];
|
||||||
|
s.parentNode.insertBefore(hm, s);
|
||||||
|
})();
|
||||||
@@ -50,12 +50,11 @@ export default {
|
|||||||
this.chartDom = document.getElementById(this.chartId)
|
this.chartDom = document.getElementById(this.chartId)
|
||||||
this.myChart = echarts.init(this.chartDom)
|
this.myChart = echarts.init(this.chartDom)
|
||||||
const seriesList = []
|
const seriesList = []
|
||||||
const legendList = []
|
|
||||||
this.data.list && this.data.list.forEach((item, index) => {
|
this.data.list && this.data.list.forEach((item, index) => {
|
||||||
seriesList.push({
|
seriesList.push({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
stack: 'Total',
|
// stack: 'Total',
|
||||||
data: item.value,
|
data: item.value,
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
@@ -74,7 +73,6 @@ export default {
|
|||||||
},
|
},
|
||||||
smooth: true
|
smooth: true
|
||||||
})
|
})
|
||||||
legendList.push(item.name)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
@@ -87,13 +85,13 @@ export default {
|
|||||||
formatter: (value) => {
|
formatter: (value) => {
|
||||||
let title = this.data.text + '<br/>'
|
let title = this.data.text + '<br/>'
|
||||||
value.forEach(item => {
|
value.forEach(item => {
|
||||||
title = title + item.marker + item.seriesName + ' : ' + this.data.list[item.seriesIndex].label[item.dataIndex] + '<br/>'
|
title = title + item.marker + item.seriesName + ' : ' + getSizeDescByByteCount(item.data) + '<br/>'
|
||||||
})
|
})
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: legendList,
|
data: this.data.legendList,
|
||||||
left: 'right'
|
left: 'right'
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
|
|||||||
@@ -70,37 +70,19 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
getChartData(last7dFlow) {
|
getChartData(last7dFlow) {
|
||||||
const title = []
|
|
||||||
const downFlowDesc = []
|
|
||||||
const totalFlowDesc = []
|
|
||||||
const upFlowDesc = []
|
|
||||||
last7dFlow.dataList.forEach(item => {
|
|
||||||
title.push(item.dateStr)
|
|
||||||
totalFlowDesc.push(item.totalFlowDesc)
|
|
||||||
downFlowDesc.push(item.downFlowDesc)
|
|
||||||
upFlowDesc.push(item.upFlowDesc)
|
|
||||||
})
|
|
||||||
const list = []
|
const list = []
|
||||||
last7dFlow.seriesList.forEach(item => {
|
last7dFlow.seriesList.forEach(item => {
|
||||||
let label = []
|
|
||||||
if (item.seriesName.indexOf('上') > -1) {
|
|
||||||
label = upFlowDesc
|
|
||||||
} else if (item.seriesName.indexOf('下') > -1) {
|
|
||||||
label = downFlowDesc
|
|
||||||
} else if (item.seriesName.indexOf('总') > -1) {
|
|
||||||
label = totalFlowDesc
|
|
||||||
}
|
|
||||||
list.push({
|
list.push({
|
||||||
name: item.seriesName,
|
name: item.seriesName,
|
||||||
value: item.seriesData,
|
value: item.seriesData
|
||||||
label: label
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
text: '流量监控',
|
text: '流量监控',
|
||||||
subtext: `最近${last7dFlow.dataList.length || 0}天流量监控`,
|
subtext: `最近${last7dFlow.dataList.length || 0}天流量监控`,
|
||||||
title: title,
|
title: last7dFlow.xDate,
|
||||||
list: list
|
list: list,
|
||||||
|
legendList: last7dFlow.legendData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -73,13 +73,15 @@ public class ClientChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
|||||||
switch (event.state()) {
|
switch (event.state()) {
|
||||||
case READER_IDLE:
|
case READER_IDLE:
|
||||||
// 读超时,断开连接
|
// 读超时,断开连接
|
||||||
log.info("读超时");
|
// log.info("读超时");
|
||||||
ctx.channel().close();
|
// ctx.channel().close();
|
||||||
break;
|
break;
|
||||||
case WRITER_IDLE:
|
case WRITER_IDLE:
|
||||||
ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage());
|
ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage());
|
||||||
break;
|
break;
|
||||||
case ALL_IDLE:
|
case ALL_IDLE:
|
||||||
|
log.info("读写超时");
|
||||||
|
ctx.channel().close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ neutrino:
|
|||||||
initial-bytes-to-strip: 0
|
initial-bytes-to-strip: 0
|
||||||
length-adjustment: 0
|
length-adjustment: 0
|
||||||
read-idle-time: 40
|
read-idle-time: 40
|
||||||
write-idle-time: 8
|
write-idle-time: 5
|
||||||
all-idle-time-seconds: 0
|
all-idle-time-seconds: 45
|
||||||
client:
|
client:
|
||||||
thread-count: 50
|
thread-count: 50
|
||||||
key-store-password: ${STORE_PASS:123456}
|
key-store-password: ${STORE_PASS:123456}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-client/app.log"/>
|
<property name="LOG_FILE" value="./neutrino-proxy-client.log"/>
|
||||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
||||||
<!-- <property name="ENCODE" value="utf8" />-->
|
<!-- <property name="ENCODE" value="utf8" />-->
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
neutrino-proxy11:
|
||||||
|
container_name: 'np-server'
|
||||||
|
restart: always
|
||||||
|
image: registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.8.0
|
||||||
|
ports:
|
||||||
|
- "9000-9200:9000-9200"
|
||||||
|
- "8888:8888"
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
volumes:
|
||||||
|
- /root/neutrino-proxy/config:/root/neutrino-proxy/config
|
||||||
|
|
||||||
|
privileged: true
|
||||||
@@ -30,17 +30,13 @@
|
|||||||
<groupId>org.noear</groupId>
|
<groupId>org.noear</groupId>
|
||||||
<artifactId>activerecord-solon-plugin</artifactId>
|
<artifactId>activerecord-solon-plugin</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--orika-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>fun.asgc</groupId>
|
<groupId>org.dromara.solon-plugins</groupId>
|
||||||
<artifactId>orika-solon-plugin</artifactId>
|
|
||||||
<version>2.2.2</version>
|
|
||||||
</dependency>
|
|
||||||
<!--job-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>fun.asgc</groupId>
|
|
||||||
<artifactId>job-solon-plugin</artifactId>
|
<artifactId>job-solon-plugin</artifactId>
|
||||||
<version>2.2.2</version>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.solon-plugins</groupId>
|
||||||
|
<artifactId>orika-solon-plugin</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara.neutrino-proxy</groupId>
|
<groupId>org.dromara.neutrino-proxy</groupId>
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package org.dromara.neutrinoproxy.server;
|
package org.dromara.neutrinoproxy.server;
|
||||||
|
|
||||||
import fun.asgc.solon.extend.job.annotation.EnableJob;
|
import org.dromara.solonplugins.job.annotation.EnableJob;
|
||||||
import org.noear.solon.Solon;
|
import org.noear.solon.Solon;
|
||||||
import org.noear.solon.annotation.SolonMain;
|
import org.noear.solon.annotation.SolonMain;
|
||||||
import org.noear.solon.web.cors.CrossFilter;
|
import org.noear.solon.web.cors.CrossFilter;
|
||||||
|
|||||||
+1
-2
@@ -64,8 +64,7 @@ public class DBInitialize implements EventListener<AppLoadEndEvent> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
||||||
// TODO 该事件有50%的概率不触发
|
|
||||||
System.out.println("11");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+19
-5
@@ -18,10 +18,15 @@ public class ProxyConfig {
|
|||||||
@Inject("${neutrino.proxy.protocol}")
|
@Inject("${neutrino.proxy.protocol}")
|
||||||
private Protocol protocol;
|
private Protocol protocol;
|
||||||
/**
|
/**
|
||||||
* 服务端配置
|
* 代理服务配置
|
||||||
*/
|
*/
|
||||||
@Inject("${neutrino.proxy.server}")
|
@Inject("${neutrino.proxy.server}")
|
||||||
private Server server;
|
private Server server;
|
||||||
|
/**
|
||||||
|
* 代理隧道配置
|
||||||
|
*/
|
||||||
|
@Inject("${neutrino.proxy.tunnel}")
|
||||||
|
private Tunnel tunnel;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class Protocol {
|
public static class Protocol {
|
||||||
@@ -37,15 +42,24 @@ public class ProxyConfig {
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class Server {
|
public static class Server {
|
||||||
|
private Integer bossThreadCount;
|
||||||
|
private Integer workThreadCount;
|
||||||
|
private String domainName;
|
||||||
|
private Integer httpProxyPort;
|
||||||
|
private Integer httpsProxyPort;
|
||||||
|
private String keyStorePassword;
|
||||||
|
private String jksPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Tunnel {
|
||||||
|
private Integer bossThreadCount;
|
||||||
|
private Integer workThreadCount;
|
||||||
private Integer port;
|
private Integer port;
|
||||||
private Integer sslPort;
|
private Integer sslPort;
|
||||||
private String keyStorePassword;
|
private String keyStorePassword;
|
||||||
private String keyManagerPassword;
|
private String keyManagerPassword;
|
||||||
private String jksPath;
|
private String jksPath;
|
||||||
private Integer bossThreadCount;
|
|
||||||
private Integer workThreadCount;
|
|
||||||
private String domainName;
|
|
||||||
private Integer httpProxyPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -43,4 +43,14 @@ public class ProxyConfiguration implements LifecycleBean {
|
|||||||
return new NioEventLoopGroup(proxyConfig.getServer().getWorkThreadCount());
|
return new NioEventLoopGroup(proxyConfig.getServer().getWorkThreadCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean("tunnelBossGroup")
|
||||||
|
public NioEventLoopGroup tunnelBossGroup(@Inject ProxyConfig proxyConfig) {
|
||||||
|
return new NioEventLoopGroup(proxyConfig.getTunnel().getBossThreadCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("tunnelWorkerGroup")
|
||||||
|
public NioEventLoopGroup tunnelWorkerGroup(@Inject ProxyConfig proxyConfig) {
|
||||||
|
return new NioEventLoopGroup(proxyConfig.getTunnel().getWorkThreadCount());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -3,12 +3,12 @@ package org.dromara.neutrinoproxy.server.job;
|
|||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import org.dromara.neutrinoproxy.core.util.DateUtil;
|
import org.dromara.neutrinoproxy.core.util.DateUtil;
|
||||||
import org.dromara.neutrinoproxy.server.dal.*;
|
import org.dromara.neutrinoproxy.server.dal.*;
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
package org.dromara.neutrinoproxy.server.job;
|
package org.dromara.neutrinoproxy.server.job;
|
||||||
|
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-2
@@ -30,10 +30,10 @@ import org.dromara.neutrinoproxy.server.dal.LicenseMapper;
|
|||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportDayDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportDayDO;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportHourDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportHourDO;
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -8,10 +8,10 @@ import org.dromara.neutrinoproxy.server.dal.LicenseMapper;
|
|||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportHourDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportHourDO;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMinuteDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMinuteDO;
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -7,9 +7,9 @@ import org.dromara.neutrinoproxy.server.dal.LicenseMapper;
|
|||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMinuteDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMinuteDO;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.LicenseDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.LicenseDO;
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -6,10 +6,10 @@ import org.dromara.neutrinoproxy.server.dal.*;
|
|||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportDayDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportDayDO;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMonthDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.FlowReportMonthDO;
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
import fun.asgc.solon.extend.job.IJobHandler;
|
|
||||||
import fun.asgc.solon.extend.job.annotation.JobHandler;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.annotation.JobHandler;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+1
-142
@@ -2,28 +2,17 @@ package org.dromara.neutrinoproxy.server.proxy.core;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.*;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.dromara.neutrinoproxy.core.Constants;
|
|
||||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
|
||||||
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
|
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
|
||||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
|
||||||
import org.noear.solon.Solon;
|
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
import org.noear.solon.core.event.AppLoadEndEvent;
|
import org.noear.solon.core.event.AppLoadEndEvent;
|
||||||
import org.noear.solon.core.event.EventListener;
|
import org.noear.solon.core.event.EventListener;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author: aoshiguchen
|
* @author: aoshiguchen
|
||||||
* @date: 2023/4/2
|
* @date: 2023/4/2
|
||||||
@@ -54,7 +43,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
|||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addFirst(new BytesMetricsHandler());
|
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||||
ch.pipeline().addLast(new VisitorChannelHandler());
|
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
|
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
|
||||||
@@ -63,134 +52,4 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
|||||||
log.error("http proxy start err!", e);
|
log.error("http proxy start err!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
|
|
||||||
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = new byte[byteBuf.readableBytes()];
|
|
||||||
byteBuf.readBytes(bytes);
|
|
||||||
byteBuf.resetReaderIndex();
|
|
||||||
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
|
|
||||||
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
|
|
||||||
if (null == proxyChannel) {
|
|
||||||
// 该端口还没有代理客户端
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(ProxyUtil.getVisitorIdByChannel(channel), bytes));
|
|
||||||
|
|
||||||
// 增加流量计数
|
|
||||||
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(channel);
|
|
||||||
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
|
|
||||||
});
|
|
||||||
|
|
||||||
String visitorId = ProxyUtil.getVisitorIdByChannel(ctx.channel());
|
|
||||||
if (StringUtils.isNotBlank(visitorId)) {
|
|
||||||
proxyAttachment.execute();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String host = getHost(bytes);
|
|
||||||
if (StringUtils.isBlank(host)) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.debug("HttpProxy host: {}", host);
|
|
||||||
if (!host.endsWith(proxyConfig.getServer().getDomainName())) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int index = host.lastIndexOf("." + proxyConfig.getServer().getDomainName());
|
|
||||||
String subdomain = host.substring(0, index);
|
|
||||||
|
|
||||||
// 根据域名拿到绑定的映射对应的cmdChannel
|
|
||||||
Integer serverPort = ProxyUtil.getServerPortBySubdomain(subdomain);
|
|
||||||
if (null == serverPort) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(serverPort);
|
|
||||||
if (null == cmdChannel) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String lanInfo = ProxyUtil.getClientLanInfoByServerPort(serverPort);
|
|
||||||
if (StringUtils.isBlank(lanInfo)) {
|
|
||||||
ctx.channel().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitorId = ProxyUtil.newVisitorId();
|
|
||||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
|
|
||||||
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
|
|
||||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
|
|
||||||
// 通知代理客户端
|
|
||||||
Channel visitorChannel = ctx.channel();
|
|
||||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
|
||||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
|
||||||
|
|
||||||
if (cmdChannel == null) {
|
|
||||||
// 该端口还没有代理客户端
|
|
||||||
ctx.channel().close();
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// 用户连接断开,从控制连接中移除
|
|
||||||
String visitorId = ProxyUtil.getVisitorIdByChannel(visitorChannel);
|
|
||||||
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
|
|
||||||
|
|
||||||
// 删除代理附加对象
|
|
||||||
ProxyUtil.remoteProxyConnectAttachment(visitorId);
|
|
||||||
|
|
||||||
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
|
||||||
if (proxyChannel != null && proxyChannel.isActive()) {
|
|
||||||
proxyChannel.attr(Constants.NEXT_CHANNEL).remove();
|
|
||||||
proxyChannel.attr(Constants.LICENSE_ID).remove();
|
|
||||||
proxyChannel.attr(Constants.VISITOR_ID).remove();
|
|
||||||
|
|
||||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
|
||||||
// 通知客户端,用户连接已经断开
|
|
||||||
proxyChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.channelInactive(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
||||||
// 当出现异常就关闭连接
|
|
||||||
ctx.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getHost(byte[] buf) {
|
|
||||||
String req = new String(buf);
|
|
||||||
String[] lines = req.split("\r\n");
|
|
||||||
String firstLine = lines[0];
|
|
||||||
if (!(firstLine.endsWith("HTTP/1.1") || firstLine.endsWith("HTTP/1.0"))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (int i = 1; i < lines.length; i++) {
|
|
||||||
String line = lines[i];
|
|
||||||
if (!line.startsWith("Host: ")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 域名
|
|
||||||
String domain = line.substring(6);
|
|
||||||
return domain;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
package org.dromara.neutrinoproxy.server.proxy.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.neutrinoproxy.core.Constants;
|
||||||
|
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||||
|
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
||||||
|
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||||
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
|
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||||
|
import org.noear.solon.Solon;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: aoshiguchen
|
||||||
|
* @date: 2023/5/27
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||||
|
/**
|
||||||
|
* 域名
|
||||||
|
*/
|
||||||
|
private String domainName;
|
||||||
|
|
||||||
|
public HttpVisitorChannelHandler(String domainName) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
|
||||||
|
if (StrUtil.isBlank(domainName)) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = new byte[byteBuf.readableBytes()];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
byteBuf.resetReaderIndex();
|
||||||
|
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
|
||||||
|
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
|
||||||
|
if (null == proxyChannel) {
|
||||||
|
// 该端口还没有代理客户端
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(ProxyUtil.getVisitorIdByChannel(channel), bytes));
|
||||||
|
|
||||||
|
// 增加流量计数
|
||||||
|
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(channel);
|
||||||
|
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
String visitorId = ProxyUtil.getVisitorIdByChannel(ctx.channel());
|
||||||
|
if (StringUtils.isNotBlank(visitorId)) {
|
||||||
|
proxyAttachment.execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
|
||||||
|
ctx.channel().config().setOption(ChannelOption.AUTO_READ, false);
|
||||||
|
|
||||||
|
String host = getHost(bytes);
|
||||||
|
if (StringUtils.isBlank(host)) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.debug("HttpProxy host: {}", host);
|
||||||
|
if (!host.endsWith(domainName)) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int index = host.lastIndexOf("." + domainName);
|
||||||
|
String subdomain = host.substring(0, index);
|
||||||
|
|
||||||
|
// 根据域名拿到绑定的映射对应的cmdChannel
|
||||||
|
Integer serverPort = ProxyUtil.getServerPortBySubdomain(subdomain);
|
||||||
|
if (null == serverPort) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(serverPort);
|
||||||
|
if (null == cmdChannel) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String lanInfo = ProxyUtil.getClientLanInfoByServerPort(serverPort);
|
||||||
|
if (StringUtils.isBlank(lanInfo)) {
|
||||||
|
ctx.channel().close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitorId = ProxyUtil.newVisitorId();
|
||||||
|
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
|
||||||
|
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
|
||||||
|
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
|
||||||
|
// 通知代理客户端
|
||||||
|
Channel visitorChannel = ctx.channel();
|
||||||
|
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||||
|
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
||||||
|
|
||||||
|
if (cmdChannel == null) {
|
||||||
|
// 该端口还没有代理客户端
|
||||||
|
ctx.channel().close();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// 用户连接断开,从控制连接中移除
|
||||||
|
String visitorId = ProxyUtil.getVisitorIdByChannel(visitorChannel);
|
||||||
|
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
|
||||||
|
|
||||||
|
// 删除代理附加对象
|
||||||
|
ProxyUtil.remoteProxyConnectAttachment(visitorId);
|
||||||
|
|
||||||
|
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||||
|
if (proxyChannel != null && proxyChannel.isActive()) {
|
||||||
|
proxyChannel.attr(Constants.NEXT_CHANNEL).remove();
|
||||||
|
proxyChannel.attr(Constants.LICENSE_ID).remove();
|
||||||
|
proxyChannel.attr(Constants.VISITOR_ID).remove();
|
||||||
|
|
||||||
|
proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
|
||||||
|
// 通知客户端,用户连接已经断开
|
||||||
|
proxyChannel.writeAndFlush(ProxyMessage.buildDisconnectMessage(visitorId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.channelInactive(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
super.channelActive(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
// 当出现异常就关闭连接
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getHost(byte[] buf) {
|
||||||
|
String req = new String(buf);
|
||||||
|
String[] lines = req.split("\r\n");
|
||||||
|
String firstLine = lines[0];
|
||||||
|
if (!(firstLine.endsWith("HTTP/1.1") || firstLine.endsWith("HTTP/1.0"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
String line = lines[i];
|
||||||
|
if (!line.startsWith("Host: ")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 域名
|
||||||
|
String domain = line.substring(6);
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package org.dromara.neutrinoproxy.server.proxy.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.ssl.SslHandler;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dromara.neutrinoproxy.core.util.FileUtil;
|
||||||
|
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
import org.noear.solon.core.event.AppLoadEndEvent;
|
||||||
|
import org.noear.solon.core.event.EventListener;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: aoshiguchen
|
||||||
|
* @date: 2023/4/2
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class HttpsProxy implements EventListener<AppLoadEndEvent> {
|
||||||
|
@Inject("serverBossGroup")
|
||||||
|
private NioEventLoopGroup serverBossGroup;
|
||||||
|
@Inject("serverWorkerGroup")
|
||||||
|
private NioEventLoopGroup serverWorkerGroup;
|
||||||
|
@Inject
|
||||||
|
private ProxyConfig proxyConfig;
|
||||||
|
@Override
|
||||||
|
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
||||||
|
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpsProxyPort() ||
|
||||||
|
StringUtils.isEmpty(proxyConfig.getServer().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getKeyStorePassword())) {
|
||||||
|
log.info("no config domain name,nonsupport https proxy.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
try {
|
||||||
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
|
bootstrap.group(serverBossGroup, serverWorkerGroup)
|
||||||
|
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(createSslHandler());
|
||||||
|
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||||
|
ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getDomainName()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync();
|
||||||
|
log.info("Https代理服务启动成功!");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("https proxy start err!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChannelHandler createSslHandler() {
|
||||||
|
try {
|
||||||
|
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getServer().getJksPath());
|
||||||
|
SSLContext serverContext = SSLContext.getInstance("TLS");
|
||||||
|
final KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
|
|
||||||
|
ks.load(jksInputStream, proxyConfig.getServer().getKeyStorePassword().toCharArray());
|
||||||
|
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
|
kmf.init(ks, proxyConfig.getServer().getKeyStorePassword().toCharArray());
|
||||||
|
TrustManager[] trustManagers = null;
|
||||||
|
|
||||||
|
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
|
||||||
|
|
||||||
|
SSLEngine sslEngine = serverContext.createSSLEngine();
|
||||||
|
sslEngine.setUseClientMode(false);
|
||||||
|
sslEngine.setNeedClientAuth(false);
|
||||||
|
|
||||||
|
return new SslHandler(sslEngine);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("创建SSL处理器失败", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-20
@@ -36,20 +36,10 @@ import java.security.KeyStore;
|
|||||||
public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
||||||
@Inject
|
@Inject
|
||||||
private ProxyConfig proxyConfig;
|
private ProxyConfig proxyConfig;
|
||||||
@Inject("serverBossGroup")
|
@Inject("tunnelBossGroup")
|
||||||
private NioEventLoopGroup serverBossGroup;
|
private NioEventLoopGroup serverBossGroup;
|
||||||
@Inject("serverWorkerGroup")
|
@Inject("tunnelWorkerGroup")
|
||||||
private NioEventLoopGroup serverWorkerGroup;
|
private NioEventLoopGroup serverWorkerGroup;
|
||||||
@Inject("${neutrino.proxy.server.port}")
|
|
||||||
private Integer port;
|
|
||||||
@Inject("${neutrino.proxy.server.ssl-port}")
|
|
||||||
private Integer sslPort;
|
|
||||||
@Inject("${neutrino.proxy.server.jks-path}")
|
|
||||||
private String jksPath;
|
|
||||||
@Inject("${neutrino.proxy.server.key-store-password}")
|
|
||||||
private String keyStorePassword;
|
|
||||||
@Inject("${neutrino.proxy.server.key-manager-password}")
|
|
||||||
private String keyManagerPassword;
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
||||||
startProxyServer();
|
startProxyServer();
|
||||||
@@ -68,15 +58,15 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
bootstrap.bind(port).sync();
|
bootstrap.bind(proxyConfig.getTunnel().getPort()).sync();
|
||||||
log.info("代理服务启动,端口:{}", port);
|
log.info("代理服务启动,端口:{}", proxyConfig.getTunnel().getPort());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("代理服务异常", e);
|
log.error("代理服务异常", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startProxyServerForSSL() {
|
private void startProxyServerForSSL() {
|
||||||
if (null == sslPort) {
|
if (null == proxyConfig.getTunnel().getSslPort()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
@@ -89,8 +79,8 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
bootstrap.bind(sslPort).sync();
|
bootstrap.bind(proxyConfig.getTunnel().getSslPort()).sync();
|
||||||
log.info("代理服务启动,SSL端口: {}", sslPort);
|
log.info("代理服务启动,SSL端口: {}", proxyConfig.getTunnel().getSslPort());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("代理服务异常", e);
|
log.error("代理服务异常", e);
|
||||||
}
|
}
|
||||||
@@ -98,13 +88,13 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
|||||||
|
|
||||||
private ChannelHandler createSslHandler() {
|
private ChannelHandler createSslHandler() {
|
||||||
try {
|
try {
|
||||||
InputStream jksInputStream = FileUtil.getInputStream(jksPath);
|
InputStream jksInputStream = FileUtil.getInputStream(proxyConfig.getTunnel().getJksPath());
|
||||||
SSLContext serverContext = SSLContext.getInstance("TLS");
|
SSLContext serverContext = SSLContext.getInstance("TLS");
|
||||||
final KeyStore ks = KeyStore.getInstance("JKS");
|
final KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
|
|
||||||
ks.load(jksInputStream, keyStorePassword.toCharArray());
|
ks.load(jksInputStream, proxyConfig.getTunnel().getKeyStorePassword().toCharArray());
|
||||||
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
kmf.init(ks, keyManagerPassword.toCharArray());
|
kmf.init(ks, proxyConfig.getTunnel().getKeyManagerPassword().toCharArray());
|
||||||
TrustManager[] trustManagers = null;
|
TrustManager[] trustManagers = null;
|
||||||
|
|
||||||
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
|
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
|
||||||
|
|||||||
+9
-8
@@ -71,19 +71,21 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
Channel userChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
Channel visitorChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||||
if (userChannel != null && userChannel.isActive()) {
|
if (null != visitorChannel) {
|
||||||
Integer licenseId = ctx.channel().attr(Constants.LICENSE_ID).get();
|
Integer licenseId = ctx.channel().attr(Constants.LICENSE_ID).get();
|
||||||
String visitorId = ctx.channel().attr(Constants.VISITOR_ID).get();
|
String visitorId = ctx.channel().attr(Constants.VISITOR_ID).get();
|
||||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseId);
|
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseId);
|
||||||
|
|
||||||
if (cmdChannel != null) {
|
if (null != cmdChannel) {
|
||||||
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
|
ProxyUtil.removeVisitorChannelFromCmdChannel(cmdChannel, visitorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
if (visitorChannel.isActive()) {
|
||||||
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||||
userChannel.close();
|
visitorChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
visitorChannel.close();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
CmdChannelAttachInfo cmdChannelAttachInfo = ProxyUtil.getAttachInfo(ctx.channel());
|
CmdChannelAttachInfo cmdChannelAttachInfo = ProxyUtil.getAttachInfo(ctx.channel());
|
||||||
if (null != cmdChannelAttachInfo) {
|
if (null != cmdChannelAttachInfo) {
|
||||||
@@ -96,8 +98,8 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
|||||||
.setCode(SuccessCodeEnum.SUCCESS.getCode())
|
.setCode(SuccessCodeEnum.SUCCESS.getCode())
|
||||||
.setCreateTime(new Date())
|
.setCreateTime(new Date())
|
||||||
);
|
);
|
||||||
|
ProxyUtil.removeCmdChannel(ctx.channel());
|
||||||
}
|
}
|
||||||
ProxyUtil.removeCmdChannel(ctx.channel());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.channelInactive(ctx);
|
super.channelInactive(ctx);
|
||||||
@@ -122,7 +124,6 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
|||||||
ctx.channel().close();
|
ctx.channel().close();
|
||||||
break;
|
break;
|
||||||
case WRITER_IDLE:
|
case WRITER_IDLE:
|
||||||
log.info("写超时");
|
|
||||||
break;
|
break;
|
||||||
case ALL_IDLE:
|
case ALL_IDLE:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+4
-6
@@ -4,7 +4,6 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.neutrinoproxy.core.Constants;
|
import org.dromara.neutrinoproxy.core.Constants;
|
||||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||||
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
import org.dromara.neutrinoproxy.server.service.FlowReportService;
|
||||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||||
@@ -23,7 +22,7 @@ import java.net.InetSocketAddress;
|
|||||||
* @date: 2022/6/16
|
* @date: 2022/6/16
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
@@ -91,8 +90,7 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
|
|||||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
||||||
|
|
||||||
if (cmdChannel == null) {
|
if (null == cmdChannel) {
|
||||||
|
|
||||||
// 该端口还没有代理客户端
|
// 该端口还没有代理客户端
|
||||||
ctx.channel().close();
|
ctx.channel().close();
|
||||||
} else {
|
} else {
|
||||||
@@ -127,12 +125,12 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
|
|||||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
||||||
|
|
||||||
if (cmdChannel == null) {
|
if (null == cmdChannel) {
|
||||||
// 该端口还没有代理客户端
|
// 该端口还没有代理客户端
|
||||||
ctx.channel().close();
|
ctx.channel().close();
|
||||||
} else {
|
} else {
|
||||||
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||||
if (proxyChannel != null) {
|
if (null != proxyChannel) {
|
||||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable());
|
proxyChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+35
-13
@@ -19,19 +19,24 @@ import org.dromara.neutrinoproxy.server.controller.res.system.JobInfoUpdateEnabl
|
|||||||
import org.dromara.neutrinoproxy.server.controller.res.system.JobInfoUpdateRes;
|
import org.dromara.neutrinoproxy.server.controller.res.system.JobInfoUpdateRes;
|
||||||
import org.dromara.neutrinoproxy.server.dal.JobInfoMapper;
|
import org.dromara.neutrinoproxy.server.dal.JobInfoMapper;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.JobInfoDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.JobInfoDO;
|
||||||
|
import org.dromara.neutrinoproxy.server.job.*;
|
||||||
import org.dromara.neutrinoproxy.server.util.ParamCheckUtil;
|
import org.dromara.neutrinoproxy.server.util.ParamCheckUtil;
|
||||||
import fun.asgc.solon.extend.job.IJobSource;
|
|
||||||
import fun.asgc.solon.extend.job.JobInfo;
|
|
||||||
import fun.asgc.solon.extend.job.impl.JobExecutor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import ma.glasnost.orika.MapperFacade;
|
import ma.glasnost.orika.MapperFacade;
|
||||||
import org.apache.ibatis.solon.annotation.Db;
|
import org.apache.ibatis.solon.annotation.Db;
|
||||||
|
import org.dromara.solonplugins.job.IJobHandler;
|
||||||
|
import org.dromara.solonplugins.job.IJobSource;
|
||||||
|
import org.dromara.solonplugins.job.JobInfo;
|
||||||
|
import org.dromara.solonplugins.job.impl.JobExecutor;
|
||||||
import org.noear.solon.Solon;
|
import org.noear.solon.Solon;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
|
import org.noear.solon.annotation.Init;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -45,6 +50,29 @@ public class JobInfoService implements IJobSource {
|
|||||||
private MapperFacade mapperFacade;
|
private MapperFacade mapperFacade;
|
||||||
@Db
|
@Db
|
||||||
private JobInfoMapper jobInfoMapper;
|
private JobInfoMapper jobInfoMapper;
|
||||||
|
@Inject
|
||||||
|
private DataCleanJob dataCleanJob;
|
||||||
|
@Inject
|
||||||
|
private DemoJob demoJob;
|
||||||
|
@Inject
|
||||||
|
private FlowReportForDayJob flowReportForDayJob;
|
||||||
|
@Inject
|
||||||
|
private FlowReportForHourJob flowReportForHourJob;
|
||||||
|
@Inject
|
||||||
|
private FlowReportForMinuteJob flowReportForMinuteJob;
|
||||||
|
@Inject
|
||||||
|
private FlowReportForMonthJob flowReportForMonthJob;
|
||||||
|
private Map<String, IJobHandler> jobHandlerMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Init
|
||||||
|
public void init() {
|
||||||
|
jobHandlerMap.put("DataCleanJob", dataCleanJob);
|
||||||
|
jobHandlerMap.put("DemoJob", demoJob);
|
||||||
|
jobHandlerMap.put("FlowReportForDayJob", flowReportForDayJob);
|
||||||
|
jobHandlerMap.put("FlowReportForHourJob", flowReportForHourJob);
|
||||||
|
jobHandlerMap.put("FlowReportForMinuteJob", flowReportForMinuteJob);
|
||||||
|
jobHandlerMap.put("FlowReportForMonthJob", flowReportForMonthJob);
|
||||||
|
}
|
||||||
|
|
||||||
public PageInfo<JobInfoListRes> page(PageQuery pageQuery, JobInfoListReq req) {
|
public PageInfo<JobInfoListRes> page(PageQuery pageQuery, JobInfoListReq req) {
|
||||||
Page<JobInfoListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
|
Page<JobInfoListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
|
||||||
@@ -65,22 +93,15 @@ public class JobInfoService implements IJobSource {
|
|||||||
ParamCheckUtil.checkNotNull(jobInfoDO, ExceptionConstant.JOB_INFO_NOT_EXIST);
|
ParamCheckUtil.checkNotNull(jobInfoDO, ExceptionConstant.JOB_INFO_NOT_EXIST);
|
||||||
jobInfoMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
jobInfoMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||||
if (EnableStatusEnum.ENABLE.getStatus().equals(req.getEnable())) {
|
if (EnableStatusEnum.ENABLE.getStatus().equals(req.getEnable())) {
|
||||||
Solon.context().getBean(JobExecutor.class).add(new JobInfo()
|
Solon.context().getBean(JobExecutor.class).startById(String.valueOf(req.getId()));
|
||||||
.setId(String.valueOf(jobInfoDO.getId()))
|
|
||||||
.setName(jobInfoDO.getHandler())
|
|
||||||
.setDesc(jobInfoDO.getDesc())
|
|
||||||
.setCron(jobInfoDO.getCron())
|
|
||||||
.setParam(jobInfoDO.getParam())
|
|
||||||
.setEnable(true)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
Solon.context().getBean(JobExecutor.class).remove(String.valueOf(req.getId()));
|
Solon.context().getBean(JobExecutor.class).stopById(String.valueOf(req.getId()));
|
||||||
}
|
}
|
||||||
return new JobInfoUpdateEnableStatusRes();
|
return new JobInfoUpdateEnableStatusRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public JobInfoExecuteRes execute(JobInfoExecuteReq req) {
|
public JobInfoExecuteRes execute(JobInfoExecuteReq req) {
|
||||||
Solon.context().getBean(JobExecutor.class).trigger(String.valueOf(req.getId()), req.getParam());
|
Solon.context().getBean(JobExecutor.class).triggerById(String.valueOf(req.getId()), req.getParam());
|
||||||
return new JobInfoExecuteRes();
|
return new JobInfoExecuteRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +120,7 @@ public class JobInfoService implements IJobSource {
|
|||||||
.setCron(item.getCron())
|
.setCron(item.getCron())
|
||||||
.setParam(item.getParam())
|
.setParam(item.getParam())
|
||||||
.setEnable(EnableStatusEnum.ENABLE.getStatus().equals(item.getEnable()))
|
.setEnable(EnableStatusEnum.ENABLE.getStatus().equals(item.getEnable()))
|
||||||
|
.setJobHandler(jobHandlerMap.get(item.getHandler()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -30,12 +30,12 @@ import org.dromara.neutrinoproxy.server.controller.req.log.JobLogListReq;
|
|||||||
import org.dromara.neutrinoproxy.server.controller.res.log.JobLogListRes;
|
import org.dromara.neutrinoproxy.server.controller.res.log.JobLogListRes;
|
||||||
import org.dromara.neutrinoproxy.server.dal.JobLogMapper;
|
import org.dromara.neutrinoproxy.server.dal.JobLogMapper;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.JobLogDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.JobLogDO;
|
||||||
import fun.asgc.solon.extend.job.IJobCallback;
|
|
||||||
import fun.asgc.solon.extend.job.JobInfo;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import ma.glasnost.orika.MapperFacade;
|
import ma.glasnost.orika.MapperFacade;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.apache.ibatis.solon.annotation.Db;
|
import org.apache.ibatis.solon.annotation.Db;
|
||||||
|
import org.dromara.solonplugins.job.IJobCallback;
|
||||||
|
import org.dromara.solonplugins.job.JobInfo;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
import org.noear.solon.annotation.Inject;
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -2,6 +2,7 @@ package org.dromara.neutrinoproxy.server.service;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
|
||||||
import org.dromara.neutrinoproxy.server.controller.res.system.ProtocalListRes;
|
import org.dromara.neutrinoproxy.server.controller.res.system.ProtocalListRes;
|
||||||
import org.noear.solon.annotation.Component;
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ public class ProtocalService {
|
|||||||
public List<ProtocalListRes> list() {
|
public List<ProtocalListRes> list() {
|
||||||
return Lists.newArrayList(
|
return Lists.newArrayList(
|
||||||
new ProtocalListRes().setName("TCP").setEnable(Boolean.TRUE).setRemark("支持一切TCP之上的协议"),
|
new ProtocalListRes().setName("TCP").setEnable(Boolean.TRUE).setRemark("支持一切TCP之上的协议"),
|
||||||
new ProtocalListRes().setName("HTTP").setEnable(Boolean.TRUE).setRemark("支持绑定子域名,未绑定时等价于时使用TCP"),
|
new ProtocalListRes().setName("HTTP(S)").setEnable(Boolean.TRUE).setRemark("支持绑定子域名,未绑定时等价于时使用TCP。 若配置了证书,则同时支持HTTPS。"),
|
||||||
new ProtocalListRes().setName("UDP").setEnable(Boolean.FALSE).setRemark("暂不支持")
|
new ProtocalListRes().setName("UDP").setEnable(Boolean.FALSE).setRemark("暂不支持")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@ import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO;
|
|||||||
import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO;
|
||||||
import org.dromara.neutrinoproxy.server.dal.entity.UserDO;
|
import org.dromara.neutrinoproxy.server.dal.entity.UserDO;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler;
|
import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.core.VisitorChannelHandler;
|
import org.dromara.neutrinoproxy.server.proxy.core.TcpVisitorChannelHandler;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo;
|
import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo;
|
||||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
|
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
|
||||||
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
|
||||||
@@ -224,7 +224,7 @@ public class VisitorChannelService {
|
|||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ch.pipeline().addFirst(new BytesMetricsHandler());
|
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||||
ch.pipeline().addLast(new VisitorChannelHandler());
|
ch.pipeline().addLast(new TcpVisitorChannelHandler());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,25 @@ neutrino:
|
|||||||
initial-bytes-to-strip: 0
|
initial-bytes-to-strip: 0
|
||||||
length-adjustment: 0
|
length-adjustment: 0
|
||||||
read-idle-time: 40
|
read-idle-time: 40
|
||||||
write-idle-time: 10
|
write-idle-time: 5
|
||||||
all-idle-time-seconds: 0
|
all-idle-time-seconds: 0
|
||||||
server:
|
tunnel:
|
||||||
boss-thread-count: 10
|
boss-thread-count: 2
|
||||||
work-thread-count: 60
|
work-thread-count: 10
|
||||||
port: ${OPEN_PORT:9000}
|
port: ${OPEN_PORT:9000}
|
||||||
ssl-port: ${SSL_PORT:9002}
|
ssl-port: ${SSL_PORT:9002}
|
||||||
key-store-password: ${STORE_PASS:123456}
|
key-store-password: ${STORE_PASS:123456}
|
||||||
key-manager-password: ${MGR_PASS:123456}
|
key-manager-password: ${MGR_PASS:123456}
|
||||||
jks-path: ${JKS_PATH:classpath:/test.jks}
|
jks-path: ${JKS_PATH:classpath:/test.jks}
|
||||||
|
server:
|
||||||
|
boss-thread-count: 5
|
||||||
|
work-thread-count: 20
|
||||||
http-proxy-port: ${HTTP_PROXY_PORT:80}
|
http-proxy-port: ${HTTP_PROXY_PORT:80}
|
||||||
|
https-proxy-port: ${HTTPS_PROXY_PORT:443}
|
||||||
# 如果不配置,则不支持域名映射
|
# 如果不配置,则不支持域名映射
|
||||||
domain-name: ${DOMAIN_NAME:}
|
domain-name: ${DOMAIN_NAME:}
|
||||||
|
key-store-password: ${HTTPS_STORE_PASS:}
|
||||||
|
jks-path: ${HTTPS_JKS_PATH:}
|
||||||
data:
|
data:
|
||||||
db:
|
db:
|
||||||
type: ${DB_TYPE:sqlite}
|
type: ${DB_TYPE:sqlite}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-server/app.log"/>
|
<property name="LOG_FILE" value="./neutrino-proxy-server.log"/>
|
||||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
||||||
<!-- <property name="ENCODE" value="utf8" />-->
|
<!-- <property name="ENCODE" value="utf8" />-->
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ CREATE TABLE IF NOT EXISTS `user_login_record` (
|
|||||||
#客户端连接记录表
|
#客户端连接记录表
|
||||||
CREATE TABLE IF NOT EXISTS `client_connect_record` (
|
CREATE TABLE IF NOT EXISTS `client_connect_record` (
|
||||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
`ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'IP',
|
`ip` varchar(50) NOT NULL COMMENT 'IP',
|
||||||
`license_id` int NOT NULL COMMENT 'licenseId',
|
`license_id` int NOT NULL COMMENT 'licenseId',
|
||||||
`type` int NOT NULL COMMENT '类型(1、连接 2、断开连接)',
|
`type` int NOT NULL COMMENT '类型(1、连接 2、断开连接)',
|
||||||
`msg` varchar(512) DEFAULT NULL COMMENT '消息',
|
`msg` varchar(512) DEFAULT NULL COMMENT '消息',
|
||||||
@@ -163,7 +163,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_hour` (
|
|||||||
`write_bytes` int NOT NULL COMMENT '写入流量',
|
`write_bytes` int NOT NULL COMMENT '写入流量',
|
||||||
`read_bytes` int NOT NULL COMMENT '读取流量',
|
`read_bytes` int NOT NULL COMMENT '读取流量',
|
||||||
`date` datetime(3) NOT NULL COMMENT '时间',
|
`date` datetime(3) NOT NULL COMMENT '时间',
|
||||||
`date_str` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '时间 yyyy-MM-dd HH',
|
`date_str` varchar(20) NOT NULL COMMENT '时间 yyyy-MM-dd HH',
|
||||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `I_flow_report_hour_create_time` (`create_time`) USING BTREE,
|
KEY `I_flow_report_hour_create_time` (`create_time`) USING BTREE,
|
||||||
@@ -180,7 +180,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_day` (
|
|||||||
`write_bytes` int NOT NULL COMMENT '写入流量',
|
`write_bytes` int NOT NULL COMMENT '写入流量',
|
||||||
`read_bytes` int NOT NULL COMMENT '读取流量',
|
`read_bytes` int NOT NULL COMMENT '读取流量',
|
||||||
`date` datetime(3) NOT NULL COMMENT '时间',
|
`date` datetime(3) NOT NULL COMMENT '时间',
|
||||||
`date_str` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '时间 yyyy-MM-dd',
|
`date_str` varchar(20) NOT NULL COMMENT '时间 yyyy-MM-dd',
|
||||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `I_flow_report_day_create_time` (`create_time`) USING BTREE,
|
KEY `I_flow_report_day_create_time` (`create_time`) USING BTREE,
|
||||||
@@ -197,7 +197,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` (
|
|||||||
`write_bytes` int NOT NULL COMMENT '写入流量',
|
`write_bytes` int NOT NULL COMMENT '写入流量',
|
||||||
`read_bytes` int NOT NULL COMMENT '读取流量',
|
`read_bytes` int NOT NULL COMMENT '读取流量',
|
||||||
`date` datetime(3) NOT NULL COMMENT '时间',
|
`date` datetime(3) NOT NULL COMMENT '时间',
|
||||||
`date_str` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '时间 yyyy-MM',
|
`date_str` varchar(20) NOT NULL COMMENT '时间 yyyy-MM',
|
||||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `I_flow_report_month_create_time` (`create_time`) USING BTREE,
|
KEY `I_flow_report_month_create_time` (`create_time`) USING BTREE,
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -46,8 +46,8 @@ registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
|
|||||||
- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server`
|
- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server`
|
||||||
- 将` neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`上传至服务器部署目录。
|
- 将` neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`上传至服务器部署目录。
|
||||||
- 解压`neutrino-proxy-admin.zip`文件
|
- 解压`neutrino-proxy-admin.zip`文件
|
||||||
- 执行命令`java -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用sqlite数据库。
|
- 执行命令`java -Dfile.encoding=utf-8 -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用sqlite数据库。
|
||||||
- 若需要指定自己的mysql数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署
|
- 若需要指定自己的mysql数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -Dfile.encoding=utf-8 -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署
|
||||||
- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server_start.sh 使用shell脚本启动服务端。
|
- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server_start.sh 使用shell脚本启动服务端。
|
||||||
|
|
||||||
## 2、管理后台配置
|
## 2、管理后台配置
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ License分组下的端口,归属于某一个特定的License。
|
|||||||
|
|
||||||
# 基础端口说明
|
# 基础端口说明
|
||||||
- WEB端口:服务端API、后台管理访问端口,默认为:8888
|
- WEB端口:服务端API、后台管理访问端口,默认为:8888
|
||||||
- 服务端等待客户端连接的端口,非SSL:默认9000,SSL端口:默认9000(若不需要SSL支持,可不配置SSL端口)
|
- 服务端等待客户端连接的端口,非SSL:默认9000,SSL端口:默认9002(若不需要SSL支持,可不配置SSL端口)
|
||||||
- HTTP代理端口:默认80,用于域名映射,若无需域名映射,可以忽略。
|
- HTTP代理端口:默认80,用于域名映射,若无需域名映射,可以忽略。
|
||||||
@@ -289,7 +289,7 @@ postList: none
|
|||||||
<h2>🤝 友情开源项目</h2>
|
<h2>🤝 友情开源项目</h2>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://gitee.com/noear/solon" target="_blank" class="friends-item" style="display: flex;">
|
<a href="https://gitee.com/noear/solon" target="_blank" class="friends-item" style="display: flex;">
|
||||||
<img class="no-zoom friends-item-img hover-alt" :src="$withBase('/img/logo/solon.png')" msg="一个高效的应用开发框架:更快、更小、更简单。" style="margin-left: 15px;"/><div style="margin-left: 10px;font-size: 30px;">Solon</div>
|
<img class="no-zoom friends-item-img hover-alt" :src="$withBase('/img/logo/solon_logo_500_150.png')" msg="一个高效的应用开发框架:更快、更小、更简单。" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
<module>neutrino-proxy-core</module>
|
<module>neutrino-proxy-core</module>
|
||||||
<module>neutrino-proxy-client</module>
|
<module>neutrino-proxy-client</module>
|
||||||
<module>neutrino-proxy-server</module>
|
<module>neutrino-proxy-server</module>
|
||||||
<module>_solon_plugin/job-solon-plugin</module>
|
|
||||||
<module>_solon_plugin/orika-solon-plugin</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -104,6 +102,22 @@
|
|||||||
<artifactId>HikariCP</artifactId>
|
<artifactId>HikariCP</artifactId>
|
||||||
<version>4.0.3</version>
|
<version>4.0.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.solon-plugins</groupId>
|
||||||
|
<artifactId>job-solon-plugin</artifactId>
|
||||||
|
<version>0.0.4</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.solon-plugins</groupId>
|
||||||
|
<artifactId>orika-solon-plugin</artifactId>
|
||||||
|
<version>0.0.4</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ cp $OUT $JAR_PATH/logs/back_$time.out
|
|||||||
fi
|
fi
|
||||||
rm -f $OUT
|
rm -f $OUT
|
||||||
cd $JAR_PATH
|
cd $JAR_PATH
|
||||||
nohup java $JAVA_OPS -jar $NAME.jar $startupParams > $OUT 2>&1 &
|
nohup java -Dfile.encoding=utf-8 $JAVA_OPS -jar $NAME.jar $startupParams > $OUT 2>&1 &
|
||||||
echo "sleep 15s wating service start"
|
echo "sleep 15s wating service start"
|
||||||
sleep 15
|
sleep 15
|
||||||
tail -200 $OUT
|
tail -200 $OUT
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ cp $OUT $JAR_PATH/logs/back_$time.out
|
|||||||
fi
|
fi
|
||||||
rm -f $OUT
|
rm -f $OUT
|
||||||
cd $JAR_PATH
|
cd $JAR_PATH
|
||||||
nohup java $JAVA_OPS -jar $NAME.jar > $OUT 2>&1 &
|
nohup java -Dfile.encoding=utf-8 $JAVA_OPS -jar $NAME.jar > $OUT 2>&1 &
|
||||||
echo "sleep 15s wating service start"
|
echo "sleep 15s wating service start"
|
||||||
sleep 15
|
sleep 15
|
||||||
tail -200 $OUT
|
tail -200 $OUT
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
- [ ] 增加针对https的支持
|
- [ ] 增加针对https的支持
|
||||||
|
|
||||||
# Bug
|
# Bug
|
||||||
|
- 指令通达被close的问题,org.dromara.neutrinoproxy.server.proxy.core.ServerChannelHandler.channelInactive
|
||||||
- windows环境下直接运行发布版的jar包,日志输出乱码
|
- windows环境下直接运行发布版的jar包,日志输出乱码
|
||||||
- 代理mysql时,使用未开启远程访问的账号走代理访问mysql,代理客户端出现断开现象
|
- 代理mysql时,使用未开启远程访问的账号走代理访问mysql,代理客户端出现断开现象
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user