Netty core principles: 2. Intermediate extension-07: Netty request and response synchronous communication

Article directory

  • 1. Introduction
  • 2. Code implementation
    • 2.0 Introduce dependencies
    • 2.1 Engineering structure
    • 2.2 RPC codec
      • 2.2.1 Serialization tool class
      • 2.2.2 RPC decoder
      • 2.2.3 RPC encoder
    • 2.3 Writing interface and its implementation
      • 2.3.1 Write interface MAP
      • 2.3.2 Writing interface
      • 2.3.3 Locked writing interface implementation class
      • 2.3.4 Writing class
    • 2.4 Request and response objects
      • 2.4.1 Request object
      • 2.4.2 Response object
    • 2.5 Netty client implementation
      • 2.5.1 Client Processor
      • 2.5.2 Client connection
    • 2.6 Netty server implementation
      • 2.6.1 Server-side processor
      • 2.6.2 Server connection
    • 2.7 Test class
      • 2.7.1 Client testing
      • 2.7.2 Server-side testing
  • 3. Unit testing

1. Introduction

When implementing and developing the RPC framework, you need to choose the socket communication method. And we know that under normal circumstances, socket communication is similar to QQ chat. You can send messages and reply at any time. However, our RPC framework communication, which feels similar to http calls, needs to return within a certain period of time, otherwise a timeout will occur.
Here we choose netty as our socket framework and use the future method for communication.

  • Dubbo: The earliest open source RPC framework in China, developed by Alibaba and open sourced at the end of 2011, only supports Java language.
  • Motan: The RPC framework used internally by Weibo was open sourced in 2016 and only supports the Java language.
  • Tars: The RPC framework used internally by Tencent was open sourced in 2017 and only supports C++ language.
  • Spring Cloud: An RPC framework open sourced by the foreign company Pivotal in 2014. It only supports the Java language.
  • gRPC: A cross-language RPC framework open sourced by Google in 2015, supporting multiple languages.
  • Thrift: Originally an internal system cross-language RPC framework developed by Facebook, it was contributed to the Apache Foundation in 2017 and became one of the Apache open source projects, supporting multiple languages.
  • hprose: A new lightweight cross-language cross-platform object-oriented high-performance remote dynamic communication middleware licensed by MIT open source, supporting multiple languages.
    • nodeJs, C++, .NET, Java, Delphi, Objective-C, ActionScript, JavaScript, ASP, PHP, Python, Ruby, Perl, Golang.

2. Code implementation

2.0 introduces dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.lino</groupId>
        <artifactId>netty-step-02</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>netty-2.0-07</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <protostuff.version>1.0.10</protostuff.version>
        <objenesis.version>2.4</objenesis.version>
    </properties>

    <dependencies>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <!-- Protostuff -->
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>${protostuff.version}</version>
        </dependency>
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>${protostuff.version}</version>
        </dependency>
        <dependency>
            <groupId>org.objenesis</groupId>
            <artifactId>objenesis</artifactId>
            <version>${objenesis.version}</version>
        </dependency>
    </dependencies>

</project>

2.1 Project Structure

netty-2.0-07
|-src
    |-main
         |-java
             |-com.lino.netty
|-client
| |-ClientSocket.java
| |-MyClientHandler.java
|-codec
| |-RpcDecoder.java
| |-RpcEncoder.java
|-future
| |-SyncWrite.java
| |-SyncWriteFuture.java
| |-SyncWriteMap.java
| |-WriteFuture.java
|-msg
| |-Request.java
| |-Response.java
|-server
| |-MyServerHandler.java
| |-ServerSocket.java
|-util
| |-SerializationUtil.java
    |-test
         |-java
             |-com.lino.netty.test
|-StartClient.java
|-StartServer.java

2.2 RPC codec

2.2.1 Serialization tool class

SerializationUtil.java

package com.lino.netty.util;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: Serialization tool class
 */
public class SerializationUtil {<!-- -->

    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

    private static Objenesis objenesis = new ObjenesisStd();

    public SerializationUtil() {<!-- -->
    }

    /**
     * Serialization (object -> byte array)
     *
     * @param obj object
     * @return byte array
     */
    public static <T> byte[] serialize(T obj) {<!-- -->
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {<!-- -->
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {<!-- -->
            throw new IllegalStateException(e.getMessage(), e);
        } finally {<!-- -->
            buffer.clear();
        }
    }

    /**
     * Deserialization (byte array -> object)
     *
     * @param data byte array
     * @param cls object type
     * @return object
     */
    public static <T> T deserialize(byte[] data, Class<T> cls) {<!-- -->
        try {<!-- -->
            T message = objenesis.newInstance(cls);
            Schema<T> schema = getSchema(cls);
            ProtostuffIOUtil.mergeFrom(data, message, schema);
            return message;
        } catch (Exception e) {<!-- -->
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    private static <T> Schema<T> getSchema(Class<T> cls) {<!-- -->
        Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {<!-- -->
            schema = RuntimeSchema.createFrom(cls);
            cachedSchema.put(cls, schema);
        }
        return schema;
    }
}

2.2.2 RPC decoder

RpcDecoder.java

package com.lino.netty.codec;

import com.lino.netty.util.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;

/**
 * @description: RPC decoder
 */
public class RpcDecoder extends ByteToMessageDecoder {<!-- -->

    private Class<?> genericClass;

    public RpcDecoder(Class<?> genericClass) {<!-- -->
        this.genericClass = genericClass;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {<!-- -->
        if (in.readableBytes() < 4) {<!-- -->
            return;
        }
        in.markReaderIndex();
        int dataLength = in.readInt();
        if (in.readableBytes() < dataLength) {<!-- -->
            in.resetReaderIndex();
            return;
        }
        byte[] data = new byte[dataLength];
        in.readBytes(data);
        out.add(SerializationUtil.deserialize(data, genericClass));
    }
}

2.2.3 RPC encoder

RpcEncoder.java

package com.lino.netty.codec;

import com.lino.netty.util.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * @description: RPC encoder
 */
public class RpcEncoder extends MessageToByteEncoder {<!-- -->

    private Class<?> genericClass;

    public RpcEncoder(Class<?> genericClass) {<!-- -->
        this.genericClass = genericClass;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {<!-- -->
        if (genericClass.isInstance(in)) {<!-- -->
            byte[] data = SerializationUtil.serialize(in);
            out.writeInt(data.length);
            out.writeBytes(data);
        }
    }
}

2.3 Writing interface and its implementation

2.3.1 Writing interface MAP

SyncWriteMap.java

package com.lino.netty.future;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: Lock and write to map
 */
public class SyncWriteMap {<!-- -->

    public static Map<String, WriteFuture> syncKey = new ConcurrentHashMap<>();
}

2.3.2 Writing interface

WriteFuture.java

package com.lino.netty.future;

import com.lino.netty.msg.Response;
import java.util.concurrent.Future;

/**
 * @description: writing interface
 */
public interface WriteFuture<T> extends Future<T> {<!-- -->

    Throwable cause();

    void setCause(Throwable cause);

    boolean isWriteSuccess();

    void setWriteResult(boolean result);

    String requestId();

    T response();

    void setResponse(Response response);

    boolean isTimeout();

}

2.3.3 Locked writing interface implementation class

SyncWriteFuture.java

package com.lino.netty.future;

import com.lino.netty.msg.Response;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @description: Lock writing interface implementation class
 */
public class SyncWriteFuture implements WriteFuture<Response> {<!-- -->

    private CountDownLatch latch = new CountDownLatch(1);
    private final long begin = System.currentTimeMillis();
    private long timeout;
    private Response response;
    private final String requestId;
    private boolean writeResult;
    private Throwable cause;
    private boolean isTimeout = false;

    public SyncWriteFuture(String requestId) {<!-- -->
        this.requestId = requestId;
    }

    public SyncWriteFuture(long timeout, String requestId) {<!-- -->
        this.timeout = timeout;
        this.requestId = requestId;
        writeResult = true;
        isTimeout = true;
    }

    @Override
    public Throwable cause() {<!-- -->
        return cause;
    }

    @Override
    public void setCause(Throwable cause) {<!-- -->
        this.cause = cause;
    }

    @Override
    public boolean isWriteSuccess() {<!-- -->
        return writeResult;
    }

    @Override
    public void setWriteResult(boolean result) {<!-- -->
        this.writeResult = result;
    }

    @Override
    public String requestId() {<!-- -->
        return requestId;
    }

    @Override
    public Response response() {<!-- -->
        return response;
    }

    @Override
    public void setResponse(Response response) {<!-- -->
        this.response = response;
        latch.countDown();
    }

    @Override
    public boolean isTimeout() {<!-- -->
        if (isTimeout) {<!-- -->
            return isTimeout;
        }
        return System.currentTimeMillis() - begin > timeout;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {<!-- -->
        return true;
    }

    @Override
    public boolean isCancelled() {<!-- -->
        return false;
    }

    @Override
    public boolean isDone() {<!-- -->
        return false;
    }

    @Override
    public Response get() throws InterruptedException, ExecutionException {<!-- -->
        latch.wait();
        return response;
    }

    @Override
    public Response get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {<!-- -->
        if (latch.await(timeout, unit)) {<!-- -->
            return response;
        }
        return null;
    }
}

2.3.4 Writing class

SyncWrite.java

package com.lino.netty.future;

import com.lino.netty.msg.Request;
import com.lino.netty.msg.Response;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @description: Lock write object
 */
public class SyncWrite {<!-- -->

    public Response writeAndSync(final Channel channel, final Request request, final long timeout) throws Exception {<!-- -->

        if (channel == null) {<!-- -->
            throw new NullPointerException("channel");
        }
        if (request == null) {<!-- -->
            throw new NullPointerException("request");
        }
        if (timeout <= 0) {<!-- -->
            throw new IllegalArgumentException("timeout <= 0");
        }

        String requestId = UUID.randomUUID().toString();
        request.setRequestId(requestId);

        WriteFuture<Response> future = new SyncWriteFuture(request.getRequestId());
        SyncWriteMap.syncKey.put(request.getRequestId(), future);

        Response response = doWriteAndSync(channel, request, timeout, future);
        SyncWriteMap.syncKey.remove(request.getRequestId());
        return response;
    }

    private Response doWriteAndSync(final Channel channel, final Request request, final long timeout, final WriteFuture<Response> writeFuture) throws Exception {<!-- -->

        channel.writeAndFlush(request).addListener(new ChannelFutureListener() {<!-- -->
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {<!-- -->
                writeFuture.setWriteResult(future.isSuccess());
                writeFuture.setCause(future.cause());
                // Failed to remove
                if (!writeFuture.isWriteSuccess()) {<!-- -->
                    SyncWriteMap.syncKey.remove(writeFuture.requestId());
                }
            }
        });

        Response response = writeFuture.get(timeout, TimeUnit.MILLISECONDS);
        if (request == null) {<!-- -->
            if (writeFuture.isTimeout()) {<!-- -->
                throw new TimeoutException();
            } else {<!-- -->
                throw new Exception(writeFuture.cause());
            }
        }

        return response;
    }
}

2.4 Request and response objects

2.4.1 Request object

Request.java

package com.lino.netty.msg;

/**
 * @description: request object
 */
public class Request {<!-- -->

    private String requestId;

    private Object result;

    public String getRequestId() {<!-- -->
        return requestId;
    }

    public void setRequestId(String requestId) {<!-- -->
        this.requestId = requestId;
    }

    public Object getResult() {<!-- -->
        return result;
    }

    public void setResult(Object result) {<!-- -->
        this.result = result;
    }
}

2.4.2 Response object

Response.java

package com.lino.netty.msg;

/**
 * @description: response object
 */
public class Response {<!-- -->

    private String requestId;

    private String param;

    public String getRequestId() {<!-- -->
        return requestId;
    }

    public void setRequestId(String requestId) {<!-- -->
        this.requestId = requestId;
    }

    public String getParam() {<!-- -->
        return param;
    }

    public void setParam(String param) {<!-- -->
        this.param = param;
    }
}

2.5 Netty client implementation

2.5.1 Client Processor

MyClientHandler.java

package com.lino.netty.client;

import com.lino.netty.future.SyncWriteFuture;
import com.lino.netty.future.SyncWriteMap;
import com.lino.netty.msg.Response;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @description: client processor
 */
public class MyClientHandler extends ChannelInboundHandlerAdapter {<!-- -->

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {<!-- -->
        Response msg = (Response) obj;
        String requestId = msg.getRequestId();
        SyncWriteFuture future = (SyncWriteFuture) SyncWriteMap.syncKey.get(requestId);
        if (future != null) {<!-- -->
            future.setResponse(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {<!-- -->
        cause.printStackTrace();
        ctx.close();
    }
}

2.5.2 Client connection

ClientSocket.java

package com.lino.netty.client;

import com.lino.netty.codec.RpcDecoder;
import com.lino.netty.codec.RpcEncoder;
import com.lino.netty.msg.Request;
import com.lino.netty.msg.Response;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @description: client connection
 */
public class ClientSocket implements Runnable {<!-- -->

    private ChannelFuture future;

    @Override
    public void run() {<!-- -->
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {<!-- -->
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.AUTO_READ, true);
            b.handler(new ChannelInitializer<SocketChannel>() {<!-- -->
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {<!-- -->
                    channel.pipeline().addLast(new RpcDecoder(Response.class),
                            new RpcEncoder(Request.class),
                            new MyClientHandler());
                }
            });
            ChannelFuture f = b.connect("127.0.0.1", 7397).sync();
            this.future = f;
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            workerGroup.shutdownGracefully();
        }
    }

    public ChannelFuture getFuture() {<!-- -->
        return future;
    }

    public void setFuture(ChannelFuture future) {<!-- -->
        this.future = future;
    }
}

2.6 Netty server implementation

2.6.1 Server-side processor

MyServerHandler.java

package com.lino.netty.server;

import com.lino.netty.msg.Request;
import com.lino.netty.msg.Response;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

/**
 * @description: Server-side processor
 */
public class MyServerHandler extends ChannelInboundHandlerAdapter {<!-- -->

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {<!-- -->
        Request msg = (Request) obj;
        //feedback
        Response request = new Response();
        request.setRequestId(msg.getRequestId());
        request.setParam(msg.getResult() + "The request is successful, please accept and process the feedback result.");
        ctx.writeAndFlush(request);
        // freed
        ReferenceCountUtil.release(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {<!-- -->
        ctx.flush();
    }
}

2.6.2 Server connection

ServerSocket.java

package com.lino.netty.server;

import com.lino.netty.codec.RpcDecoder;
import com.lino.netty.codec.RpcEncoder;
import com.lino.netty.msg.Request;
import com.lino.netty.msg.Response;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @description: Server connection
 */
public class ServerSocket implements Runnable {<!-- -->

    private ChannelFuture f;

    @Override
    public void run() {<!-- -->
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {<!-- -->
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new ChannelInitializer<SocketChannel>() {<!-- -->
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {<!-- -->
                            channel.pipeline().addLast(
                                    new RpcDecoder(Request.class),
                                    new RpcEncoder(Response.class),
                                    newMyServerHandler()
                            );
                        }
                    });
            ChannelFuture f = null;
            f = b.bind(7397).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

2.7 Test class

2.7.1 Client Test

StartClient.java

package com.lino.netty.test;

import com.alibaba.fastjson.JSON;
import com.lino.netty.client.ClientSocket;
import com.lino.netty.future.SyncWrite;
import com.lino.netty.msg.Request;
import com.lino.netty.msg.Response;
import io.netty.channel.ChannelFuture;

/**
 * @description: client test
 */
public class StartClient {<!-- -->

    private static ChannelFuture future;

    public static void main(String[] args) {<!-- -->
        System.out.println("hi netty client");
        ClientSocket client = new ClientSocket();
        new Thread(client).start();

        while (true) {<!-- -->
            try {<!-- -->
                // Get the future, the thread has waiting time for processing
                if (null == future) {<!-- -->
                    future = client.getFuture();
                    Thread.sleep(500);
                    continue;
                }
                //Construct sending parameters
                Request request = new Request();
                request.setResult("Query user information");
                SyncWrite s = new SyncWrite();
                Response response = s.writeAndSync(future.channel(), request, 1000);
                System.out.println("Call result:" + JSON.toJSON(response));
                Thread.sleep(1000);
            } catch (Exception e) {<!-- -->
                e.printStackTrace();
            }
        }
    }
}

2.7.2 Server-side testing

StartServer.java

package com.lino.netty.test;

import com.lino.netty.server.ServerSocket;

/**
 * @description: Server-side testing
 */
public class StartServer {<!-- -->

    public static void main(String[] args) {<!-- -->
        new Thread(new ServerSocket()).start();
        System.out.println("lino-learn-netty server start done.");
    }
}

3. Unit testing

Start the NettyServer server

lino-learn-netty server start done.

Start the Netty client

hi netty client
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"fdcc8302-e5c5-47a8-bfa3- 0023d870f10b"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"ca6cd0af-b81d-496d-b649- f5e49187f202"}
Call result: {<!-- -->"param":"The request to query user information is successful, please accept the feedback result for processing.","requestId":"bbd11aca-e8b6-4293-8827- 389acd4779b5"}
Call result: {<!-- -->"param":"The request to query user information is successful, please accept the feedback result for processing.","requestId":"cfb5e544-bf9d-4b31-aa35- 96305b96cbbf"}
Call result: {<!-- -->"param":"The user information query request was successful, please accept the feedback result for processing.","requestId":"880a9048-95ce-496c-bcd9- 79b7021e1b28"}
Call result: {<!-- -->"param":"The user information query request was successful, please accept the feedback result for processing.","requestId":"17c0a3ab-37bf-4371-808a- c10c7049dc1c"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"c6d491f8-bb6d-4059-9692- aea1d5050c39"}
Call result: {<!-- -->"param":"The request to query user information is successful, please accept the feedback result for processing.","requestId":"db6f57f4-5e18-4c04-bf8f- 0c473c03f121"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"81e2192f-02e7-469c-ae27- 438429bd7a32"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"b7187328-7f5c-4ad5-8bd6- 6b5203eef02b"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"ad05992e-4f58-4e9f-91f5- 04b47b76445b"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"4f6665eb-f796-4b2d-a98d- e6035b2a4813"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"f71b6d4d-ed7a-4ae4-8e99- 51acb46de555"}
Call result: {<!-- -->"param":"The user information query request is successful, please accept the feedback result for processing.","requestId":"6e8d1440-79ad-4081-8bde- 1e01665bf999"}