开始在 Java 中使用中继混合连接 HTTP 请求

在本快速入门中,请创建 Java 发送者和接收者应用程序,用于通过 HTTP 协议发送和接收消息。 这些应用程序使用 Azure 中继的混合连接功能。 若要了解 Azure 中继的常规信息,请参阅 Azure 中继

在本快速入门中,你将执行以下步骤:

  1. 使用 Azure 门户创建中继命名空间。
  2. 使用 Azure 门户在该命名空间中创建混合连接。
  3. 编写服务器(侦听器)控制台应用程序,用于接收消息。
  4. 编写客户端(发送方)控制台应用程序,用于发送消息。
  5. 运行应用程序。

先决条件

  • Java。 确保运行的是 JDK 1.8+

  • Maven。 确保已安装 Maven

  • Azure 中继 SDK。 评审 Java SDK

  • 一个 Azure 订阅。 如果没有 Azure 订阅,请在开始之前创建一个试用版订阅

使用 Azure 门户创建命名空间

  1. 登录 Azure 门户

  2. 在左侧菜单中,选择“所有服务” 。 选择“集成”,搜索“中继”,将鼠标移到“中继”上方,然后选择“创建”。

    Screenshot showing the selection of Relays -> Create button.

  3. 在“创建命名空间”页上,执行以下步骤:

    1. 选择要在其中创建命名空间的 Azure 订阅。

    2. 对于资源组,选择一个要在其中放置命名空间的现有资源组,或创建一个新资源组。

    3. 输入中继命名空间的名称。

    4. 选择应托管该命名空间的区域。

    5. 在页面底部选择查看 + 创建

      Screenshot showing the Create namespace page.

    6. 在“查看 + 创建”页面上,选择“创建”。

    7. 几分钟后,将看到该命名空间的“中继”页面。

      Screenshot showing the home page for Relay namespace.

获取管理凭据

  1. 在“中继”页上,选择左侧菜单的“共享访问策略”。

  2. 在“共享访问策略”页,选择“RootManageSharedAccessKey” 。

  3. 在“SAS 策略:RootManageSharedAccessKey”下,选择“主连接字符串”旁边的“复制”按钮。 此操作会将连接字符串复制到剪贴板,供以后使用。 将此值粘贴到记事本或其他某个临时位置。

  4. 重复上述步骤,将主密钥的值复制和粘贴到临时位置,供以后使用。

    Screenshot showing the connection info for Relay namespace.

使用 Azure 门户创建混合连接

在命名空间的“中继”页上,按照以下步骤创建混合连接。

  1. 请在左侧菜单中的“实体”下选择“混合连接”,然后选择“+ 混合连接”。

    Screenshot showing the Hybrid Connections page.

  2. 在“创建混合连接”页上,输入混合连接的名称,然后选择“创建”。

    Screenshot showing the Create Hybrid Connection page.

创建服务器应用程序(侦听程序)

若要侦听和接收来自中继的消息,请编写 Java 控制台应用程序。

创建 Java 应用程序

如果创建中继时已禁用“需要客户端授权”选项,可使用任何浏览器向混合连接 URL 发送请求。 若要访问受保护的终结点,需在 ServiceBusAuthorization 标头中创建并传递令牌,如下所示。

下面是一个简单的 Maven 项目结构和一个 Java 类,该类演示如何使用 Azure 中继库通过客户端授权将请求发送到混合连接 URL。

添加中继 MVN 包

修改 maven 应用程序包中的 pom.xml 文件,以包括以下内容:

	<dependency>
  	<groupId>com.microsoft.azure</groupId>
  	<artifactId>azure-relay</artifactId>
  	<version>0.0.6</version>
	</dependency>

在 mvn 项目中运行 mvn dependency:copy-dependencies -DoutputDirectory=lib,以在项目的 lib 目录中添加依赖项 jar 文件。 这还将导入 azure-relay mvn 包的所有依赖项。 此包提供用于构造中继 URI 和令牌的函数。

编写一些代码来发送消息

  1. 将依赖项 jar 文件添加到 Listener.java 文件的 ClassPath。

    javac -cp lib/* src/main/java/com/example/listener/Listener.Java
    
  2. 导入 Listener.java 类中的依赖项。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    import com.microsoft.azure.relay.HybridConnectionListener;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.RelayedHttpListenerResponse;
    import com.microsoft.azure.relay.TokenProvider;
    
  3. 将以下 constants 添加到 createConnectionString java 函数 Listener.java 文件的顶部,以获取混合连接详细信息。

     public static String createConnectionString(){
         StringBuilder connectionString = new StringBuilder();
         connectionString.append("Endpoint=sb://");
         connectionString.append("{namespace}");
         connectionString.append(".servicebus.chinacloudapi.cn/;SharedAccessKeyName=");
         connectionString.append("{keyrule}");
         connectionString.append(";SharedAccessKey=");
         connectionString.append("{key}");
         connectionString.append(";EntityPath=");
         connectionString.append("{path}");
         return connectionString.toString();
     }
    

    将括号中的占位符替换为在创建混合连接时获得的值。

    1. namespace - 中继命名空间。 请务必使用完全限定的命名空间名称,例如 {namespace}.servicebus.chinacloudapi.cn
    2. path - 混合连接的名称。
    3. keyrule - SAS 密钥的名称。
    4. key - SAS 密钥值。
  4. 将以下代码添加到 Listener.java 文件中,下面是 java 类的预期主函数:

     public static void main( String[] args ) throws URISyntaxException
     {
         String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
         RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
         TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                     connectionParams.getSharedAccessKeyName(),
                     connectionParams.getSharedAccessKey());
         HybridConnectionListener listener = new HybridConnectionListener(new URI(connectionParams.getEndpoint().toString() + connectionParams.getEntityPath()), tokenProvider);
    
        // The "context" object encapsulates both the incoming request and the outgoing response
         listener.setRequestHandler((context) -> {
             String receivedText = "";
             if (context.getRequest().getInputStream() != null) {
                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream(), "UTF8"))) {
                     StringBuilder builder = new StringBuilder();
                     String inputLine;
                     while ((inputLine = reader.readLine()) != null) {
                         builder.append(inputLine);
                     }
                     receivedText = builder.toString();
                 } catch (IOException e) {
                     System.out.println(e.getMessage());
                 }
             }
             System.out.println("requestHandler received " + receivedText);
    
             RelayedHttpListenerResponse response = context.getResponse();
             response.setStatusCode(202);
             response.setStatusDescription("OK");
    
             try {
                 response.getOutputStream().write(("Echo: " + receivedText).getBytes());
             } catch (IOException e) {
                 e.printStackTrace();
             }
    
             // The context MUST be closed for the message to be sent
             response.close();
         });
    
         listener.openAsync().join();
    
         Scanner in = new Scanner(System.in);
         System.out.println("Press ENTER to terminate this program.");
         in.nextLine();
    
         listener.close();
         in.close();
     }
    
    

    Listener.java 文件的内容应如下所示:

    package com.example.listener;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    import com.microsoft.azure.relay.HybridConnectionListener;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.RelayedHttpListenerResponse;
    import com.microsoft.azure.relay.TokenProvider;
    
     public class Listener
     { 
         public static String createConnectionString(){
             StringBuilder connectionString = new StringBuilder();
             connectionString.append("Endpoint=sb://");
             connectionString.append("{namespace}");
             connectionString.append(".servicebus.chinacloudapi.cn/;SharedAccessKeyName=");
             connectionString.append("{keyrule}");
             connectionString.append(";SharedAccessKey=");
             connectionString.append("{key}");
             connectionString.append(";EntityPath=");
             connectionString.append("{path}");
             return connectionString.toString();
         }
    
         public static void main( String[] args ) throws URISyntaxException
         {
             String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
             RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
             TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                         connectionParams.getSharedAccessKeyName(),
                         connectionParams.getSharedAccessKey());
             HybridConnectionListener listener = new HybridConnectionListener(new URI(connectionParams.getEndpoint().toString() + connectionParams.getEntityPath()), tokenProvider);
    
            // The "context" object encapsulates both the incoming request and the outgoing response
             listener.setRequestHandler((context) -> {
                 String receivedText = "";
                 if (context.getRequest().getInputStream() != null) {
                     try (BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream(), "UTF8"))) {
                         StringBuilder builder = new StringBuilder();
                         String inputLine;
                         while ((inputLine = reader.readLine()) != null) {
                             builder.append(inputLine);
                         }
                         receivedText = builder.toString();
                     } catch (IOException e) {
                         System.out.println(e.getMessage());
                     }
                 }
                 System.out.println("requestHandler received " + receivedText);
    
                 RelayedHttpListenerResponse response = context.getResponse();
                 response.setStatusCode(202);
                 response.setStatusDescription("OK");
    
                 try {
                     response.getOutputStream().write(("Echo: " + receivedText).getBytes());
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
    
                 // The context MUST be closed for the message to be sent
                 response.close();
             });
    
             listener.openAsync().join();
    
             Scanner in = new Scanner(System.in);
             System.out.println("Press ENTER to terminate this program.");
             in.nextLine();
    
             listener.close();
             in.close();
         }
    }
    

创建客户端应用程序(发送程序)

若要将消息发送到中继,可使用任何 HTTP 客户端,或编写 Java 控制台应用程序。

创建 Java 应用程序

如果创建中继时已禁用“需要客户端授权”选项,可使用任何浏览器向混合连接 URL 发送请求。 若要访问受保护的终结点,需在 ServiceBusAuthorization 标头中创建并传递令牌,如下所示。

下面是一个简单的 Maven 项目结构和一个 Java 类,该类演示如何使用 Azure 中继库通过客户端授权将请求发送到混合连接 URL。

添加中继 MVN 包

修改 maven 应用程序包中的 pom.xml 文件,以包括以下内容:

	<dependency>
  	<groupId>com.microsoft.azure</groupId>
  	<artifactId>azure-relay</artifactId>
  	<version>0.0.6</version>
	</dependency>

在 mvn 项目中运行 mvn dependency:copy-dependencies -DoutputDirectory=lib,以在项目的 lib 目录中添加依赖项 jar 文件。 这还将导入 azure-relay mvn 包的所有依赖项。 此包提供用于构造中继 URI 和令牌的函数。

编写一些代码来发送消息

  1. 将依赖项 jar 文件添加到 Sender.java 文件的 ClassPath。

    javac -cp lib/* src/main/java/com/example/sender/Sender.Java
    
  2. 导入 Sender.java 类中的依赖项。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.Scanner;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.TokenProvider;
    
  3. 将以下 constants 添加到 createConnectionString java 函数 Sender.java 文件的顶部,以获取混合连接详细信息。

    public static String createConnectionString(){
        StringBuilder connectionString = new StringBuilder();
        connectionString.append("Endpoint=sb://");
        connectionString.append("{namespace}");
        connectionString.append(".servicebus.chinacloudapi.cn/;SharedAccessKeyName=");
        connectionString.append("{keyrule}");
        connectionString.append(";SharedAccessKey=");
        connectionString.append("{key}");
        connectionString.append(";EntityPath=");
        connectionString.append("{path}");
        return connectionString.toString();
    }
    

    将括号中的占位符替换为在创建混合连接时获得的值。

    1. namespace - 中继命名空间。 请务必使用完全限定的命名空间名称,例如 {namespace}.servicebus.chinacloudapi.cn
    2. path - 混合连接的名称。
    3. keyrule - SAS 密钥的名称。
    4. key - SAS 密钥值。
  4. 将以下代码添加到 Sender.java 文件中,下面是 java 类的预期主函数:

    public static void main(String[] args) throws IOException {
        String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
        if (CONNECTION_STRING_ENV_VARIABLE_NAME == null || CONNECTION_STRING_ENV_VARIABLE_NAME.isEmpty()){
            System.err.println("Connection string is null or empty. Please check your createConnectionString method.");
            return;
        }
        RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
        TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                connectionParams.getSharedAccessKeyName(), 
                connectionParams.getSharedAccessKey());
        URL url = buildHttpConnectionURL(connectionParams.getEndpoint().toString(), connectionParams.getEntityPath());
        String tokenString = tokenProvider.getTokenAsync(url.toString(), Duration.ofHours(1)).join().getToken();
        Scanner in = new Scanner(System.in);
        while (true) {
            System.out.println("Press ENTER to terminate this program.");
            String message = in.nextLine();
            int value = System.in.read();
            if (value == '\n' || value == '\r') {
                System.out.println("Terminating the program...");
                break;}
            // Starting a HTTP connection to the listener
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // Sending an HTTP request to the listener
            // To send a message body, use POST
            conn.setRequestMethod((message == null || message.length() == 0) ? "GET" : "POST");
            conn.setRequestProperty("ServiceBusAuthorization", tokenString);
            conn.setDoOutput(true);
            OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
            out.write(message, 0, message.length());
            out.flush();
            out.close();
            // Reading the HTTP response
            String inputLine;
            BufferedReader reader = null;
            StringBuilder responseBuilder = new StringBuilder();
            try {
                InputStream inputStream = conn.getInputStream();
                reader = new BufferedReader(new InputStreamReader(inputStream));
                System.out.println("status code: " + conn.getResponseCode());
                while ((inputLine = reader.readLine()) != null) {
                    responseBuilder.append(inputLine);
                }
                System.out.println("received back " + responseBuilder.toString());
            } catch (IOException e) {
                System.out.println("The listener is offline or could not be reached.");
                break;
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
        in.close();
    }
    
    static URL buildHttpConnectionURL(String endpoint, String entity) throws MalformedURLException {
        StringBuilder urlBuilder = new StringBuilder(endpoint + entity);
    
        // For HTTP connections, the scheme must be https://
        int schemeIndex = urlBuilder.indexOf("://");
        if (schemeIndex < 0) {
            throw new IllegalArgumentException("Invalid scheme from the given endpoint.");
        }
        urlBuilder.replace(0, schemeIndex, "https");
        return new URL(urlBuilder.toString());
    }
    

    Sender.java 文件的内容应如下所示:

    package com.example.sender;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.Scanner;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.TokenProvider;
    
    public class Sender
    {
    
        public static String createConnectionString(){
            StringBuilder connectionString = new StringBuilder();
            connectionString.append("Endpoint=sb://");
            connectionString.append("{namespace}");
            connectionString.append(".servicebus.chinacloudapi.cn/;SharedAccessKeyName=");
            connectionString.append("{keyrule}");
            connectionString.append(";SharedAccessKey=");
            connectionString.append("{key}");
            connectionString.append(";EntityPath=");
            connectionString.append("{path}");
            return connectionString.toString();
            }
        public static void main(String[] args) throws IOException {
            String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
            if (CONNECTION_STRING_ENV_VARIABLE_NAME == null || CONNECTION_STRING_ENV_VARIABLE_NAME.isEmpty()){
                System.err.println("Connection string is null or empty. Please check your createConnectionString method.");
                return;
            }
            RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
            TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                    connectionParams.getSharedAccessKeyName(), 
                    connectionParams.getSharedAccessKey());
            URL url = buildHttpConnectionURL(connectionParams.getEndpoint().toString(), connectionParams.getEntityPath());
            String tokenString = tokenProvider.getTokenAsync(url.toString(), Duration.ofHours(1)).join().getToken();
            Scanner in = new Scanner(System.in);
            while (true) {
                System.out.println("Press ENTER to terminate this program.");
                String message = in.nextLine();
                int value = System.in.read();
                if (value == '\n' || value == '\r') {
                    System.out.println("Terminating the program...");
                    break;}
                // Starting a HTTP connection to the listener
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                // Sending an HTTP request to the listener
                // To send a message body, use POST
                conn.setRequestMethod((message == null || message.length() == 0) ? "GET" : "POST");
                conn.setRequestProperty("ServiceBusAuthorization", tokenString);
                conn.setDoOutput(true);
                OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
                out.write(message, 0, message.length());
                out.flush();
                out.close();
                // Reading the HTTP response
                String inputLine;
                BufferedReader reader = null;
                StringBuilder responseBuilder = new StringBuilder();
                try {
                    InputStream inputStream = conn.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    System.out.println("status code: " + conn.getResponseCode());
                    while ((inputLine = reader.readLine()) != null) {
                        responseBuilder.append(inputLine);
                    }
                    System.out.println("received back " + responseBuilder.toString());
                } catch (IOException e) {
                    System.out.println("The listener is offline or could not be reached.");
                    break;
                } finally {
                    if (reader != null) {
                        reader.close();
                    }
                }
            }
            in.close();
        }
    
        static URL buildHttpConnectionURL(String endpoint, String entity) throws MalformedURLException {
            StringBuilder urlBuilder = new StringBuilder(endpoint + entity);
    
            // For HTTP connections, the scheme must be https://
            int schemeIndex = urlBuilder.indexOf("://");
            if (schemeIndex < 0) {
                throw new IllegalArgumentException("Invalid scheme from the given endpoint.");
            }
            urlBuilder.replace(0, schemeIndex, "https");
            return new URL(urlBuilder.toString());
        }
    }
    

运行应用程序

  1. 运行服务器应用程序:在 Java 命令提示符或应用程序类型处,键入 java -cp <jar_dependency_path> com.example.listener.Listener.java
  2. 运行客户端应用程序:在 Java 命令提示符或应用程序类型处,键入 java -cp <jar_dependency_path> com.example.sender.Sender.java,并输入某些文本。
  3. 确保服务器应用程序控制台输出了客户端应用程序中输入的文本。

恭喜,现已使用 Java 创建端到端混合连接应用程序!

后续步骤

在本快速入门中,你创建了 Java 客户端和服务器应用程序,此类程序使用 HTTP 来发送和接收消息。 Azure 中继的混合连接功能也支持使用 WebSocket 发送和接收消息。 若要了解如何将 WebSocket 与 Azure 中继混合连接配合使用,请参阅 WebSocket 快速入门

在本快速入门中,你使用了 Java 来创建客户端和服务器应用程序。 若要了解如何使用 .NET Framework 编写客户端和服务器应用程序,请参阅 .NET WebSocket 快速入门.NET HTTP 快速入门