feat:负载中心

master
Yunfei Du 2024-05-31 11:03:46 +08:00
commit 77ade6288e
32 changed files with 1547 additions and 0 deletions

38
.gitignore vendored 100644
View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
.idea/.gitignore vendored 100644
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
</component>

14
.idea/misc.xml 100644
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml 100644
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

171
pom.xml 100644
View File

@ -0,0 +1,171 @@
<?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>
<groupId>com.load</groupId>
<artifactId>Load_center</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 规定SpringBoot版本 -->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.6.2</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.0</version>
</dependency>
<!-- http协议 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!-- servlet 依赖. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
<scope>runtime</scope>
</dependency>
<!-- Mybatis 依赖配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- PageHelper mybatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<!-- lombok小辣椒依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<!-- quartz定时任务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!-- mq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>ecs20140526</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.load;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*
* @author YunFei·Du
* @ClassName: LoadApplication
* @Description:
* @CreateTime: 2024/5/27 09:50
*/
@SpringBootApplication
public class LoadApplication {
public static void main(String[] args) {
SpringApplication.run(LoadApplication.class,args);
}
}

View File

@ -0,0 +1,10 @@
package com.load;
/**
* @ClassName MqttApplication
* @Description
* @Author YunFei.Du
* @Date 2024/5/30 9:19
*/
public class MqttApplication {
}

View File

@ -0,0 +1,61 @@
package com.load;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
/**
* MQTT
*/
public class SubscribeSample {
public static void main(String[] args) {
/**
*
*/
String broker = "tcp://39.100.87.192:1883";
/**
*
*/
String topic = "mqtt/test";
String username = "emqx";
String password = "public";
/**
* ID()
*/
String clientid = "subscribe_client";
int qos = 0;
try {
MqttClient client = new MqttClient(broker, clientid, new MemoryPersistence());
// 连接参数
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setConnectionTimeout(60);
// 堵塞60S
options.setKeepAliveInterval(60);
// 设置回调
client.setCallback(new MqttCallback() {
public void connectionLost(Throwable cause) {
System.out.println("connectionLost: " + cause.getMessage());
}
public void messageArrived(String topic, MqttMessage message) {
System.out.println("topic: " + topic);
System.out.println("Qos: " + message.getQos());
System.out.println("message content: " + new String(message.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
});
client.connect(options);
client.subscribe(topic, qos);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,171 @@
package com.load.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.DescribeInstancesRequest;
import com.aliyun.ecs20140526.models.DescribeInstancesResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import com.load.domain.ConnectWeight;
import com.load.util.ECSTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
/**
*
*
* @author YunFei·Du
* @ClassName: InitConnectWeight
* @Description:
* @CreateTime: 2024/5/27 21:31
*/
@Component
@Slf4j
public class InitConnectWeight implements ApplicationRunner {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private RestTemplate restTemplate;
@Override
public void run(ApplicationArguments args) throws Exception {
ArrayList<ConnectWeight> connectWeightList = new ArrayList<>();
ArrayList<String> ipList = new ArrayList<>();
Client client = null;
//获取阿里云客户端
try {
client = ECSTool.createClient();
} catch (Exception e) {
throw new RuntimeException(e);
}
//获取所有实例
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
.setRegionId("cn-zhangjiakou");
// .setInstanceType("ecs.e-c1m2.xlarge");
RuntimeOptions runtime = new RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
DescribeInstancesResponse describeInstancesResponse = client.describeInstancesWithOptions(describeInstancesRequest, runtime);
List<List<String>> ipListList = describeInstancesResponse.getBody().instances.getInstance().stream().map(instance -> instance.publicIpAddress.ipAddress).collect(Collectors.toList());
for (List<String> strings : ipListList) {
for (String ip : strings) {
ipList.add(ip);
}
}
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
//http://fluxmq.muyu.icu/public/login
//遍历所有ip,获取每一个服务的连接数
for (String ip : ipList) {
String url = "http://" + ip + ":8080/public/login";
Map<String, Object> request = new HashMap<>();
request.put("username", "fluxmq");
request.put("password", "fluxmq");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> r = new HttpEntity<Map<String, Object>>(request, httpHeaders);
String result = restTemplate.postForObject(url, r, String.class);
//http://fluxmq.muyu.icu/public/cluster
int nextInt = new Random().nextInt(1000);
String getInfoUrl = "http://" + ip + ":8080/public/cluster?random=" + nextInt;
HttpHeaders httpHeadersGetInfo = new HttpHeaders();
httpHeadersGetInfo.setContentType(MediaType.APPLICATION_JSON);
httpHeadersGetInfo.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeadersGetInfo.set("Cookie", result);
HttpEntity getInfoRequest = new HttpEntity(httpHeadersGetInfo);
ResponseEntity<String> responseInfo = restTemplate.exchange(getInfoUrl, HttpMethod.GET, getInfoRequest, String.class, 1);
log.error("响应是:{}", responseInfo.getBody());
JSONArray jsonArray = JSON.parseArray(responseInfo.getBody());
if (jsonArray.size() > 0) {
JSONObject jsonObject = jsonArray.getJSONObject(0);
Integer connectSize = Integer.valueOf(jsonObject.getJSONObject("mqttInfo").getString("connectSize"));
connectWeightList.add(new ConnectWeight(ip,100-connectSize));
log.error("链接数量:{}", connectSize);
} else {
log.error("得到的相应数据为null");
}
}
// 初始化权重求和变量
Integer sum =0;
// 遍历ConnectWeight列表计算所有权重值的总和
for (ConnectWeight connectWeight : connectWeightList) {
sum = sum + connectWeight.getWeightValue();
}
// 初始化最大轮询次数为0
int max = 0;
for (ConnectWeight connectWeight : connectWeightList) {
log.error("权重值:{}",connectWeight.getWeightValue());
// 将权重值转换为百分比并向下取整,得到每一轮的轮询次数
Integer result = BigDecimal.valueOf(connectWeight.getWeightValue() * 100).divide(BigDecimal.valueOf(sum), 0, RoundingMode.DOWN).intValue();
// 如果计算出的轮询次数大于当前最大值,更新最大值
if (result > max){
max = result;
}
// 更新ConnectWeight对象的权重值为计算出的轮询次数
connectWeight.setWeightValue(result);
// 输出100次轮询次数
log.error("100次轮询次数:{}",result);
}
ArrayList<String> weightIpList = new ArrayList<>();
//轮询出现次数
for (int i = 0; i <= max; i++) {
for (ConnectWeight connectWeight : connectWeightList) {
if (connectWeight.getWeightValue() > i){
weightIpList.add(connectWeight.getCarServerIp());
}else if (connectWeight.getWeightValue() == max){
weightIpList.add(connectWeight.getCarServerIp());
}
}
}
//存入redis
redisTemplate.delete("ipList");
for (String ip : weightIpList) {
redisTemplate.opsForList().rightPush("ipList",ip);
}
}
}

View File

@ -0,0 +1,54 @@
package com.load.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* rabbitMq
*
* @author YunFei·Du
* @ClassName: RabbitmqConfig
* @Description: rabbitMq
* @CreateTime: 2024/5/27 16:56
*/
@Configuration
public class RabbitmqConfig {
public static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
public static final String QUEUE_INFORM_SMS = "disconnect_connect";
public static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";
public static final String ROUTINGKEY_EMAIL="inform.#.email.#";
public static final String ROUTINGKEY_SMS="inform.#.sms.#";
@Bean(EXCHANGE_TOPICS_INFORM)
public Exchange EXCHANGE_TOPICS_INFORM(){
//durable(true) 持久化mq重启之后交换机还在
return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_INFORM).durable(true).build();
}
//声明QUEUE_INFORM_EMAIL队列
@Bean(QUEUE_INFORM_EMAIL)
public Queue QUEUE_INFORM_EMAIL(){
return new Queue(QUEUE_INFORM_EMAIL);
}
//声明QUEUE_INFORM_SMS队列
@Bean(QUEUE_INFORM_SMS)
public Queue QUEUE_INFORM_SMS(){
return new Queue(QUEUE_INFORM_SMS);
}
//ROUTINGKEY_EMAIL队列绑定交换机指定routingKey
@Bean
public Binding BINDING_QUEUE_INFORM_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs();
}
//ROUTINGKEY_SMS队列绑定交换机指定routingKey
@Bean
public Binding BINDING_ROUTINGKEY_SMS(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_SMS).noargs();
}
}

View File

@ -0,0 +1,33 @@
package com.load.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redsi
*
* @author YunFei·Du
* @ClassName: RedisConfoig
* @Description: redsi
* @CreateTime: 2024/5/27 14:16
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}

View File

@ -0,0 +1,33 @@
package com.load.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* rest
*
* @author YunFei·Du
* @ClassName: RestTemplateConfig
* @Description: rest
* @CreateTime: 2024/5/27 10:01
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
//超时设置
factory.setReadTimeout(5000);//ms
factory.setConnectTimeout(15000);//ms
return factory;
}
}

View File

@ -0,0 +1,42 @@
package com.load.consumer;
import com.load.config.InitConnectWeight;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.DefaultApplicationArguments;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
*
*
* @author YunFei·Du
* @ClassName: ReceiveHandler
* @Description:
* @CreateTime: 2024/5/27 17:00
*/
@Component
@Slf4j
public class ReceiveHandler {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private InitConnectWeight initConnectWeight;
//监听sms队列 ADD_LOG_AAA
@RabbitListener(queues = {"ADD_LOG_AAA"})
public void receiveSms(Message message) {
try {
initConnectWeight.run(new DefaultApplicationArguments());
redisTemplate.delete("count");
log.warn("ip权重重新分配成功!");
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println("消费者得到的消息: " + new String(message.getBody()));
}
}

View File

@ -0,0 +1,47 @@
package com.load.controller;
import com.load.domain.MqttServerModel;
import com.load.domain.Result;
import com.load.domain.req.VehicleConnectionReq;
import com.load.service.LoadBalanceService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
*
* @author YunFei·Du
* @ClassName: ConnectController
* @Description:
* @CreateTime: 2024/5/27 09:00
*/
@RestController
@RequestMapping("loadBalance")
@Log4j2
public class LoadBalanceController {
@Autowired
private LoadBalanceService loadBalanceService;
/**
*
*/
@PostMapping("/getConnect")
public Result<MqttServerModel> getConnect(@RequestBody VehicleConnectionReq vehicleConnectionReq){
return loadBalanceService.getConnect(vehicleConnectionReq);
}
/**
*
*/
@GetMapping("/createConnect")
public void createConnect() {
loadBalanceService.createConnect ( );
log.info ( "创建实例成功" );
}
}

View File

@ -0,0 +1,32 @@
package com.load.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 使
*
* @author YunFei·Du
* @ClassName: MemoryUserInfo
* @Description: 使
* @CreateTime: 2024/5/27 15:53
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConnectUseInfo {
/**
* ID
*/
private String clusterId;
/**
* IP
*/
private String ipAddress;
/**
*
*/
private Integer remainingNum;
}

View File

@ -0,0 +1,28 @@
package com.load.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*
* @author YunFei·Du
* @ClassName: CarServer
* @Description:
* @CreateTime: 2024/5/27 09:04
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConnectWeight {
/**
* ip
*/
private String carServerIp;
/**
*
*/
private Integer weightValue;
}

View File

@ -0,0 +1,93 @@
package com.load.domain;
/**
*
*
* @author ruoyi
*/
public class HttpStatus {
/**
*
*/
public static final int SUCCESS = 200;
/**
*
*/
public static final int CREATED = 201;
/**
*
*/
public static final int ACCEPTED = 202;
/**
*
*/
public static final int NO_CONTENT = 204;
/**
*
*/
public static final int MOVED_PERM = 301;
/**
*
*/
public static final int SEE_OTHER = 303;
/**
*
*/
public static final int NOT_MODIFIED = 304;
/**
*
*/
public static final int BAD_REQUEST = 400;
/**
*
*/
public static final int UNAUTHORIZED = 401;
/**
* 访
*/
public static final int FORBIDDEN = 403;
/**
*
*/
public static final int NOT_FOUND = 404;
/**
* http
*/
public static final int BAD_METHOD = 405;
/**
*
*/
public static final int CONFLICT = 409;
/**
*
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
*
*/
public static final int ERROR = 500;
/**
*
*/
public static final int NOT_IMPLEMENTED = 501;
/**
*
*/
public static final int WARN = 601;
}

View File

@ -0,0 +1,28 @@
package com.load.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Mqtt
* @author YunFei.Du
* @date 22:08 2024/5/29
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MqttServerModel {
/**
* MQTT
*/
private String broker;
/**
* MQTT
*/
private String topic;
}

View File

@ -0,0 +1,101 @@
package com.load.domain;
import lombok.Data;
import java.io.Serializable;
/**
*
*
* @author ruoyi
*/
@Data
public class Result<T> implements Serializable {
/**
*
*/
public static final int SUCCESS = HttpStatus.SUCCESS;
/**
*
*/
public static final int FAIL = HttpStatus.ERROR;
private static final long serialVersionUID = 1L;
/**
*
*/
private static final int WARN = HttpStatus.WARN;
private int code;
private String msg;
private T data;
public static <T> Result<T> success () {
return restResult(null, SUCCESS, "操作成功");
}
public static <T> Result<T> success (T data) {
return restResult(data, SUCCESS, "操作成功");
}
public static <T> Result<T> success (T data, String msg) {
return restResult(data, SUCCESS, msg);
}
public static <T> Result<T> error () {
return restResult(null, FAIL, "操作失败");
}
public static <T> Result<T> error (String msg) {
return restResult(null, FAIL, msg);
}
public static <T> Result<T> error (T data) {
return restResult(data, FAIL, "操作失败");
}
public static <T> Result<T> error (T data, String msg) {
return restResult(data, FAIL, msg);
}
public static <T> Result<T> error (int code, String msg) {
return restResult(null, code, msg);
}
public static <T> Result<T> warn () {
return restResult(null, WARN, "操作失败");
}
public static <T> Result<T> warn (String msg) {
return restResult(null, WARN, msg);
}
public static <T> Result<T> warn (T data) {
return restResult(data, WARN, "操作失败");
}
public static <T> Result<T> warn (T data, String msg) {
return restResult(data, WARN, msg);
}
public static <T> Result<T> warn (int code, String msg) {
return restResult(null, code, msg);
}
private static <T> Result<T> restResult (T data, int code, String msg) {
Result<T> apiResult = new Result<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public static <T> Boolean isError (Result<T> ret) {
return !isSuccess(ret);
}
public static <T> Boolean isSuccess (Result<T> ret) {
return Result.SUCCESS == ret.getCode();
}
}

View File

@ -0,0 +1,19 @@
package com.load.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName VinIp
* @Description
* @Author YunFei.Du
* @Date 2024/5/30 20:34
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VinIp {
String vin;
String ip;
}

View File

@ -0,0 +1,51 @@
package com.load.domain.req;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author DongZl
* @description:
* @Date 2023-11-28 10:32
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VehicleConnectionReq {
/**
* {
* "vehicleVin": "VIN1234567894",
* "timestamp": "11111",
* "username": "你好",
* "nonce": "33"
* }
*/
/**
* vin
*/
@JSONField(name = "vehicleVin")
private String vehicleVin;
/**
*
*/
private String timestamp;
/**
*
*/
@JSONField(name = "username")
private String userName;
/**
*
*/
private String nonce;
}

View File

@ -0,0 +1,32 @@
package com.load.mqtt;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Service;
/**
* ()
* @author YunFei.Du
* @date 22:37 2024/5/30
*/
@Service
public class MessageCallbackService implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
System.out.println("connectionLost: " + cause.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) {
System.out.println("topic: " + topic);
System.out.println("Qos: " + message.getQos());
System.out.println("message content: " + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,46 @@
package com.load.mqtt;
import lombok.AllArgsConstructor;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.stereotype.Service;
/**
* Mqtt
* @author YunFei.Du
* @date 22:38 2024/5/30
*/
@Service
@AllArgsConstructor
public class MqttFactory {
private final MessageCallbackService messageCallbackService;
// 连接参数
/**
* Mqtt
* @param mqttProperties
* @return
*/
public MqttClient createClient(MqttProperties mqttProperties){
MqttClient mqttClient =null;
try {
mqttClient=new MqttClient ( mqttProperties.getBroker() , mqttProperties.getClientId() , new MemoryPersistence() );
} catch (MqttException e) {
throw new RuntimeException ( e );
}
MqttConnectOptions options = new MqttConnectOptions ( );
if (mqttProperties.isLogin()){
options.setUserName ( mqttProperties.getUsername() );
options.setPassword ( mqttProperties.getPassword().toCharArray() );
}
options.setConnectionTimeout(60);
options.setKeepAliveInterval(60);
mqttClient.setCallback ( messageCallbackService );
return mqttClient;
}
}

View File

@ -0,0 +1,51 @@
package com.load.mqtt;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
/**
*
* @author YunFei.Du
* @date 8:53 2024/5/30
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MqttProperties {
private String broker;
private String topic;
private String username;
private String password;
private String clientId;
/**
* mqtt
* @param ip
* @param topic
* @return
*/
public static MqttProperties configBuild(String ip, String topic){
return MqttProperties.builder()
.broker("tcp://"+ip+":1883")
.topic(topic)
.username("admin")
.password("public")
.clientId("protocol-parsing") //协议解析 定值 --> 配置
.build();
}
/**
*
* @return
*/
public boolean isLogin(){
// commons-lang3
return StringUtils.isBlank ( username ) && !StringUtils.isBlank ( password );
}
}

View File

@ -0,0 +1,28 @@
package com.load.rebbitmq;
import com.load.mqtt.MqttFactory;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* @author YunFei.Du
* @date 9:21 2024/5/30
*/
@Component
@Log4j2
public class MsgHandle {
@Autowired
private MqttFactory mqttFactory;
// @RabbitListener(queues = {"create.topic"})
// private void msg(String msg){
// log.info ( "接收到消息:{}" , msg );
// MqttProperties mqttProperties = MqttProperties.configBuild ( "39.100.87.192", "mqtt/test" );
// mqttFactory.createClient ( mqttProperties );
//
// }
}

View File

@ -0,0 +1,36 @@
package com.load.rebbitmq;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* rabbit
* @author YunFei.Du
* @date 8:39 2024/5/31
*/
@Configuration
public class RabbitConfig {
@Bean
public Queue initQueue() {
return new Queue ("create.totic", true);
}
@Bean
public DirectExchange direct(){
return new DirectExchange ("topic.direct");
}
@Bean
public Binding bindingla(DirectExchange direct, Queue initQueue){
return BindingBuilder.bind(initQueue)
.to(direct)
.with("protocol-parsing");
}
}

View File

@ -0,0 +1,27 @@
package com.load.service;
/**
*
*
* @author YunFei·Du
* @ClassName: ConnectServer
* @Description:
* @CreateTime: 2024/5/27 09:22
*/
import com.load.domain.MqttServerModel;
import com.load.domain.Result;
import com.load.domain.req.VehicleConnectionReq;
/**
*@ClassName ConnectServer
*@Description
*@Author YunFei·Du
*@Date 2024/5/27 09:22
*/
public interface LoadBalanceService {
Result<MqttServerModel> getConnect(VehicleConnectionReq vehicleConnectionReq);
void createConnect();
}

View File

@ -0,0 +1,81 @@
package com.load.service.impl;
import com.load.domain.MqttServerModel;
import com.load.domain.Result;
import com.load.domain.VinIp;
import com.load.domain.req.VehicleConnectionReq;
import com.load.service.LoadBalanceService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import static com.load.util.ECSTool.runEcsInstance;
/**
*
*
* @author YunFei·Du
* @ClassName: ConnectServiceImpl
* @Description:
* @CreateTime: 2024/5/27 09:22
*/
@Service
@Slf4j
public class LoadBalanceServiceImpl implements LoadBalanceService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public Result<MqttServerModel> getConnect(VehicleConnectionReq vehicleConnectionReq) {
log.error(vehicleConnectionReq.toString());
//判断是否有游标key --count
if (redisTemplate.hasKey("count")){
Integer count = Integer.valueOf(redisTemplate.opsForValue().get("count"));
if (count == 100){
redisTemplate.opsForValue().set("count",String.valueOf(0));
}else {
redisTemplate.opsForValue().set("count",String.valueOf(count + 1));
}
//根据游标count获取服务IP
String ip = redisTemplate.opsForList().index("ipList", count);
//关联车辆和服务
this.insertVinIp(new VinIp (vehicleConnectionReq.getVehicleVin(),ip));
//响应信息
return Result.success(new MqttServerModel("tcp://"+ip+":1883","test1"));
}else {
redisTemplate.opsForValue().set("count",String.valueOf(1));
//根据游标count获取服务IP
String ip = redisTemplate.opsForList().index("ipList", 0);
//关联车辆和服务
this.insertVinIp(new VinIp(vehicleConnectionReq.getVehicleVin(),ip));
//响应信息
return Result.success(new MqttServerModel("tcp://"+ip+":1883","test1"));
}
}
private void insertVinIp(VinIp vinIp) {
}
@SneakyThrows
@Override
public void createConnect() {
runEcsInstance("cn-zhangjiakou", "lt-8vbepqjihmawbkqcwkcm");
}
}

View File

@ -0,0 +1,150 @@
package com.load.util;
import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import lombok.extern.log4j.Log4j2;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* ECS
* @author YunFei.Du
* @date 9:30 2024/5/28
*/
@Log4j2
public class ECSTool {
/**
* ECS
* @return ECS
* @throws Exception
* 使STS
* 访https://help.aliyun.com/document_detail/378657.html
*/
public static Client createClient() throws Exception {
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
Config config = new Config()
.setAccessKeyId("LTAI5tPTk3MFkmCGBbnQgmrM")
.setAccessKeySecret("q7rLjxrI0SLBXlvNT4VmYcHCNCY2p6");
// Endpoint 请参考 https://api.aliyun.com/product/Ecs
config.endpoint = "ecs.cn-zhangjiakou.aliyuncs.com";
return new Client(config);
}
/**
* ECS
* @param regionId ID
* @param launchTemplateId ID
* @throws Exception API
*/
public static void runEcsInstance(String regionId, String launchTemplateId) throws Exception {
// 创建ECS客户端
Client client = ECSTool.createClient();
// 设置运行实例的请求参数
RunInstancesRequest runInstancesRequest = new RunInstancesRequest()
.setRegionId(regionId)
.setLaunchTemplateId(launchTemplateId);
// 创建运行选项
RuntimeOptions runtime = new RuntimeOptions();
try {
// 调用API运行实例
client.runInstancesWithOptions(runInstancesRequest, runtime);
} catch (Exception error) {
// 处理API调用过程中出现的异常
System.out.println(error.getMessage());
if (error instanceof TeaException) {
// 处理特定类型的异常如TeaException
TeaException teaError = (TeaException) error;
System.out.println(teaError.getData().get("Recommend")); // 打印诊断推荐链接
com.aliyun.teautil.Common.assertAsString(teaError.getMessage()); // 断言错误信息
} else {
// 处理其他类型的异常
System.out.println(error.getMessage());
}
}
}
/**
*
*/
public static void runEcsRemove(String instanceId) throws Exception {
// 创建ECS客户端
Client client = ECSTool.createClient();
DeleteInstanceRequest deleteInstanceRequest = new DeleteInstanceRequest ()
.setInstanceId(instanceId);
RuntimeOptions runtime = new RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
client.deleteInstanceWithOptions(deleteInstanceRequest, runtime);
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
}
/**
*
* @param regionId
* @return
* @throws Exception
*/
public static List<String> FindInstance( String regionId) throws Exception {
// 创建ECS客户端
Client client = ECSTool.createClient ( );
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest ( )
.setRegionId ( regionId );
RuntimeOptions runtime = new RuntimeOptions ( );
List< String > ipList = new ArrayList<> ( );
try {
// 复制代码运行请自行打印 API 的返回值
DescribeInstancesResponse response = client.describeInstancesWithOptions ( describeInstancesRequest, runtime );
List< List< String > > ipListList = response.getBody ( ).instances.getInstance ( ).stream ( ).map ( instance -> instance.publicIpAddress.ipAddress ).collect ( Collectors.toList ( ) );
for (List< String > strings : ipListList) {
for (String ip : strings) {
ipList.add ( ip );
}
System.out.println ( "------------------------" );
}
log.info ( "ipList: " + ipList ); // [39.100.89.218, 39.100.87.192]
return ipList;
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println ( error.getMessage ( ) );
// 诊断地址
System.out.println ( error.getData ( ).get ( "Recommend" ) );
com.aliyun.teautil.Common.assertAsString ( error.message );
} catch (Exception _error) {
TeaException error = new TeaException ( _error.getMessage ( ), _error );
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println ( error.getMessage ( ) );
// 诊断地址
System.out.println ( error.getData ( ).get ( "Recommend" ) );
com.aliyun.teautil.Common.assertAsString ( error.message );
}
return null;
}
}

View File

@ -0,0 +1,24 @@
# 服务器相关
server:
port: 82
spring:
application:
# 协议解析
name: protocol-parsing
rabbitmq:
host: 111.229.102.61
port: 5672
username: guest
password: guest
virtualHost: /
redis:
host: 127.0.0.1
port: 6379
password: dyf@123
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://111.229.102.61/car?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
username: root
password: dyf@123