All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.iteaj.iot.client.protocol.ClientInitiativeProtocol Maven / Gradle / Ivy

The newest version!
package com.iteaj.iot.client.protocol;

import cn.hutool.core.util.StrUtil;
import com.iteaj.iot.*;
import com.iteaj.iot.business.ProtocolHandleFactory;
import com.iteaj.iot.client.*;
import com.iteaj.iot.consts.ExecStatus;
import io.netty.channel.ChannelFuture;

import java.nio.channels.ClosedChannelException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 首先它是一个客户端协议
 * 其次它是一个客户端主动请求服务端的协议
 * @param 
 */
public abstract class ClientInitiativeProtocol extends ClientSocketProtocol implements ProtocolPreservable {

    private long timeout = 5 * 1000; // 超时时间, 默认10秒
    private CountDownLatch downLatch; //同步锁

    private SocketClient iotClient;

    // 自定义协议处理器
    private FreeProtocolHandle freeProtocolHandle;

    /**
     * 1. 如果业务处理器存在则执行业务
     * 2. 如果是同步请求则交由业务线程处理
     * 3. 如果是异步请求则交由netty工作线程处理 这种情况最好不要执行耗时的动作
     * @param factory 协议业务
     * @return
     */
    @Override
    public AbstractProtocol exec(ProtocolHandleFactory factory) {
        ProtocolHandle protocolHandle = getProtocolHandle();

        // 如果是同步请求则交由其线程执行业务
        if(protocolHandle != null && !isSyncRequest()) {
            return this.exec(protocolHandle);
        }

        return null;
    }

    @Override
    public AbstractProtocol exec(ProtocolHandle handle) {
        if(handle != null) {
            handle.handle(this);
        }

        // 客户端主动调用的协议不需要在响应给服务端, 返回null
        return null;
    }

    @Override
    public ClientSocketProtocol buildRequestMessage() {
        this.requestMessage = doBuildRequestMessage();

        if(this.requestMessage == null) {
            throw new ProtocolException("构建请求报文失败");
        }

        if(this.requestMessage().getHead() == null || this.requestMessage().getBody() == null) {
            throw new ProtocolException("构建请求报文失败, 没有设置[MessageHead] or [MessageBody]");
        }

        return this;
    }

    protected abstract C doBuildRequestMessage();

    /**
     *
     * @return
     */
    @Override
    public ClientSocketProtocol buildResponseMessage() {
        try {
            doBuildResponseMessage(responseMessage());

            return null;
        } finally {
            // 如果是同步请求必须释放锁
            this.releaseLock();
        }
    }

    public final AbstractProtocol buildResponseMessage(C responseMessage) {
        this.responseMessage = responseMessage;
        return this.buildResponseMessage();
    }

    /**
     * @param responseMessage
     */
    public abstract void doBuildResponseMessage(C responseMessage);

    /**
     * 平台主动向外发起请求
     */
    protected void sendRequest() throws ClientProtocolException {
        try {

            /**
             * 首先 先获取对应的客户端 {@link #getClientKey()}
             */
            SocketClient client = getIotClient();

            /**
             * 其次 构建要请求的报文
             */
            buildRequestMessage();

            /**
             * 对已经构建好的报文做些其他初始化操作
             * @see #requestMessage()
             */
            this.requestMessageHandle(client);

            /**
             * 最后 写出报文
             */
            writeAndFlush(client);

            // 写出失败或者连接不在线
            if(getExecStatus() != ExecStatus.success) {
                this.exec(getProtocolHandle()); return;
            }

            /**
             * 是同步请求
             */
            if(isSyncRequest()) {
                // 检查是否会出现死锁
                syncDeadValidate(client);

                // 如果发送成功等待报文响应
                boolean await = getDownLatch().await(getTimeout(), TimeUnit.MILLISECONDS);
                if(!await) { // 响应超时
                    this.execTimeoutHandle();
                }

                // 同步执行业务
                this.exec(getProtocolHandle());
            } else if(!isRelation()) { // 既不是同步也不是异步, 直接执行业务
                this.exec(getProtocolHandle());
            }

        } catch (InterruptedException e) {
            throw new ClientProtocolException(e);
        } catch (Exception e) {
            throw new ClientProtocolException(e.getMessage(), e, this);
        }
    }

    @Override
    public SocketClient getIotClient() {
        if(iotClient == null) {
            iotClient = super.getIotClient();
        }

        return iotClient;
    }

    protected void requestMessageHandle(SocketClient client) {
        if(client.getChannel() != null) {
            requestMessage().setProperties(client.getConfig())
                    .setChannelId(client.getChannel().id().asShortText());
        }
    }

    protected void writeAndFlush(SocketClient client) throws InterruptedException {
        // 没有连接则先进行同步重连
        if(!client.isConnect()) {
            boolean await = client.connect().await(client.getConfig().getConnectTimeout());
            if(!await) {
                this.setReason("连接超时");
                setExecStatus(ExecStatus.timeout);
            }
        }

        ChannelFuture request = client.writeAndFlush(this);
        boolean await = request.await(getTimeout(), TimeUnit.MILLISECONDS);
        if(!await) {
            this.setReason("请求超时");
            setExecStatus(ExecStatus.timeout);
        } else if(!request.isDone()) { // 在timeout等于0的情况
            return;
        } else if(!request.isSuccess()) {
            if(request.cause() instanceof UnWritableProtocolException) {
                setExecStatus(ExecStatus.notWritable);
            } else {
                setExecStatus(ExecStatus.fail);
            }

            this.setReason(request.cause() != null ? request.cause().getMessage() : "请求失败");
        }
    }

    /**
     * 同步时的死锁校验
     * 对于同步请求, 释放锁的条件是等待tcp连接的返回
* 如果此时锁住此线程则此tcp的读操作失效只能等待超时 * 所以同步线程和{@code Channel}的工作线程不能是同一个 * @param client */ protected void syncDeadValidate(SocketClient client) { if(client.getChannel().eventLoop().inEventLoop()) { throw new IllegalThreadStateException("同步线程和连接工作线程相同将导致死锁"); } } protected void execTimeoutHandle() { setExecStatus(ExecStatus.timeout); if(isRelation()) { // 移除掉对应的协议 try { syncRemoveTimeoutProtocol(); } finally { // 释放锁 releaseLock(); } } } /** * 这里的移除将比超时管理器早 * @see ProtocolTimeoutManager#protocolTimeoutValidate(ProtocolTimeoutStorage) 协议超时管理 */ protected void syncRemoveTimeoutProtocol() { Message.MessageHead head = requestMessage().getHead(); ClientComponent component = FrameworkManager.getClientComponent(requestMessage().getClass()); Object protocol = component.protocolFactory().remove(getMessageId()); // 当前的协议和移除不是同一个对象(已被修改) if(protocol != null && protocol != this) { throw new ClientProtocolException("协议对象状态异常[已被修改]"); } else if(logger.isWarnEnabled()) { logger.warn("协议同步超时({}) 超时移除({}ms) - 客户端编号: {} - messageId: {} - 协议类型: {}" , component.getName(), getTimeout(), head.getEquipCode(), head.getMessageId(), protocolType()); } } public void request(ClientProtocolCallHandle handle) { this.request((FreeProtocolHandle) handle); } /** * 平台主动发起请求 * @param handle 需要处理的业务 */ public void request(FreeProtocolHandle handle) throws ProtocolException { this.freeProtocolHandle = handle; if(this.getFreeProtocolHandle() == null) { throw new ClientProtocolException("[handle]不能为Null"); } // 指定要执行的业务后, 必须指定超时时间 validateTimeout(getTimeout()); this.request(); } /** * 请求默认的远程主机 * @see ClientComponent#getClient() * @throws ProtocolException */ public void request() throws ProtocolException { this.sendRequest(); } /** * 请求指定的远程主机 * @see com.iteaj.iot.client.component.SocketClientComponent#createNewClientAndConnect(ClientConnectProperties) * @param host 远程主机 * @param port 远程端口 */ public void request(String host, int port) { if(StrUtil.isBlank(host) || port < 0) { throw new IllegalArgumentException("未指定请求服务器的[host or port]"); } this.setClientKey(new ClientConnectProperties(host, port)); request(); } /** * 请求指定的远程主机, 对应的连接需要先创建 * @see com.iteaj.iot.client.component.SocketClientComponent#createNewClientAndConnect(ClientConnectProperties) * @param server 远程主机配置 */ public void request(ClientConnectProperties server) { if(server == null) { throw new IllegalArgumentException("未指定请求服务器的参数[server]"); } this.setClientKey(server); request(); } /** * 请求指定的远程主机, 如果主机不存在则会先创建{@link #getIotClient()} * @param server 远程主机 * @param handle 自定义处理器 */ public void request(ClientConnectProperties server, ClientProtocolCallHandle handle) { this.request(server, (FreeProtocolHandle) handle); } /** * 请求指定的远程主机, 如果主机不存在则会先创建{@link #getIotClient()} * @param server 远程主机 * @param handle 自定义处理器 */ public void request(ClientConnectProperties server, FreeProtocolHandle handle) { if(server == null) { throw new IllegalArgumentException("未指定请求服务器的参数[server]"); } this.setClientKey(server); request(handle); } protected ProtocolHandle getProtocolHandle() { ProtocolHandle protocolHandle = getFreeProtocolHandle(); if(protocolHandle == null) { protocolHandle = getDefaultProtocolHandle(); } return protocolHandle; } protected void validateTimeout(long timeout) { if(timeout < 0) { throw new ProtocolException("超时时间(timeout)必须>=0(ms)"); } this.setTimeout(timeout); } protected void setTimeout(long timeout) { this.timeout = timeout; } /** * 客户端是否激活 * @return */ public boolean isActive() { return getIotClient().getChannel() != null && getIotClient().getChannel().isActive(); } /** * 同步请求 * @param timeout 同步超时时间 * @return */ public ClientInitiativeProtocol sync(long timeout) { validateTimeout(timeout); setDownLatch(new CountDownLatch(1)); return this; } /** * 设置异步超时时间 * @see ProtocolTimeoutManager 超时报文管理 * @param timeout 超时时间 * @return */ @Override public ClientInitiativeProtocol timeout(long timeout) { validateTimeout(timeout); return this; } @Override protected String getMessageId() { ClientMessage message = requestMessage(); return message.getMessageId(); } @Override public abstract ProtocolType protocolType(); /** * 是否进行同步请求处理,默认否 * @return */ @Override public boolean isSyncRequest(){ return getDownLatch() != null; } @Override public void releaseLock() { if(isSyncRequest()) { getDownLatch().countDown(); } } protected CountDownLatch getDownLatch() { return downLatch; } protected void setDownLatch(CountDownLatch downLatch) { this.downLatch = downLatch; } @Override public String relationKey() { return getMessageId(); } @Override public long getTimeout() { return timeout; } public FreeProtocolHandle getFreeProtocolHandle() { return freeProtocolHandle; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy