注册使用
按需付费,其他可调整,点击立即创建即可。
查看接入地址
开发过程
- 创建产品
2. 添加服务
- 新增属性
- 设备绑定到产品上
设备影子,查看数据
模拟数据上报
拉取华为开源工具SDK
修改图片下的类
- 修改接入信息ssl后面的
- 修改设备ID和密钥
- 修改属性和物理模型id
贴一下示例
public static void main(String[] args) throws InterruptedException, IOException {
// 加载iot平台的ca证书,进行服务端校验
File tmpCAFile = new File(IOT_ROOT_CA_TMP_PATH);
try (InputStream resource = CommandSample.class.getClassLoader().getResourceAsStream(IOT_ROOT_CA_RES_PATH)) {
Files.copy(resource, tmpCAFile.toPath(), REPLACE_EXISTING);
}
// 创建设备并初始化. 用户请替换为自己的接入地址。
IoTDevice device = new IoTDevice("ssl://自己的.com:8883",
"69ccb5c509b482_watch01", "75fdd81437aca8391", tmpCAFile);
if (device.init() != 0) {
return;
}
// 接收平台下发的属性读写
// device.getClient().setPropertyListener(new PropertyListener() {
// // 处理写属性
// @Override
// public void onPropertiesSet(String requestId, List<ServiceProperty> services) {
// // 遍历service
// for (ServiceProperty serviceProperty : services) {
// log.info("OnPropertiesSet, serviceId is {}", serviceProperty.getServiceId());
// // 遍历属性
// for (String name : serviceProperty.getProperties().keySet()) {
// log.info("property name is {}", name);
// log.info("set property value is {}", serviceProperty.getProperties().get(name));
// }
// }
// // 修改本地的属性值
// device.getClient().respondPropsSet(requestId, IotResult.SUCCESS);
// }
//
// /**
// * 处理读属性。多数场景下,用户可以直接从平台读设备影子,此接口不用实现。
// * 但如果需要支持从设备实时读属性,则需要实现此接口。
// */
// @Override
// public void onPropertiesGet(String requestId, String serviceId) {
// log.info("OnPropertiesGet, the serviceId is {}", serviceId);
// Map<String, Object> json = new HashMap<>();
// Random rand = new SecureRandom();
// json.put("alarm", 1);
// json.put("temperature", rand.nextFloat() * 100.0f);
// json.put("humidity", rand.nextFloat() * 100.0f);
// json.put("smokeConcentration", rand.nextFloat() * 100.0f);
//
// ServiceProperty serviceProperty = new ServiceProperty();
// serviceProperty.setProperties(json);
// serviceProperty.setServiceId("smokeDetector");
//
// device.getClient().respondPropsGet(requestId, Arrays.asList(serviceProperty));
// }
// });
// 定时上报属性
while (true) {
Map<String, Object> json = new HashMap<>();
Random rand = new SecureRandom();
// 按照物模型设置属性
json.put("BodyTemp", 36.5f);
json.put("xueyang", rand.nextFloat() * 100.0f);
json.put("HeartRate", rand.nextFloat() * 100.0f);
json.put("BatteryPercentage", rand.nextFloat() * 100.0f);
ServiceProperty serviceProperty = new ServiceProperty();
serviceProperty.setProperties(json);
serviceProperty.setServiceId("watch-services"); // serviceId要和物模型一致
device.getClient().reportProperties(Arrays.asList(serviceProperty), new ActionListener() {
@Override
public void onSuccess(Object context) {
log.info("pubMessage success");
}
@Override
public void onFailure(Object context, Throwable var2) {
log.error("reportProperties failed" + var2.toString());
}
});
Thread.sleep(10000);
}
}
集成SpringBoot
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-core</artifactId>
<version>3.1.92</version>
</dependency>
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-iotda</artifactId>
<version>3.1.92</version>
</dependency>
登录华为云,点击我的,点击我的凭证
点击访问密钥,点击新增
复制下载文件的,Access Key Id 和 Secret Access Key(配置文件中的ak和sk)
复制 HTTP 443 的地址 到配置文件的endpoint字段
去我的api 凭证里面复制ID到配置文件的 projectId 字段
配置 AMAP 字段
host
获取凭证
accessKey 和 accessCode
配置yml文件
huaweicloud:
ak: HPUA1VDUYFE9TQNPSWHG
sk: VbDCnhyvLYw9xzuCDlAmACJOx9cBIpXk4bcSbcGv
#如果是上海一,请填写"cn-east-3";如果是北京四,请填写"cn-north-4";
regionId: cn-east-3
endpoint: ce251462e1.st1.iotda-app.cn-east-3.myhuaweicloud.com
projectId: 9dda13117bf54ad2962e7017fbb04c73
#amqp相关配置 下一章课程接收设备数据使用
host: ce251462e1.st1.iotda-app.cn-east-3.myhuaweicloud.com
accessKey: fMPMPDCg
accessCode: XP8qnXDzYCfCNmRC8rukwHsQfPzAT8P9
queueName: DefaultQueue #默认无需改动
配置类
/**
* @author 31094
*/
@Data
@NoArgsConstructor
@Configuration
@ConfigurationProperties(prefix = "huaweicloud")
public class HuaWeiIotConfigProperties {
/**
* 访问Key
*/
private String ak;
/**
* 访问秘钥
*/
private String sk;
/**
* 区域id
*/
private String regionId;
/**
* 应用侧https接入地址
*/
private String endpoint;
/**
* 项目id
*/
private String projectId;
/**
* 应用侧amqp接入地址
*/
private String host;
/**
* amqp连接端口
*/
private int port = 5671;
/**
* amqp接入凭证键值
*/
private String accessKey;
/**
* amqp接入凭证密钥
*/
private String accessCode;
// 指定单个进程启动的连接数
// 单个连接消费速率有限,请参考使用限制,最大64个连接
// 连接数和消费速率及rebalance相关,建议每500QPS增加一个连接
//可根据实际情况自由调节,目前测试和正式环境资源有限,限制更改为4
private int connectionCount = 4;
/**
* 队列名称
*/
private String queueName;
/**
* 开门命令所属服务id
*/
private String smartDoorServiceId;
/**
* 开门记录属性
*/
private String doorOpenPropertyName;
/**
* 开门命令
*/
private String doorOpenCommandName;
/**
* 设置临时密码命令
*/
private String passwordSetCommandName;
/**
* 仅支持true
*/
private boolean useSsl = true;
/**
* IoTDA仅支持default
*/
private String vhost = "default";
/**
* IoTDA仅支持PLAIN
*/
private String saslMechanisms = "PLAIN";
/**
* true: SDK自动ACK(默认)
* false:收到消息后,需要手动调用message.acknowledge()
*/
private boolean isAutoAcknowledge = true;
/**
* 重连时延(ms)
*/
private long reconnectDelay = 3000L;
/**
* 最大重连时延(ms),随着重连次数增加重连时延逐渐增加
*/
private long maxReconnectDelay = 30 * 1000L;
/**
* 最大重连次数,默认值-1,代表没有限制
*/
private long maxReconnectAttempts = -1;
/**
* 空闲超时,对端在这个时间段内没有发送AMQP帧则会导致连接断开。默认值为30000。单位:毫秒。
*/
private long idleTimeout = 30 * 1000L;
/**
* The values below control how many messages the remote peer can send to the client and be held in a pre-fetch buffer for each consumer instance.
*/
private int queuePrefetch = 1000;
/**
* 扩展参数
*/
private Map<String, String> extendedOptions;
}
配置对象
import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.region.Region;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.kzzyl.framework.config.properties.HuaWeiIotConfigProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 31094
*/
@Configuration
public class IotClientConfig {
@Bean
public IoTDAClient iotClient(HuaWeiIotConfigProperties config) {
// 创建认证
ICredential auth = new BasicCredentials()
.withAk(config.getAk())
.withSk(config.getSk())
// 标准版/企业版需要使用衍生算法,基础版请删除配置"withDerivedPredicate"
.withDerivedPredicate(BasicCredentials.DEFAULT_DERIVED_PREDICATE)
.withProjectId(config.getProjectId());
// 创建IoTDAClient实例并初始化
return IoTDAClient.newBuilder()
.withCredential(auth)
// 标准版/企业版:需自行创建Region对象,基础版:请使用IoTDARegion的region对象,如"withRegion(IoTDARegion.CN_NORTH_4)"
// import com.huaweicloud.sdk.core.region.Region;
.withRegion(new Region(config.getRegionId(), config.getEndpoint()))
// .withRegion(IoTDARegion.CN_NORTH_4)
// 配置是否忽略SSL证书校验, 默认不忽略
// .withHttpConfig(new HttpConfig().withIgnoreSSLVerification(true))
.build();
}
}
具体使用
注册设备
// 从华为IOT创建设备
AddDeviceRequest request = new AddDeviceRequest();
AddDevice body = new AddDevice();
// 自定义密钥
String secret = UUID.randomUUID().toString().replaceAll("-", "");
// 配置信息
body.withProductId(device.getProductKey()).withNodeId(device.getNodeId())
.withDeviceName(device.getDeviceName())
// 设置密钥
.withAuthInfo(new AuthInfo().withSecret(secret));
// 发送请求
AddDeviceResponse response = ioTDAClient.addDevice(request.withBody(body));
// 获取设备ID
String deviceId = response.getDeviceId();
实现
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
@Service
@AllArgsConstructor
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device>
implements IDeviceService {
private final IoTDAClient ioTDAClient;
/**
* 设备注册
*/
@Override
public boolean insertDevice(DeviceDto device) {
Long count = lambdaQuery().eq(Device::getDeviceName, device.getDeviceName()).count();
Assert.isTrue(count == 0, "设备名称重复,请重新输入");
count = lambdaQuery().eq(Device::getNodeId, device.getNodeId()).count();
Assert.isTrue(count == 0, "设备标识码重复,请重新输入");
// 如果是随身设备,物理位置类型设置为-1
if (device.getLocationType() == 0) {
device.setPhysicalLocationType(-1);
}
count = lambdaQuery().eq(Device::getBindingLocation, device.getBindingLocation())
.eq(!ObjectUtils.isEmpty(device.getPhysicalLocationType()),
Device::getPhysicalLocationType, device.getPhysicalLocationType())
.eq(Device::getProductKey, device.getProductKey()).count();
Assert.isTrue(count == 0, "该老人/位置已经绑定了该产品,请重新选择");
// 从华为IOT创建设备
AddDeviceRequest request = new AddDeviceRequest();
AddDevice body = new AddDevice();
String secret = UUID.randomUUID().toString().replaceAll("-", "");
body.withProductId(device.getProductKey()).withNodeId(device.getNodeId())
.withDeviceName(device.getDeviceName())
// 设置密钥
.withAuthInfo(new AuthInfo().withSecret(secret));
AddDeviceResponse response = ioTDAClient.addDevice(request.withBody(body));
// 获取设备ID
String deviceId = response.getDeviceId();
Device device1 = BeanUtil.copyProperties(device, Device.class);
device1.setIotId(deviceId);
device1.setSecret(secret);
Assert.isTrue(save(device1), "设备新增失败");
return true;
}
/**
* 修改设备管理
*/
@Override
public boolean updateDevice(DeviceDto device) {
UpdateDeviceRequest request = new UpdateDeviceRequest().withDeviceId(device.getIotId())
.withBody(new UpdateDevice().withDeviceName(device.getDeviceName()));
// 调用华为云修改设备名称
UpdateDeviceResponse deviceResponse = ioTDAClient.updateDevice(request);
Device device1 = BeanUtil.toBean(device, Device.class);
updateById(device1);
return true;
}
/**
* 删除设备管理
*/
@Override
public void deleteDevice(List<Long> ids) {
// 这里应该是IOT的ID,懒得改了
ids.forEach(id -> {
DeleteDeviceRequest request = new DeleteDeviceRequest().withDeviceId(String.valueOf(id));
ioTDAClient.deleteDevice(request);
});
boolean remove = remove(Wrappers.<Device>lambdaQuery());
if (!remove) {
throw new RuntimeException("Device 删除失败");
}
}
// 获取产品列表
@Override
public void syncProductList() {
ListProductsRequest request = new ListProductsRequest();
request.setLimit(50);
ListProductsResponse response = ioTDAClient.listProducts(request);
// 判断相应是否成功
if (response.getHttpStatusCode() != 200) {
throw new BaseException("从IOT平台同步产品列表失败");
}
// 存储到Redis
RedisUtil.setJSONList(CacheConstants.IOT_ALL_PRODUCT_LIST, response.getProducts());
}
// 查询详情
@Override
public DeviceDetailVo queryDeviceDetail(String id) {
Device device = lambdaQuery().eq(Device::getIotId, id).one();
Assert.notNull(device, "设备不存在");
// 调用华为云查询详情
ShowDeviceResponse response = ioTDAClient
.showDevice(new ShowDeviceRequest().withDeviceId(device.getIotId()));
DeviceDetailVo deviceDetailVo = BeanUtil.copyProperties(device, DeviceDetailVo.class);
deviceDetailVo.setDeviceStatus(response.getStatus());
// 时间转换
LocalDateTime dateTime = CommonUtils.utcToShanghaiTime(response.getActiveTime());
deviceDetailVo.setActiveTime(dateTime);
return deviceDetailVo;
}
// 查询数据
@Override
public List<DeviceDataVo> queryServiceProperties(String iotId) {
ShowDeviceShadowRequest request = new ShowDeviceShadowRequest()
.withDeviceId(iotId);
// 调用华为云查询数据
ShowDeviceShadowResponse response = ioTDAClient.showDeviceShadow(request);
// 获取数据
List<DeviceShadowData> shadow = response.getShadow();
if (CollectionUtils.isEmpty(shadow) || shadow.size() != 1) {
return List.of();
}
DeviceShadowData deviceShadowData = shadow.get(0);
DeviceShadowProperties reported = deviceShadowData.getReported();
String eventTime = reported.getEventTime();
List<DeviceDataVo> dataVos = CommonUtils.mapToObjects(reported.getProperties(),
"functionId", DeviceDataVo.class);
dataVos.forEach(deviceDataVo -> deviceDataVo.setEventTime(eventTime));
return dataVos;
}
}
数据转发
AMQP 华为官网配置数据转发
创建规则
完成
Java Amqt 对接
下载所需要的demo
打开日志
在启动之前的模拟属性上报的程序,此时就可以监听到上报的数据。
[JmsSession [ID:d0ce6fc6-320b-4ec0-b9c3-f70a0e41c1ee:1:1] delivery dispatcher] INFO com.iot.amqp.examples.AbstractAmqpExample - receive a message,content={"resource":"device.property","event":"report","event_time":"20260505T153616Z","event_time_ms":"2026-05-05T15:36:16.414Z","request_id":"feb77a07-3d61-4772-9b3e-146195c8f1cc","notify_data":{"header":{"app_id":"f3d7eeaa7d324ec3899e8814b7dff5d6","device_id":"69ccb55618855b39c509b482_watch04","node_id":"watch04","product_id":"69ccb55618855b39c509b482","gateway_id":"69ccb55618855b39c509b482_watch04"},"body":{"services":[{"service_id":"watch-services","properties":{"BodyTemp":1,"HeartRate":80.98859,"xueyang":4.7724667,"BatteryPercentage":25.743711},"event_time":"20260505T153616Z"}]}}}
[pool-1-thread-1] INFO com.iot.amqp.examples.AmqpClientTest - Receive message speed
Java SDK 对接
<!-- amqp 1.0 qpid client -->
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-jms-client</artifactId>
<version>2.10.0</version>
</dependency>
配置参数和配置类
huaweicloud:
ak: HPUA1LDUY...QNPSWHG
sk: VbDCnhyvLYw9xzu.....9cBIpXk4bcSbcGv
#如果是上海一,请填写"cn-east-3";如果是北京四,请填写"cn-north-4";
regionId: cn-east-3
endpoint: ce2...1e1.st1.iotda-app.cn-east-3.myhuaweicloud.com
projectId: 9dda2....2962e7017fbb04c73
#amqp相关配置 下一章课程接收设备数据使用
host: ce...1e1.st1.iotda-app.cn-east-3.myhuaweicloud.com
accessKey: fMPMP...
accessCode: XP8qnXD...8rukwHsQfPzAT8P9
queueName: DefaultQueue #默认无需改动
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* @author 31094
*/
@Data
@NoArgsConstructor
@Configuration
@ConfigurationProperties(prefix = "huaweicloud")
public class HuaWeiIotConfigProperties {
/**
* 访问Key
*/
private String ak;
/**
* 访问秘钥
*/
private String sk;
/**
* 区域id
*/
private String regionId;
/**
* 应用侧https接入地址
*/
private String endpoint;
/**
* 项目id
*/
private String projectId;
/**
* 应用侧amqp接入地址
*/
private String host;
/**
* amqp连接端口
*/
private int port = 5671;
/**
* amqp接入凭证键值
*/
private String accessKey;
/**
* amqp接入凭证密钥
*/
private String accessCode;
// 指定单个进程启动的连接数
// 单个连接消费速率有限,请参考使用限制,最大64个连接
// 连接数和消费速率及rebalance相关,建议每500QPS增加一个连接
//可根据实际情况自由调节,目前测试和正式环境资源有限,限制更改为4
private int connectionCount = 4;
/**
* 队列名称
*/
private String queueName;
/**
* 开门命令所属服务id
*/
private String smartDoorServiceId;
/**
* 开门记录属性
*/
private String doorOpenPropertyName;
/**
* 开门命令
*/
private String doorOpenCommandName;
/**
* 设置临时密码命令
*/
private String passwordSetCommandName;
/**
* 仅支持true
*/
private boolean useSsl = true;
/**
* IoTDA仅支持default
*/
private String vhost = "default";
/**
* IoTDA仅支持PLAIN
*/
private String saslMechanisms = "PLAIN";
/**
* true: SDK自动ACK(默认)
* false:收到消息后,需要手动调用message.acknowledge()
*/
private boolean isAutoAcknowledge = true;
/**
* 重连时延(ms)
*/
private long reconnectDelay = 3000L;
/**
* 最大重连时延(ms),随着重连次数增加重连时延逐渐增加
*/
private long maxReconnectDelay = 30 * 1000L;
/**
* 最大重连次数,默认值-1,代表没有限制
*/
private long maxReconnectAttempts = -1;
/**
* 空闲超时,对端在这个时间段内没有发送AMQP帧则会导致连接断开。默认值为30000。单位:毫秒。
*/
private long idleTimeout = 30 * 1000L;
/**
* The values below control how many messages the remote peer can send to the client and be held in a pre-fetch buffer for each consumer instance.
*/
private int queuePrefetch = 1000;
/**
* 扩展参数
*/
private Map<String, String> extendedOptions;
}
具体实现
具体关注:processMessage 方法用于接收数据。
import cn.hutool.core.text.CharSequenceUtil;
import com.kzzyl.framework.config.properties.HuaWeiIotConfigProperties;
import jakarta.jms.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.qpid.jms.*;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.apache.qpid.jms.transports.TransportOptions;
import org.apache.qpid.jms.transports.TransportSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author itcast
*/
@Slf4j
@Component
public class AmqpClient implements ApplicationRunner {
@Autowired
private HuaWeiIotConfigProperties huaWeiIotConfigProperties;
// 业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
// 控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
// 建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
private static String clientId;
static {
try {
clientId = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.info("找不到地址");
}
}
@Override
public void run(ApplicationArguments args) throws Exception {
start();
}
public void start() throws Exception {
// 参数说明,请参见AMQP客户端接入说明文档。
for (int i = 0; i < huaWeiIotConfigProperties.getConnectionCount(); i++) {
// 创建amqp连接
Connection connection = getConnection();
// 加入监听者
((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
// 创建会话。
// Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
// Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
// 创建Receiver连接。
MessageConsumer consumer = newConsumer(session, connection, huaWeiIotConfigProperties.getQueueName());
consumer.setMessageListener(messageListener);
}
log.info("amqp is started successfully, and will exit after server shutdown ");
}
/**
* 创建amqp连接
*
* @return amqp连接
*/
private Connection getConnection() throws Exception {
String connectionUrl = generateConnectUrl();
JmsConnectionFactory cf = new JmsConnectionFactory(connectionUrl);
// 信任服务端
TransportOptions to = new TransportOptions();
to.setTrustAll(true);
cf.setSslContext(TransportSupport.createJdkSslContext(to));
String userName = "accessKey=" + huaWeiIotConfigProperties.getAccessKey();
cf.setExtension(JmsConnectionExtensions.USERNAME_OVERRIDE.toString(), (connection, uri) -> {
// IoTDA的userName组成格式如下:“accessKey=${accessKey}|timestamp=${timestamp}”
String newUserName = userName;
if (connection instanceof JmsConnection) {
newUserName = ((JmsConnection) connection).getUsername();
}
return newUserName + "|timestamp=" + System.currentTimeMillis();
});
// 创建连接。
return cf.createConnection(userName, huaWeiIotConfigProperties.getAccessCode());
}
/**
* 生成amqp连接地址
*
* @return amqp连接地址
*/
public String generateConnectUrl() {
String uri = MessageFormat.format("{0}://{1}:{2}",
(huaWeiIotConfigProperties.isUseSsl() ? "amqps" : "amqp"),
huaWeiIotConfigProperties.getHost(),
String.valueOf(huaWeiIotConfigProperties.getPort()));
Map<String, String> uriOptions = new HashMap<>();
uriOptions.put("amqp.vhost", huaWeiIotConfigProperties.getVhost());
uriOptions.put("amqp.idleTimeout", String.valueOf(huaWeiIotConfigProperties.getIdleTimeout()));
uriOptions.put("amqp.saslMechanisms", huaWeiIotConfigProperties.getSaslMechanisms());
Map<String, String> jmsOptions = new HashMap<>();
jmsOptions.put("jms.prefetchPolicy.queuePrefetch", String.valueOf(huaWeiIotConfigProperties.getQueuePrefetch()));
if (CharSequenceUtil.isNotBlank(clientId)) {
jmsOptions.put("jms.clientID", clientId);
} else {
jmsOptions.put("jms.clientID", UUID.randomUUID().toString());
}
jmsOptions.put("failover.reconnectDelay", String.valueOf(huaWeiIotConfigProperties.getReconnectDelay()));
jmsOptions.put("failover.maxReconnectDelay", String.valueOf(huaWeiIotConfigProperties.getMaxReconnectDelay()));
if (huaWeiIotConfigProperties.getMaxReconnectAttempts() > 0) {
jmsOptions.put("failover.maxReconnectAttempts", String.valueOf(huaWeiIotConfigProperties.getMaxReconnectAttempts()));
}
if (huaWeiIotConfigProperties.getExtendedOptions() != null) {
for (Map.Entry<String, String> option : huaWeiIotConfigProperties.getExtendedOptions().entrySet()) {
if (option.getKey().startsWith("amqp.") || option.getKey().startsWith("transport.")) {
uriOptions.put(option.getKey(), option.getValue());
} else {
jmsOptions.put(option.getKey(), option.getValue());
}
}
}
return uriOptions.entrySet().stream()
.map(option -> MessageFormat.format("{0}={1}", option.getKey(), option.getValue()))
.collect(Collectors.joining("&", "failover:(" + uri + "?", ")")) +
jmsOptions.entrySet().stream()
.map(option -> MessageFormat.format("{0}={1}", option.getKey(), option.getValue()))
.collect(Collectors.joining("&", "?", ""));
}
/**
* 创建消费者
*
* @param session session
* @param connection amqp连接
* @param queueName 队列名称
* @return 消费者
*/
public MessageConsumer newConsumer(Session session, Connection connection, String queueName) throws Exception {
if (!(connection instanceof JmsConnection) || ((JmsConnection) connection).isClosed()) {
throw new Exception("create consumer failed,the connection is disconnected.");
}
return session.createConsumer(new JmsQueue(queueName));
}
private final MessageListener messageListener = message -> {
try {
// 异步处理收到的消息,确保onMessage函数里没有耗时逻辑
threadPoolTaskExecutor.submit(() -> processMessage(message));
} catch (Exception e) {
log.error("submit task occurs exception ", e);
}
};
/**
* 在这里处理您收到消息后的具体业务逻辑。
*/
private void processMessage(Message message) {
String contentStr;
try {
contentStr = message.getBody(String.class);
String topic = message.getStringProperty("topic");
String messageId = message.getStringProperty("messageId");
log.info("receive message,\n topic = {},\n messageId = {},\n content = {}", topic, messageId, contentStr);
} catch (JMSException e) {
throw new RuntimeException("服务器错误");
}
}
private final JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
/**
* 连接成功建立。
*/
@Override
public void onConnectionEstablished(URI remoteURI) {
log.info("onConnectionEstablished, remoteUri:{}", remoteURI);
}
/**
* 尝试过最大重试次数之后,最终连接失败。
*/
@Override
public void onConnectionFailure(Throwable error) {
log.error("onConnectionFailure, {}", error.getMessage());
}
/**
* 连接中断。
*/
@Override
public void onConnectionInterrupted(URI remoteURI) {
log.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
}
/**
* 连接中断后又自动重连上。
*/
@Override
public void onConnectionRestored(URI remoteURI) {
log.info("onConnectionRestored, remoteUri:{}", remoteURI);
}
@Override
public void onInboundMessage(JmsInboundMessageDispatch envelope) {
}
@Override
public void onSessionClosed(Session session, Throwable cause) {
}
@Override
public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
}
@Override
public void onProducerClosed(MessageProducer producer, Throwable cause) {
}
};
}
多线程改造
在 framework.config.ThreadPoolConfig 模块下有这么个类
线程池核心参数和原理
- corePoolSize 核心线程数目
- 到底多少合适?
- 对于IO密集型的项目,一般设置核心线程数为:CPU核数 * 2
- 对于计算密集型的项目,一般设置核心线程数为: CPU核数 + 1
- 到底多少合适?
- maximumPoolSize 最大线程数目 = (核心线程+临时线程的最大数目)
- keepAliveTime 生存时间 - 临时线程的生存时间,生存时间内没有新任务,此线程资源会释放
- unit 时间单位 - 临时线程的生存时间单位,如秒、毫秒等
- workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建临时线程执行任务
- threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
- handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
改造,CPU数 * 2
@Configuration
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
SpringBoot3 必须使用@RequestParam 注解标注参数问题
在聚合pom里
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- 解决传参校验报错,需要使用maven打包 -->
<parameters>true</parameters>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>