package com.nb.aliyun; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.nb.aliyun.api.bean.PlatformAccount; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.qpid.jms.JmsConnection; import org.apache.qpid.jms.JmsConnectionListener; import org.apache.qpid.jms.message.JmsInboundMessageDispatch; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Async; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.jms.*; import javax.naming.Context; import javax.naming.InitialContext; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; /** * @Author 龙三郎 * @Date 2021-06-17 10:53:30 * @Version 1.0 * @Description XXX */ @Configuration @EnableConfigurationProperties(PlatformAccount.class) @Slf4j public class AliyunIotSubscribeClient { private final PlatformAccount platformAccount; public AliyunIotSubscribeClient(PlatformAccount platformAccount) { this.platformAccount = platformAccount; this.accessKey = platformAccount.getAccessKey(); this.accessSecret = platformAccount.getAccessSecret(); this.regionId = platformAccount.getRegionId(); this.consumerGroupId = platformAccount.getConsumerGroupId(); this.aliyunUid = platformAccount.getAliyunUid(); this.iotInstanceId = platformAccount.getIotInstanceId(); this.host = aliyunUid+".iot-amqp."+ regionId +".aliyuncs.com"; // 设置客户端id setClientIdAndHost(); } // 设置客户端id private void setClientIdAndHost(){ // 获取ip地址 InetAddress addr = null; try { addr = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } this.clientId = IdWorker.getIdStr(); } private String accessKey; private String accessSecret; private String consumerGroupId; private String aliyunUid; private String regionId; //iotInstanceId:企业版实例请填写实例ID,公共实例请填空字符串""。 private String iotInstanceId; //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。 //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。 private String clientId; //${YourHost}为接入域名,请参见AMQP客户端接入说明文档。 private String host; // 指定单个进程启动的连接数 // 单个连接消费速率有限,请参考使用限制,最大64个连接 // 连接数和消费速率及rebalance相关,建议每500QPS增加一个连接 private static int connectionCount = 1; @Getter private List connections = null; @Async public void start(MessageListener messageListener) throws Exception { // 先关闭一下 close(); connections = new ArrayList<>(); //参数说明,请参见AMQP客户端接入说明文档。 for (int i = 0; i < connectionCount; i++) { long timeStamp = System.currentTimeMillis(); //签名方法:支持hmacmd5、hmacsha1和hmacsha256。 String signMethod = "hmacsha1"; //userName组装方法,请参见AMQP客户端接入说明文档。 String userName = clientId + "-" + i + "|authMode=aksign" + ",signMethod=" + signMethod + ",timestamp=" + timeStamp + ",authId=" + accessKey + ",iotInstanceId=" + iotInstanceId + ",consumerGroupId=" + consumerGroupId + "|"; //计算签名,password组装方法,请参见AMQP客户端接入说明文档。 String signContent = "authId=" + accessKey + "×tamp=" + timeStamp; String password = doSign(signContent, accessSecret, signMethod); String connectionUrl = "failover:(amqps://" + host + ":5671?amqp.idleTimeout=80000)" + "?failover.reconnectDelay=30"; Hashtable hashtable = new Hashtable<>(); hashtable.put("connectionfactory.SBCF", connectionUrl); hashtable.put("queue.QUEUE", "default"); hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory"); Context context = new InitialContext(hashtable); ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF"); Destination queue = (Destination)context.lookup("QUEUE"); // 创建连接。 Connection connection = cf.createConnection(userName, password); connections.add(connection); ((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 = session.createConsumer(queue); consumer.setMessageListener(messageListener); } } public void close() throws Exception { if (connections == null){ return; } connections.forEach(c-> { try { c.close(); } catch (JMSException e) { log.error("连接关闭失败", e); } }); } private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() { /** * 连接成功建立。 */ @Override public void onConnectionEstablished(URI remoteURI) { log.info("连接成功, remoteUri:{}", remoteURI); } /** * 尝试过最大重试次数之后,最终连接失败。 */ @Override public void onConnectionFailure(Throwable error) { log.error("连接失败, {}", error.getMessage()); } /** * 连接中断。 */ @Override public void onConnectionInterrupted(URI remoteURI) { log.info("连接中断, remoteUri:{}", remoteURI); } /** * 连接中断后又自动重连上。 */ @Override public void onConnectionRestored(URI remoteURI) { log.info("自动重连, 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) {} }; /** * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。 */ private static String doSign(String toSignString, String secret, String signMethod) throws Exception { SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod); Mac mac = Mac.getInstance(signMethod); mac.init(signingKey); byte[] rawHmac = mac.doFinal(toSignString.getBytes()); return Base64.encodeBase64String(rawHmac); } }