Java獲取被nginx代理的emqx客戶端真實ip

Java獲取被nginx代理的emqx客戶端真實ip

契機

? 使用nginx作為負載均衡(Load Balancing)的時候,發現真實ip無法獲取。幾經折騰終于拿到真實ip,又發現被代理的端口又無法使用非代理模式連接,由于之前暴露的docker端口有限,從中部分取巧終于撥開云霧見光明,再結合Java客戶端獲取真實ip一氣呵成。

EMQX配置

#docker部署
#18083為管理頁面端口,1883為默認mqtt端口,8883為默認mqtts端口
#如果還沒有新建emqx容器,建議多暴露端口,具體原因見下文
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:5.7.2
  • 進入emqx管理頁面,新建proxy_tcp監聽器,類型為tcp,端口為8883
  • 然后關閉之前ssl-8883監聽
  • 借用端口是因為docker暴露到宿主機的端口不夠了,一旦設置比如proxy_tcp監聽器的8883代理監聽后,客戶端就只能通過nginx代理連接這個8883端口了,直接連接8883就不行了

請添加圖片描述

#獲取emqx的容器id,假如為1111
docker ps | grep emqx #將配置文件拷貝出來
docker cp 1111:/opt/emqx/etc/emqx.conf .#修改,添加下面項目
vim emqx.conf #打開proxy_tcp的代理監聽
listeners.tcp.proxy_tcp {proxy_protocol = true
}#關閉default的代理,默認是關閉的
#但是一旦打開過,就要手動設置為false
listeners.tcp.default {proxy_protocol = false
}#放置回去
docker cp ./emqx.conf 1111:/opt/emqx/etc/#重啟容器
docker restart 1111

此時

  • 直接使用客戶端設備連接8883端口失敗,無論mqtt/mqtts協議
  • 1883端口可以使用mqtt協議可以正常連接
  • emqx不存放證書,ssl認證是一個耗時的操作,丟到nginx去搞

nginx配置

#nginx安裝 - 略#nginx安裝模塊
sudo yum install nginx-mod-stream#修改nginx配置文件
vim /etc/nginx/nginx.conf#配置文件如下
#192.168.0.1為emqx所在服務器,與nginx不在一個服務器
#監聽1883為mqtt
#監聽8883為mqtts
stream {upstream stream_backend {zone tcp_servers 64k;hash $remote_addr;server 192.168.0.1:1883 max_fails=2 fail_timeout=30s;}server {listen 1883;proxy_pass stream_backend;proxy_buffer_size 4k;}upstream stream_backend_ssl {zone tcp_servers 64k;hash $remote_addr;server 192.168.0.1:8883 max_fails=2 fail_timeout=30s;}server {listen 8883 ssl;proxy_protocol on;        proxy_pass stream_backend_ssl;proxy_buffer_size 4k;ssl_handshake_timeout 15s;ssl_certificate     /etc/nginx/cert/_.xx.pem;ssl_certificate_key /etc/nginx/cert/_.xx.key;}}#重啟nginx
sudo systemctl restart nginx

此時

  • 8883為mqtts端口,鏈接mqtts,emqx控制臺可以看到真實ip
  • 1883為mqtt端口,鏈接mqtt,emqx控制臺看不到真實ip
  • 理論上業務只暴露mqtts端口到外網,mqtt為內部調試方便抓包用的

請添加圖片描述

腳本獲取

設置api密鑰,保存下username和pasword

請添加圖片描述

#!/bin/bash# Check if both host and password arguments are provided
if [ $# -lt 2 ]; thenecho "Usage: $0 <host> <password>"exit 1
fi# Define host, username, and password
HOST="$1"
USERNAME="username"
PASSWORD="$2"# Encode username and password in Base64
AUTH=$(echo -n "$USERNAME:$PASSWORD" | base64)# Create the curl command
curl -H "Authorization: Basic $AUTH" "http://$HOST:18083/api/v5/clients?like_username=server"
#運行
chmod +x ./emqx_curl.sh
./emqx_curl.sh localhost your_password_here

Java獲取

service

package com.bothsavage.common.mqtt.service;import com.alibaba.fastjson.JSONObject;
import com.bothsavage.common.mqtt.config.interceptor.EmqxAuthInterceptor;
import com.bothsavage.common.mqtt.entity.dto.ClientInfoDTO;
import com.bothsavage.common.mqtt.entity.dto.EmqxRespDTO;import feign.Headers;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;/*** Emqx請求** @author bothSavage*/
@FeignClient(name = "emqxRmiService", url = "${emqx.rmi.url:http://127.0.0.1:18083/}",configuration = EmqxAuthInterceptor.class)
public interface EmqxRmiService {/*** 獲取客戶端信息** @param clientId 客戶端id* @return -*/@GetMapping(value = "/api/v5/clients/{id}")@Headers(value = {"Content-Type=application/json;charset=UTF-8", "Accept=application/json"})ClientInfoDTO getClientById(@PathVariable("id") String clientId);/*** 查詢客戶端信息** @param likeUserName 客戶端名稱* @return -*/@GetMapping(value = "/api/v5/clients")@Headers(value = {"Content-Type=application/json;charset=UTF-8", "Accept=application/json"})EmqxRespDTO<ClientInfoDTO> getClientsByUserName(@RequestParam("like_username") String likeUserName);}

auth

package com.bothsavage.common.mqtt.config.interceptor;import feign.RequestInterceptor;
import feign.RequestTemplate;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;import java.util.Base64;/*** Emqx請求權限攔截** @author bothSavage*/
@Configuration
public class EmqxAuthInterceptor implements RequestInterceptor {@Value("${emqx.rmi.username:x}")private String username;@Value("${emqx.rmi.password:x}")private String password;@Overridepublic void apply(RequestTemplate template) {String auth = username + ":" + password;String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());template.header("Authorization", "Basic " + encodedAuth);}}

dto

package com.bothsavage.common.mqtt.entity.dto;import com.alibaba.fastjson.annotation.JSONField;import lombok.Data;/*** dto** @author bothSavage*/
@Data
public class ClientInfoDTO {@JSONField(name = "clientid")private String clientid;@JSONField(name = "mqueue_len")private Integer mqueueLen;@JSONField(name = "reductions")private Integer reductions;@JSONField(name = "keepalive")private Integer keepalive;@JSONField(name = "listener")private String listener;@JSONField(name = "proto_ver")private Integer protoVer;@JSONField(name = "recv_msg.dropped.await_pubrel_timeout")private Integer recvMsgDroppedAwaitPubrelTimeout;@JSONField(name = "send_msg.dropped.expired")private Integer sendMsgDroppedExpired;@JSONField(name = "mountpoint")private Object mountpoint;@JSONField(name = "mailbox_len")private Integer mailboxLen;@JSONField(name = "send_msg")private Integer sendMsg;@JSONField(name = "zone")private String zone;@JSONField(name = "subscriptions_cnt")private Integer subscriptionsCnt;@JSONField(name = "heap_size")private Integer heapSize;@JSONField(name = "recv_msg")private Integer recvMsg;@JSONField(name = "recv_cnt")private Integer recvCnt;@JSONField(name = "send_msg.dropped.too_large")private Integer sendMsgDroppedTooLarge;@JSONField(name = "awaiting_rel_cnt")private Integer awaitingRelCnt;@JSONField(name = "subscriptions_max")private String subscriptionsMax;@JSONField(name = "recv_msg.qos0")private Integer recvMsgQos0;@JSONField(name = "recv_msg.qos1")private Integer recvMsgQos1;@JSONField(name = "recv_msg.qos2")private Integer recvMsgQos2;@JSONField(name = "node")private String node;@JSONField(name = "inflight_max")private Integer inflightMax;@JSONField(name = "port")private Integer port;@JSONField(name = "recv_pkt")private Integer recvPkt;@JSONField(name = "send_oct")private Integer sendOct;@JSONField(name = "inflight_cnt")private Integer inflightCnt;@JSONField(name = "awaiting_rel_max")private String awaitingRelMax;@JSONField(name = "is_persistent")private Boolean isPersistent;@JSONField(name = "send_msg.dropped")private Integer sendMsgDropped;@JSONField(name = "recv_msg.dropped")private Integer recvMsgDropped;@JSONField(name = "clean_start")private Boolean cleanStart;@JSONField(name = "send_msg.qos0")private Integer sendMsgQos0;@JSONField(name = "created_at")private String createdAt;@JSONField(name = "connected_at")private String connectedAt;@JSONField(name = "enable_authn")private Boolean enableAuthn;@JSONField(name = "mqueue_dropped")private Integer mqueueDropped;@JSONField(name = "is_bridge")private Boolean isBridge;@JSONField(name = "send_msg.dropped.queue_full")private Integer sendMsgDroppedQueueFull;@JSONField(name = "proto_name")private String protoName;@JSONField(name = "ip_address")private String ipAddress;@JSONField(name = "send_cnt")private Integer sendCnt;@JSONField(name = "connected")private Boolean connected;@JSONField(name = "recv_oct")private Integer recvOct;@JSONField(name = "mqueue_max")private Integer mqueueMax;@JSONField(name = "send_msg.qos2")private Integer sendMsgQos2;@JSONField(name = "send_msg.qos1")private Integer sendMsgQos1;@JSONField(name = "expiry_interval")private Integer expiryInterval;@JSONField(name = "send_pkt")private Integer sendPkt;@JSONField(name = "username")private String username;}

test

clientIp = emqxRmiService.getClientById("clientId").getIpAddress();

總結

  • 路線清晰,只是操作起來稍微麻煩點
  • 注意:emqx打開了代理的訪問的監聽器,只能通過nginx來中轉,無法直接鏈接了
  • 當前只用了一個emqx,沒有做集群
  • docker端口暴露需要前期規劃好,要不然特別麻煩

寫到最后

請添加圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94139.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94139.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94139.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Jenkins自動化部署服務到Kubernetes環境

在現代軟件開發中,持續集成和持續部署(CI/CD)已成為提高開發效率和軟件質量的關鍵實踐。本文將介紹如何使用Jenkins自動化部署服務到Kubernetes環境,并重點介紹Maven與私服的配置。 環境準備 在開始之前,請確保您已準備好以下環境: Jenkins服務器 Kubernetes集群 Docker鏡…

OpenAI重新開源!gpt-oss-20b適配昇騰并上線魔樂社區

2025年8月5日&#xff0c;OpenAI發布了兩款全新的開源權重語言模型&#xff0c;均為混合專家&#xff08;MoE&#xff09;架構&#xff0c;其規模設計可在消費級GPU和云端的多種硬件上高效運行。這些模型采用 Apache 2.0 許可協議&#xff0c;因此可用于蒸餾到其他推理模型中、…

SpringCloud入門(簡潔明了)

目錄 一.創建微服務項目 (一)環境準備 (二)項目結構圖 (三)流程 二. Nacos (一)注冊中心 1.服務注冊 2.服務發現 3.編寫微服務API 4.遠程調用基本實現 5.負載均衡 6.LoadBalanced注解式注解均衡 7.注冊中心宕機&#xff0c;遠程調用還能成功嗎 (二)配置中心 1.基…

集成算法學習筆記

一、集成算法簡介1. 核心思想類比“多個專家綜合判斷優于單個專家”&#xff0c;通過構建并結合多個個體學習器&#xff0c;提升模型的泛化能力&#xff08;降低過擬合風險、提高預測準確性&#xff09;&#xff0c;完成復雜的學習任務。2. 個體學習器與結合模塊個體學習器&…

讓Chrome信任自簽名證書

讓Chrome信任自簽名證書&#xff08;Unix系列OS&#xff09; 背景 想在本地測試自己寫的基于HTTPS連接的Web應用&#xff0c;跑在3001端口。但使用Chrome瀏覽器訪問https://localhost:3001時顯示連接不安全。解決了但沒解決 使用mkcert一鍵創建證書&#xff1a; mkcert localho…

[江科大庫]基于 OpenMV 的矩形識別與 STM32 串口通信(電子設計大賽實用教程)

?? 基于 OpenMV 的矩形識別與 STM32 串口通信(電子設計大賽實用教程) 一、前言 在本科生電子設計大賽中,經常會遇到圖像識別相關的任務,例如: 識別 矩形框(如識別一個 A4 紙、黑色標記框等); 將識別結果傳輸到 STM32 單片機,用于后續控制(舵機、移動小車、機械臂…

人臉識別驅動的工廠人體屬性檢測與預警機制

人體屬性檢測&#xff1a;人臉識別智慧檢測驅動的工廠管理革新&#xff08;所有圖片均為真實項目案例&#xff09;在制造業數字化轉型浪潮中&#xff0c;人體屬性檢測技術已成為破解傳統工廠管理難題的核心工具。通過融合人臉識別智慧檢測、目標檢測算法與多模態數據分析&#…

數據工程師——ETL

ETL面試題01 一、基礎概念與理論類 1. 請解釋什么是 ETL?它在數據處理流程中扮演什么角色? 答:ETL就是數據抽取、轉化、加載。目的是將分散的數據源集中在一起進行處理分析。 數據抽取:是指各種數據源中抽取數據,包括關系型數據庫(MySQL、Oracle等)、日志文件、Exce…

Oracle APEX 經典報表中的Checkbox

目錄 1. 建表&#xff06;投入測試數據 2. 經典報表做成 2-1. 畫面布局如下?編輯 2-2. 報表使用的SQL 2-3. RS列的Heading設定 2-4. Function and Global Variable Declaration 2-5. Execute when Page Loads 2-6. Process 3. 運行效果?編輯 1. 建表&#xff06;投入…

Codeforces Round 1043 (Div.3)

比賽連接&#xff1a;Codeforces Round 1043 (Div.3) A. Homework 題目鏈接&#xff1a;A - Homework Vlad and Dima have been assigned a task in school for their English class. They were given two strings aaa and bbb and asked to append all characters from bbb …

GPS欺騙式干擾的產生

我們在GNSS抗干擾天線的選型、測試方法以及為什么不能做RTK&#xff1f;&#xff08;抗干擾內容全集&#xff09;中提到的抗干擾天線&#xff0c;針對的是GPS壓制式干擾。對于GPS欺騙式干擾&#xff0c;抗干擾天線是無能為力的。 簡單來說&#xff0c;壓制式干擾是通過發射強功…

[PV]AXI R/W/RW帶寬計算的tcl腳本

AXI R/W/RW帶寬計算的tcl腳本 我基于前述的axi_read_bw_per_id.tcl腳本進行了修改,使其支持: 讀通道(Read Channel):計算基于rvalid && rready的有效周期(已在前述實現)。 寫通道(Write Channel):計算基于wvalid && wready的有效周期,考慮wstrb的ac…

阿里云AnalyticDB同步數據至華為云taurusdb

1 概述 AnalyticDB和taurusdb都是高度兼容mysql協議的數據庫&#xff0c;從現有的AnalyticDB官方數據同步方案來看&#xff0c;只有FlinkSQL合適。 同步方案官方文檔&#xff1a; https://help.aliyun.com/zh/analyticdb/analyticdb-for-mysql/user-guide/flink-subscribes-b…

學習嵌入式之驅動——系統移植(二)

一、uboot常用命令與環境變量1.命令&#xff1a;&#xff08;1&#xff09;環境變量操作命令命令功能格式printenv 查看環境變量printenvsetenv新建/修改環境變量setenv 環境變量名 環境變量值saveenv保存環境變量saveenv&#xff08;2&#xff09;內存操作命令命令功能格式示例…

EasyExcel 合并單元格最佳實踐:基于注解的自動合并與樣式控制

EasyExcel 合并單元格最佳實踐&#xff1a;基于注解的自動合并與樣式控制 前言 在日常開發中&#xff0c;我們經常需要導出 Excel 報表&#xff0c;而合并單元格是提升報表可讀性的常見需求。本文將介紹如何基于 EasyExcel 實現智能的單元格合并功能&#xff0c;通過自定義注解…

Unity設置UI顯示區域

系列文章目錄 untiy工具 文章目錄 系列文章目錄 ??前言 ??一、效果圖 ??二、制作過程(檢測中心點位置) ??2-1、代碼實現 ??三、優化為檢測整個UI四個角點 ??四、性能優化建議 ??壁紙分享 ??總結 ??前言 思路: 獲取屏幕的寬度和高度,定義中間區域的范圍…

Qt中用于圖像縮放的核??法QPixmap::scaled

QPixmap::scaled是Qt中用于圖像縮放的核??法&#xff0c;其作?和?法如下&#xff1a;?一、核心作用??圖像尺寸調整?根據指定尺寸對圖像進?等?例或?等?例縮放&#xff0c;?持放?和縮?操作。?保持寬高比?通過AspectRatioMode參數控制是否保持原始圖像的寬??。…

SQL Workbench/J:一款免費開源、跨平臺的通用SQL查詢工具

SQL Workbench/J 是一款基于 Java 開發的免費開源、跨平臺的通用 SQL 查詢工具。 SQL Workbench/J 主要專注于 SQL 腳本開發和數據導入導出功能&#xff0c;不提供各種數據庫管理功能。 功能特性 跨平臺&#xff1a;可以在任何安裝了 Java 運行時環境的操作系統上運行&#xf…

DOLO 上漲:Berachain 生態爆發的前奏?

在 Berachain 生態逐漸進入公眾視野之際&#xff0c;Dolomite&#xff08;簡稱 Dolomite&#xff0c;代幣 DOLO&#xff09;成為鏈上表現最為突出的明星協議。其代幣價格在短短兩個月內&#xff0c;從 $0.03 飆升至 $0.3&#xff0c;漲幅接近 10 倍。市場不僅將其視作 Berachai…

吉利汽車與芯鼎微成立聯合創新實驗室共譜車規級LCoS顯示新篇章

2025年8月20日&#xff0c;吉利汽車研究院技術規劃中心副主任李莉、光學實驗室負責人李金樺博士等一行四人蒞臨芯鼎微&#xff0c;雙方共同為"吉利汽車-芯鼎微聯合創新實驗室"揭牌&#xff0c;標志著兩家企業在車載先進顯示技術領域邁入深度協同創新的新階段。 在這汽…