feat():合并车辆网管
commit
a63d614a23
|
@ -16,5 +16,25 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||
public class CloudVehicleGatewayApplication {
|
||||
public static void main (String[] args) {
|
||||
SpringApplication.run(CloudVehicleGatewayApplication.class, args);
|
||||
System.out.println(" _ooOoo_\n" +
|
||||
" o8888888o\n" +
|
||||
" 88\" . \"88\n" +
|
||||
" (| -_- |)\n" +
|
||||
" O\\ = /O\n" +
|
||||
" ____/`---'\\____\n" +
|
||||
" .' \\\\| |// `.\n" +
|
||||
" / \\\\||| : |||// \\\n" +
|
||||
" / _||||| -:- |||||- \\\n" +
|
||||
" | | \\\\\\ - /// | |\n" +
|
||||
" | \\_| ''\\---/'' | |\n" +
|
||||
" \\ .-\\__ `-` ___/-. /\n" +
|
||||
" ___`. .' /--.--\\ `. . __\n" +
|
||||
" .\"\" '< `.___\\_<|>_/___.' >'\"\".\n" +
|
||||
" | | : `- \\`.;`\\ _ /`;.`/ - ` : | |\n" +
|
||||
" \\ \\ `-. \\_ __\\ /__ _/ .-` / /\n" +
|
||||
" ======`-.____`-.___\\_____/___.-`____.-'======\n" +
|
||||
" `=---='\n" +
|
||||
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
|
||||
" // 佛祖保佑 永不宕机 永无BUG //");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package com.muyu.vehiclegateway.instance;
|
||||
package com.muyu.vehiclegateway.aliyun;
|
||||
|
||||
import com.aliyun.ecs20140526.Client;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
|
@ -0,0 +1,165 @@
|
|||
package com.muyu.vehiclegateway.config;
|
||||
|
||||
import com.aliyun.ecs20140526.Client;
|
||||
import com.aliyun.ecs20140526.models.*;
|
||||
import com.aliyun.tea.TeaException;
|
||||
import com.aliyun.teautil.Common;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.vehiclegateway.aliyun.CreateClient;
|
||||
import com.muyu.vehiclegateway.domain.Instance;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @ClassName AddClientConfig
|
||||
* @Description 描述
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/10/8 16:13
|
||||
*/
|
||||
@Log4j2
|
||||
@Component
|
||||
public class ClientConfig {
|
||||
|
||||
@Autowired
|
||||
private static StringRedisTemplate redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private static RedisService redisService;
|
||||
|
||||
public static String addServer() throws Exception {
|
||||
|
||||
// 创建阿里云ECS客户端
|
||||
Client client = CreateClient.createClient();
|
||||
// 配置系统盘参数
|
||||
RunInstancesRequest.RunInstancesRequestSystemDisk systemDisk = new RunInstancesRequest.RunInstancesRequestSystemDisk()
|
||||
.setSize("40") // 设置系统盘大小为40GB
|
||||
.setCategory("cloud_essd"); // 设置系统盘类型为cloud_essd
|
||||
// 创建创建实例请求对象并设置参数
|
||||
RunInstancesRequest runInstancesRequest = new RunInstancesRequest()
|
||||
// 设置地域ID
|
||||
.setRegionId("cn-shanghai")
|
||||
// 设置镜像ID
|
||||
.setImageId("m-uf6f7atj16s3cjn9q5l8")
|
||||
// 设置实例类型
|
||||
.setInstanceType("ecs.t6-c1m1.large")
|
||||
// 设置安全组ID
|
||||
.setSecurityGroupId("sg-uf6f6dvazjqv127a4yyt")
|
||||
// 设置虚拟交换机ID
|
||||
.setVSwitchId("vsw-uf6htez4ox9k2c4cs8505")
|
||||
// 设置实例名称
|
||||
.setInstanceName("server-mqtt")
|
||||
// 设置实例付费类型为后付费按量付费
|
||||
.setInstanceChargeType("PostPaid")
|
||||
// 设置互联网最大出带宽为1 Mbps
|
||||
.setInternetMaxBandwidthOut(1)
|
||||
// 设置系统盘配置
|
||||
.setSystemDisk(systemDisk)
|
||||
// 设置主机名
|
||||
.setHostName("root")
|
||||
// 设置实例密码
|
||||
.setPassword("Six@211206")
|
||||
// 设置创建实例的数量
|
||||
.setAmount(1);
|
||||
|
||||
|
||||
|
||||
// 创建运行时选项对象
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
|
||||
// 尝试执行创建实例请求
|
||||
try {
|
||||
|
||||
RunInstancesResponse runInstancesResponse = client.runInstancesWithOptions(runInstancesRequest, runtime);
|
||||
RunInstancesResponseBody body = runInstancesResponse.getBody();
|
||||
RunInstancesResponseBody.RunInstancesResponseBodyInstanceIdSets instanceIdSets = body.getInstanceIdSets();
|
||||
|
||||
List<Instance> instanceList = new ArrayList<>();
|
||||
List<String> instanceIps = new ArrayList<>();
|
||||
|
||||
|
||||
Thread.sleep(20000);
|
||||
|
||||
DescribeInstancesResponse describeInstancesResponse = queryInstanceDetails(client);
|
||||
// 获取实例的ID、公网IP和状态
|
||||
|
||||
String ip = "";
|
||||
|
||||
List<DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance> bodyInstancesInstances = describeInstancesResponse.getBody().getInstances().getInstance();
|
||||
for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance instance : bodyInstancesInstances) {
|
||||
String id = instance.getInstanceId();
|
||||
String ipAddress = instance.getPublicIpAddress().getIpAddress().toString();
|
||||
String status = instance.getStatus();
|
||||
Instance instance1 = new Instance(id,ipAddress,status);
|
||||
instanceList.add(instance1);
|
||||
instanceIps.add(ipAddress);
|
||||
|
||||
log.info("实例id为:{}",instance.getInstanceId());
|
||||
log.info("实例ip为:{}",instance.getPublicIpAddress().ipAddress.get(0));
|
||||
log.info("实例状态为{}",instance.getStatus());
|
||||
|
||||
ip = instance.getPublicIpAddress().ipAddress.get(0);
|
||||
|
||||
//每个 实例ip对应的绑定数量
|
||||
redisTemplate.opsForHash().put("serverBindings", ipAddress, 0);
|
||||
|
||||
}
|
||||
|
||||
log.info("====>创建的实例集合:"+instanceList);
|
||||
//把实例信息存入redis
|
||||
redisService.setCacheList("instanceList",instanceList);
|
||||
//实例ip存入redis方便做轮询
|
||||
redisService.setCacheList("ipList",instanceIps);
|
||||
|
||||
return ip;
|
||||
} catch (TeaException error) {
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend"));
|
||||
Common.assertAsString(error.message);
|
||||
} catch (Exception _error) {
|
||||
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend"));
|
||||
Common.assertAsString(error.message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DescribeInstancesResponse queryInstanceDetails(Client client) {
|
||||
|
||||
// 创建查询实例请求对象并设置参数
|
||||
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
|
||||
.setInstanceName("server-mqtt")
|
||||
.setRegionId("cn-shanghai"); // 设置地域ID // 设置实例ID
|
||||
// 创建运行时选项对象
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
|
||||
// 尝试执行查询实例请求
|
||||
try {
|
||||
return client.describeInstancesWithOptions(describeInstancesRequest, runtime);
|
||||
} catch (TeaException e) {
|
||||
// 捕获特定的TeaException并打印详细信息
|
||||
log.info("TeaException occurred: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
// 捕获其他所有异常
|
||||
log.info("An error occurred: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.muyu.vehiclegateway.config;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @ClassName ConnectWeight
|
||||
* @Description 连接权重配置类
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/10/8 19:21
|
||||
*/
|
||||
@Log4j2
|
||||
@Component
|
||||
public class ConnectWeight {
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package com.muyu.vehiclegateway.config;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Exchange;
|
||||
import org.springframework.amqp.core.ExchangeBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@Log4j2
|
||||
@Configuration
|
||||
public class RabbitmqConfig {
|
||||
// 日志
|
||||
private static final Logger logger = LoggerFactory.getLogger(RabbitmqConfig.class);
|
||||
|
||||
/**
|
||||
* 车辆上线队列
|
||||
*/
|
||||
public static final String GO_ONLINE_QUEUE = "GO_ONLINE";
|
||||
|
||||
/**
|
||||
* 车辆下线队列
|
||||
*/
|
||||
public static final String GO_OFFLINE_QUEUE = "GO_OFFLINE";
|
||||
|
||||
/**
|
||||
* 协议解析队列
|
||||
*/
|
||||
public static final String SEND_OFFLINE_QUEUE = "GO_LINE";
|
||||
|
||||
|
||||
/**
|
||||
* 车辆上线交换机
|
||||
*/
|
||||
public static final String ONLINE_EXCHANGE = "ONLINE_EXCHANGE";
|
||||
|
||||
/**
|
||||
* 车辆下线交换机
|
||||
*/
|
||||
public static final String OFFLINE_EXCHANGE = "OFFLINE_EXCHANGE";
|
||||
|
||||
/**
|
||||
* 协议解析交换机
|
||||
*/
|
||||
public static final String LINE_EXCHANGE = "LINE_EXCHANGE";
|
||||
|
||||
/**
|
||||
* 车辆下线路由key
|
||||
*/
|
||||
public static final String GO_OFFLINE_ROUTING_KEY = "GO_OFFLINE";
|
||||
/**
|
||||
* 车辆上线路由key
|
||||
*/
|
||||
public static final String GO_ONLINE_ROUTING_KEY = "GO_ONLINE";
|
||||
/**
|
||||
* 协议解析路由key
|
||||
*/
|
||||
public static final String SEND_ONLINE_ROUTING_KEY = "GO_LINE";
|
||||
|
||||
/**
|
||||
* 声明交换机,做持久化
|
||||
*/
|
||||
@Bean(ONLINE_EXCHANGE)
|
||||
public Exchange exchangeTopicsInformON() {
|
||||
try {
|
||||
Exchange exchange = ExchangeBuilder.topicExchange(ONLINE_EXCHANGE).durable(true).build();
|
||||
log.info("创建的交换机为: {}", ONLINE_EXCHANGE);
|
||||
return exchange;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 交换机失败", ONLINE_EXCHANGE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明交换机,做持久化
|
||||
*/
|
||||
@Bean(OFFLINE_EXCHANGE)
|
||||
public Exchange exchangeTopicsInformOFF() {
|
||||
try {
|
||||
Exchange exchange = ExchangeBuilder.topicExchange(OFFLINE_EXCHANGE).durable(true).build();
|
||||
log.info("创建的交换机为: {}", OFFLINE_EXCHANGE);
|
||||
return exchange;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 交换机失败", OFFLINE_EXCHANGE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明交换机,做持久化
|
||||
*/
|
||||
@Bean(LINE_EXCHANGE)
|
||||
public Exchange exchangeTopicsInform() {
|
||||
try {
|
||||
Exchange exchange = ExchangeBuilder.topicExchange(LINE_EXCHANGE).durable(true).build();
|
||||
log.info("创建的交换机为: {}", LINE_EXCHANGE);
|
||||
return exchange;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 交换机失败", LINE_EXCHANGE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 声明QUEUE_INFORM_EMAIL队列
|
||||
@Bean(GO_OFFLINE_QUEUE)
|
||||
public Queue queueInformEmail() {
|
||||
try {
|
||||
Queue queue = new Queue(GO_OFFLINE_QUEUE);
|
||||
log.info("创建的队列为: {}", GO_OFFLINE_QUEUE);
|
||||
return queue;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 队列失败", GO_OFFLINE_QUEUE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 声明QUEUE_INFORM_SMS队列
|
||||
@Bean(GO_ONLINE_QUEUE)
|
||||
public Queue queueInformSms() {
|
||||
try {
|
||||
Queue queue = new Queue(GO_ONLINE_QUEUE);
|
||||
log.info("创建的队列为: {}", GO_ONLINE_QUEUE);
|
||||
return queue;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 队列失败", GO_ONLINE_QUEUE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 声明QUEUE_INFORM_SMS队列
|
||||
@Bean(SEND_OFFLINE_QUEUE)
|
||||
public Queue queueInformSend() {
|
||||
try {
|
||||
Queue queue = new Queue(SEND_OFFLINE_QUEUE);
|
||||
log.info("创建的队列为: {}", SEND_OFFLINE_QUEUE);
|
||||
return queue;
|
||||
} catch (Exception e) {
|
||||
log.error("创建该: {} 队列失败", SEND_OFFLINE_QUEUE, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
//ROUTINGKEY_EMAIL队列绑定交换机,指定routingKey
|
||||
@Bean
|
||||
public Binding bindingQueueInformEmail(@Qualifier(GO_OFFLINE_QUEUE) Queue queue,
|
||||
@Qualifier(OFFLINE_EXCHANGE) Exchange exchange){
|
||||
return BindingBuilder.bind(queue).to(exchange).with(GO_OFFLINE_ROUTING_KEY).noargs();
|
||||
}
|
||||
//ROUTINGKEY_SMS队列绑定交换机,指定routingKey
|
||||
@Bean
|
||||
public Binding bindingRoutingKeySms(@Qualifier(GO_ONLINE_QUEUE) Queue queue,
|
||||
@Qualifier(ONLINE_EXCHANGE) Exchange exchange){
|
||||
return BindingBuilder.bind(queue).to(exchange).with(GO_ONLINE_ROUTING_KEY).noargs();
|
||||
}
|
||||
|
||||
//ROUTINGKEY_SMS队列绑定交换机,指定routingKey
|
||||
@Bean
|
||||
public Binding bindingRoutingKeySend(@Qualifier(SEND_OFFLINE_QUEUE) Queue queue,
|
||||
@Qualifier(LINE_EXCHANGE) Exchange exchange){
|
||||
return BindingBuilder.bind(queue).to(exchange).with(SEND_ONLINE_ROUTING_KEY).noargs();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.muyu.vehiclegateway.controller;
|
||||
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.vehiclegateway.domain.MqttServerModel;
|
||||
import com.muyu.vehiclegateway.domain.req.VehicleConnectionReq;
|
||||
import com.muyu.vehiclegateway.service.ConnectService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
@ -27,15 +28,15 @@ public class ConnectController {
|
|||
private ConnectService connectService;
|
||||
|
||||
/**
|
||||
* 获取车辆信息
|
||||
* 车辆连接
|
||||
* @param vehicleConnectionReq
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/receiveMsg/connect")
|
||||
private Result receiveMsg(@RequestBody VehicleConnectionReq vehicleConnectionReq){
|
||||
private Result<MqttServerModel> receiveMsg(@RequestBody VehicleConnectionReq vehicleConnectionReq) throws Exception {
|
||||
log.info("=======>" + vehicleConnectionReq);
|
||||
connectService.receiveMsg(vehicleConnectionReq);
|
||||
return Result.success();
|
||||
Result<MqttServerModel> mqttServerModelResult = connectService.receiveMsg(vehicleConnectionReq);
|
||||
return mqttServerModelResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.muyu.vehiclegateway.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @ClassName ConnectWegight
|
||||
* @Description 描述
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/10/6 16:27
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Tag(name = "连接权重")
|
||||
public class ConnectWeight {
|
||||
|
||||
/**
|
||||
* 服务器ip
|
||||
*/
|
||||
private String carServerIp;
|
||||
|
||||
/**
|
||||
* 权重值
|
||||
*/
|
||||
private String weightValue;
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.muyu.vehiclegateway.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @ClassName MqttProperties
|
||||
* @Description 描述
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/10/10 20:04
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class MqttProperties {
|
||||
|
||||
/**
|
||||
* MQTT服务节点
|
||||
*/
|
||||
private String broker;
|
||||
|
||||
/**
|
||||
* MQTT订阅主题
|
||||
*/
|
||||
private String topic;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 上报级别
|
||||
*/
|
||||
private int qos = 0;
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.muyu.vehiclegateway.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @ClassName VinIp
|
||||
* @Description 描述
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/10/6 20:33
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class VinIp {
|
||||
|
||||
private String vin;
|
||||
|
||||
private String ip;
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ public class VehicleConnectionReq {
|
|||
private String vehicleVin;
|
||||
|
||||
/**
|
||||
*时间戳
|
||||
* 时间戳
|
||||
*/
|
||||
private String timestamp;
|
||||
|
||||
|
@ -41,4 +41,17 @@ public class VehicleConnectionReq {
|
|||
*/
|
||||
private String nonce;
|
||||
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@JSONField(name = "username")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@JSONField(name = "password")
|
||||
private String password;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,16 +9,23 @@ import com.aliyun.ecs20140526.models.DescribeInstancesResponseBody;
|
|||
import com.aliyun.tea.*;
|
||||
import com.aliyun.teautil.Common;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.vehiclegateway.aliyun.CreateClient;
|
||||
import com.muyu.vehiclegateway.service.ConnectService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName GenerateInstance
|
||||
* @Description 描述
|
||||
* @Description 停止程序时删除ECS服务器实例
|
||||
* @Author YiBo.Liu
|
||||
* @Date 2024/9/28 19:39
|
||||
*/
|
||||
|
@ -27,6 +34,7 @@ import java.util.ArrayList;
|
|||
@Tag(name = "停止程序时删除ECS服务器实例")
|
||||
public class DelInstance implements DisposableBean {
|
||||
|
||||
|
||||
public static void delInstance() throws Exception {
|
||||
|
||||
// 创建ECS客户端对象,用于后续调用ECS相关API
|
||||
|
@ -75,9 +83,9 @@ public class DelInstance implements DisposableBean {
|
|||
} catch (TeaException error) {
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
System.out.println(error.getMessage());
|
||||
log.info(error.getMessage());
|
||||
// 诊断地址
|
||||
System.out.println(error.getData().get("Recommend"));
|
||||
log.info(error.getData().get("Recommend"));
|
||||
Common.assertAsString(error.message);
|
||||
} catch (Exception _error) {
|
||||
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||
|
@ -91,12 +99,12 @@ public class DelInstance implements DisposableBean {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
System.out.println("删除中=======================");
|
||||
delInstance();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
package com.muyu.vehiclegateway.instance;
|
||||
|
||||
import com.aliyun.ecs20140526.Client;
|
||||
import com.aliyun.ecs20140526.models.DescribeInstancesRequest;
|
||||
import com.aliyun.ecs20140526.models.DescribeInstancesResponse;
|
||||
import com.aliyun.ecs20140526.models.DescribeInstancesResponseBody;
|
||||
import com.aliyun.ecs20140526.models.RunInstancesRequest;
|
||||
import com.aliyun.ecs20140526.models.*;
|
||||
import com.aliyun.tea.TeaException;
|
||||
import com.aliyun.teautil.Common;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.vehiclegateway.aliyun.CreateClient;
|
||||
import com.muyu.vehiclegateway.domain.Instance;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -35,18 +34,27 @@ public class GenerateInstance implements ApplicationRunner {
|
|||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* 启动自动创建实例
|
||||
* @throws Exception
|
||||
*/
|
||||
public List<Instance> generateInstance() throws Exception {
|
||||
public void generateInstance() throws Exception {
|
||||
|
||||
// 创建ECS客户端对象,用于后续调用ECS相关API
|
||||
redisService.deleteObject("vinIp");
|
||||
redisService.deleteObject("ipList");
|
||||
redisService.deleteObject("instanceList");
|
||||
redisService.deleteObject("count");
|
||||
|
||||
// 创建阿里云ECS客户端
|
||||
Client client = CreateClient.createClient();
|
||||
|
||||
// 配置系统盘参数
|
||||
RunInstancesRequest.RunInstancesRequestSystemDisk systemDisk = new RunInstancesRequest.RunInstancesRequestSystemDisk()
|
||||
.setSize("40")
|
||||
.setCategory("cloud_essd");
|
||||
.setSize("40") // 设置系统盘大小为40GB
|
||||
.setCategory("cloud_essd"); // 设置系统盘类型为cloud_essd
|
||||
// 创建创建实例请求对象并设置参数
|
||||
RunInstancesRequest runInstancesRequest = new RunInstancesRequest()
|
||||
// 设置地域ID
|
||||
.setRegionId("cn-shanghai")
|
||||
|
@ -71,68 +79,104 @@ public class GenerateInstance implements ApplicationRunner {
|
|||
// 设置实例密码
|
||||
.setPassword("Six@211206")
|
||||
// 设置创建实例的数量
|
||||
.setAmount(1);
|
||||
.setAmount(2);
|
||||
|
||||
|
||||
|
||||
// 创建运行时选项对象
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
|
||||
// 尝试执行创建实例请求
|
||||
try {
|
||||
// 复制代码运行请自行打印 API 的返回值
|
||||
client.runInstancesWithOptions(runInstancesRequest, runtime);
|
||||
|
||||
RunInstancesResponse runInstancesResponse = client.runInstancesWithOptions(runInstancesRequest, runtime);
|
||||
RunInstancesResponseBody body = runInstancesResponse.getBody();
|
||||
RunInstancesResponseBody.RunInstancesResponseBodyInstanceIdSets instanceIdSets = body.getInstanceIdSets();
|
||||
|
||||
List<Instance> instanceList = new ArrayList<>();
|
||||
List<String> instanceIps = new ArrayList<>();
|
||||
|
||||
|
||||
Thread.sleep(20000);
|
||||
|
||||
DescribeInstancesResponse describeInstancesResponse = queryInstanceDetails(client);
|
||||
// 获取实例的ID、公网IP和状态
|
||||
|
||||
List<DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance> bodyInstancesInstances = describeInstancesResponse.getBody().getInstances().getInstance();
|
||||
for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance instance : bodyInstancesInstances) {
|
||||
String id = instance.getInstanceId();
|
||||
String ipAddress = instance.getPublicIpAddress().getIpAddress().get(0);
|
||||
String status = instance.getStatus();
|
||||
Instance instance1 = new Instance(id,ipAddress,status);
|
||||
instanceList.add(instance1);
|
||||
instanceIps.add(ipAddress);
|
||||
|
||||
log.info("实例id为:{}",instance.getInstanceId());
|
||||
log.info("实例ip为:{}",instance.getPublicIpAddress().ipAddress.get(0));
|
||||
log.info("实例状态为{}",instance.getStatus());
|
||||
|
||||
}
|
||||
|
||||
log.info("====>创建的实例集合:"+instanceList);
|
||||
//把实例信息存入redis
|
||||
redisService.setCacheList("instanceList",instanceList);
|
||||
//实例ip存入redis方便做轮询
|
||||
redisService.setCacheList("ipList",instanceIps);
|
||||
|
||||
|
||||
} catch (TeaException error) {
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
System.out.println(error.getMessage());
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
System.out.println(error.getData().get("Recommend"));
|
||||
log.error(error.getData().get("Recommend"));
|
||||
Common.assertAsString(error.message);
|
||||
} catch (Exception _error) {
|
||||
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
System.out.println(error.getMessage());
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
System.out.println(error.getData().get("Recommend"));
|
||||
log.error(error.getData().get("Recommend"));
|
||||
Common.assertAsString(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static DescribeInstancesResponse queryInstanceDetails(Client client) {
|
||||
|
||||
// 创建查询实例请求对象并设置参数
|
||||
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
|
||||
.setRegionId("cn-shanghai");
|
||||
.setInstanceName("server-mqtt")
|
||||
.setRegionId("cn-shanghai"); // 设置地域ID // 设置实例ID
|
||||
// 创建运行时选项对象
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
|
||||
//创建运行时选择对象,用于配置运行时的选项参数
|
||||
RuntimeOptions runtimeOptions = new RuntimeOptions();
|
||||
|
||||
//获取实例列表
|
||||
DescribeInstancesResponse describeInstancesResponse = client.describeInstancesWithOptions(describeInstancesRequest, runtimeOptions);
|
||||
|
||||
//提取实例ID集合
|
||||
List<Instance> list = new ArrayList<>();
|
||||
|
||||
DescribeInstancesResponseBody body = describeInstancesResponse.getBody();
|
||||
|
||||
for (DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance instance : body.getInstances().getInstance()) {
|
||||
|
||||
log.info("实例id为:"+instance.getInstanceId());
|
||||
log.info("实例ip为:"+instance.getPublicIpAddress().ipAddress.get(0));
|
||||
log.info("实例状态为:"+instance.getStatus());
|
||||
|
||||
Instance instance1 = new Instance(instance.getInstanceId(), instance.getPublicIpAddress().ipAddress.get(0), instance.getStatus());
|
||||
|
||||
list.add(instance1);
|
||||
// 尝试执行查询实例请求
|
||||
try {
|
||||
return client.describeInstancesWithOptions(describeInstancesRequest, runtime);
|
||||
} catch (TeaException e) {
|
||||
// 捕获特定的TeaException并打印详细信息
|
||||
log.info("TeaException occurred: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
// 捕获其他所有异常
|
||||
log.info("An error occurred: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Thread.sleep(20000);
|
||||
// redisService.setCacheList("aaa",list);
|
||||
|
||||
return list;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
log.info("===============>开始执行创建实例方法");
|
||||
generateInstance();
|
||||
System.out.println("创建实例成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,13 @@ package com.muyu.vehiclegateway.mapper;
|
|||
import com.muyu.vehiclegateway.domain.req.VehicleConnectionReq;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ConnectMapper {
|
||||
void addVehicle(VehicleConnectionReq vehicleConnectionReq);
|
||||
|
||||
List<String> selectVehicleVin(String vehicleVin);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package com.muyu.vehiclegateway.service;
|
||||
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.vehiclegateway.domain.MqttServerModel;
|
||||
import com.muyu.vehiclegateway.domain.req.VehicleConnectionReq;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ConnectService {
|
||||
|
||||
/**
|
||||
|
@ -11,11 +15,12 @@ public interface ConnectService {
|
|||
void createConnect() throws Exception;
|
||||
|
||||
/**
|
||||
* 获取车辆信息
|
||||
* 车辆连接
|
||||
* @param vehicleConnectionReq
|
||||
* @return
|
||||
*/
|
||||
void receiveMsg(VehicleConnectionReq vehicleConnectionReq);
|
||||
Result<MqttServerModel> receiveMsg(VehicleConnectionReq vehicleConnectionReq) throws Exception;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
package com.muyu.vehiclegateway.service.impl;
|
||||
|
||||
import cn.hutool.json.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.muyu.common.core.constant.GenConstants;
|
||||
import com.muyu.common.core.utils.uuid.UUID;
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.vehiclegateway.config.ClientConfig;
|
||||
import com.muyu.vehiclegateway.domain.MqttProperties;
|
||||
import com.muyu.vehiclegateway.domain.MqttServerModel;
|
||||
import com.muyu.vehiclegateway.domain.VinIp;
|
||||
import com.muyu.vehiclegateway.domain.req.VehicleConnectionReq;
|
||||
import com.muyu.vehiclegateway.instance.GenerateInstance;
|
||||
import com.muyu.vehiclegateway.mapper.ConnectMapper;
|
||||
import com.muyu.vehiclegateway.service.ConnectService;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static io.lettuce.core.pubsub.PubSubOutput.Type.message;
|
||||
import java.util.List;
|
||||
|
||||
import static com.muyu.vehiclegateway.config.RabbitmqConfig.LINE_EXCHANGE;
|
||||
import static com.muyu.vehiclegateway.config.RabbitmqConfig.SEND_ONLINE_ROUTING_KEY;
|
||||
|
||||
|
||||
/**
|
||||
* @ClassName ConnectServiceImpl
|
||||
|
@ -22,15 +29,24 @@ import static io.lettuce.core.pubsub.PubSubOutput.Type.message;
|
|||
* @Date 2024/10/2 16:25
|
||||
*/
|
||||
@Service
|
||||
@Log4j2
|
||||
public class ConnectServiceImpl implements ConnectService {
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ConnectMapper connectMapper;
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private ClientConfig clientConfig;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 创建实例
|
||||
* @throws Exception
|
||||
|
@ -47,11 +63,128 @@ public class ConnectServiceImpl implements ConnectService {
|
|||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void receiveMsg(VehicleConnectionReq vehicleConnectionReq) {
|
||||
rabbitTemplate.convertAndSend("GO_OFFLINE", vehicleConnectionReq.getVehicleVin(),message1 -> {
|
||||
message1.getMessageProperties().setMessageId(UUID.fastUUID().toString());
|
||||
return message1;
|
||||
});
|
||||
public Result<MqttServerModel> receiveMsg(VehicleConnectionReq vehicleConnectionReq) throws Exception {
|
||||
|
||||
log.info("车辆连接请求:{}", vehicleConnectionReq);
|
||||
|
||||
//生成密码
|
||||
vehicleConnectionReq.setPassword(vehicleConnectionReq.getVehicleVin() + vehicleConnectionReq.getTimestamp()
|
||||
+ vehicleConnectionReq.getNonce());
|
||||
|
||||
//查询有没有这辆车的vin码
|
||||
List<String> selectVehicle = connectMapper.selectVehicleVin(vehicleConnectionReq.getVehicleVin());
|
||||
|
||||
if(selectVehicle.isEmpty()){
|
||||
connectMapper.addVehicle(vehicleConnectionReq);
|
||||
log.info("车辆预上线成功");
|
||||
}else {
|
||||
log.info("车辆无法重复预上线");
|
||||
}
|
||||
|
||||
//先判断vin码
|
||||
// String vin = redisTemplate.opsForValue().get(vehicleConnectionReq.getVehicleVin());
|
||||
if(redisTemplate.hasKey(vehicleConnectionReq.getVehicleVin())){
|
||||
log.info("车辆绑定ip失败,已经存在");
|
||||
throw new RuntimeException("车辆已经绑定过了");
|
||||
}
|
||||
|
||||
//判断redis有没有count键
|
||||
if(redisTemplate.hasKey("count")){
|
||||
//取出count
|
||||
Integer count = Integer.valueOf(redisTemplate.opsForValue().get("count"));
|
||||
if(count == 1){
|
||||
redisTemplate.opsForValue().set("count",String.valueOf(0));
|
||||
}else {
|
||||
redisTemplate.opsForValue().set("count",String.valueOf(count+1));
|
||||
}
|
||||
//根据游标count获取服务IP
|
||||
Object ip = redisService.redisTemplate.opsForList().index("ipList", count);
|
||||
//关联车辆和服务
|
||||
this.insertVinIp(new VinIp(vehicleConnectionReq.getVehicleVin(),ip.toString()));
|
||||
//响应信息
|
||||
log.info("车辆:"+vehicleConnectionReq.getVehicleVin()+"成功绑定到:"+ip);
|
||||
|
||||
//发送mq到协议解析
|
||||
MqttProperties mqttProperties = new MqttProperties();
|
||||
mqttProperties.setBroker("tcp://" + ip + ":1883");
|
||||
mqttProperties.setTopic("vehicle");
|
||||
mqttProperties.setUsername(vehicleConnectionReq.getUsername());
|
||||
mqttProperties.setPassword(vehicleConnectionReq.getPassword());
|
||||
mqttProperties.setClientId(ip.toString());
|
||||
mqttProperties.setQos(0);
|
||||
|
||||
rabbitTemplate.convertAndSend(LINE_EXCHANGE,SEND_ONLINE_ROUTING_KEY,mqttProperties);
|
||||
|
||||
return Result.success(new MqttServerModel("tcp://"+ip+":1883","vehicle"));
|
||||
}else {
|
||||
redisTemplate.opsForValue().set("count",String.valueOf(0));
|
||||
//根据游标count获取服务器Ip
|
||||
Object ip = redisService.redisTemplate.opsForList().index("ipList", 0);
|
||||
log.info("ip为:{}",ip);
|
||||
//关联车辆和服务
|
||||
this.insertVinIp(new VinIp(vehicleConnectionReq.getVehicleVin(),ip.toString()));
|
||||
//响应信息
|
||||
log.info("车辆:{}",vehicleConnectionReq.getVehicleVin(),"成功绑定到:{}",ip);
|
||||
return Result.success(new MqttServerModel("tcp://"+ip+":1883","vehicle"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加车辆绑定ip 存入redis
|
||||
*/
|
||||
private void insertVinIp(VinIp vinIp){
|
||||
if (vinIp == null || vinIp.getVin() == null || vinIp.getVin().isEmpty() || vinIp.getIp() == null || vinIp.getIp().isEmpty()) {
|
||||
throw new IllegalArgumentException("vin 或 ip 不能为空或无效");
|
||||
}
|
||||
|
||||
redisTemplate.opsForValue().set(vinIp.getVin(),vinIp.getIp());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加服务器
|
||||
*/
|
||||
private String addNewServer() throws Exception {
|
||||
// 这里调用你已经写好的方法来新增服务器
|
||||
// 假设新增服务器的方法是addServer,返回新服务器的IP
|
||||
String newIp = clientConfig.addServer();
|
||||
// 将新服务器的IP添加到ipList中
|
||||
redisTemplate.opsForList().rightPush("ipList", newIp);
|
||||
// 将新服务器的绑定数量初始化为0
|
||||
redisTemplate.opsForHash().put("serverBindings", newIp, 0);
|
||||
return newIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务器是否已满
|
||||
*/
|
||||
private boolean isServerFull(String ip) {
|
||||
Integer bindingCount = (Integer)redisTemplate.opsForHash().get("serverBindings", ip);
|
||||
return bindingCount >= 6;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 寻找下一个未满的服务器
|
||||
*/
|
||||
private String findAvailableServer() {
|
||||
List<String> ipList = redisTemplate.opsForList().range("ipList", 0, -1);
|
||||
for (String ip : ipList) {
|
||||
if (!isServerFull(ip)) {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
return null; // 如果所有服务器都满了,返回null
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新服务器绑定数量
|
||||
*/
|
||||
private void incrementServerBindingCount(String ip) {
|
||||
// 获取当前服务器的绑定数量
|
||||
Integer bindingCount = (Integer)redisTemplate.opsForHash().get("serverBindings", ip);
|
||||
// 更新绑定数量
|
||||
redisTemplate.opsForHash().put("serverBindings", ip, bindingCount + 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
<mapper namespace="com.muyu.vehiclegateway.mapper.ConnectMapper">
|
||||
|
||||
<insert id="addVehicle">
|
||||
insert into connect(id,vehicle_vin,timestamp,nonce)
|
||||
values (#{id},#{vehicleVin},#{timestamp},#{nonce})
|
||||
insert into connect(id,vehicle_vin,username,password)
|
||||
values (#{id},#{vehicleVin},#{username},#{password})
|
||||
</insert>
|
||||
|
||||
<select id="selectVehicleVin" resultType="java.lang.String">
|
||||
select connect.vehicle_vin from connect
|
||||
where connect.vehicle_vin = #{vehicleVin}
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
Loading…
Reference in New Issue