apache NIFI 自定义grpc processor 源码

源码结构

在这里插入图片描述

实现效果

在这里插入图片描述

核心代码说明

继承AbstractProcessor

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.nifi.processors.grpc;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.protobuf.ByteString;
import io.grpc.CompressorRegistry;
import io.grpc.DecompressorRegistry;
import io.grpc.ManagedChannel;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.StreamObserver;
import io.netty.handler.ssl.SslContext;
import org.apache.nifi.annotation.behavior.InputRequirement;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.behavior.WritesAttribute;
import org.apache.nifi.annotation.behavior.WritesAttributes;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.DeprecationNotice;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnScheduled;
import org.apache.nifi.annotation.lifecycle.OnShutdown;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processor.*;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.grpc.gen.edge.v1.*;
import org.apache.nifi.processors.grpc.id.EntityType;
import org.apache.nifi.processors.grpc.ssl.SslContextProvider;
import org.apache.nifi.processors.grpc.util.EdgeUtils;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.StopWatch;

import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

@SupportsBatching
@Tags({"grpc", "rpc", "client"})
@InputRequirement(InputRequirement.Requirement.INPUT_REQUIRED)
@CapabilityDescription("Sends FlowFiles, optionally with content, to a configurable remote gRPC service endpoint. The remote gRPC service must abide by the service IDL defined in NiFi. " +
        " gRPC isn't intended to carry large payloads,  so this processor should be used only when FlowFile" +
        " sizes are on the order of megabytes. The default maximum message size is 4MB.")
@WritesAttributes({
        @WritesAttribute(attribute = "invokegrpc.response.code", description = "The response code that is returned (0 = ERROR, 1 = SUCCESS, 2 = RETRY)"),
        @WritesAttribute(attribute = "invokegrpc.response.body", description = "The response message that is returned"),
        @WritesAttribute(attribute = "invokegrpc.service.host", description = "The remote gRPC service hostname"),
        @WritesAttribute(attribute = "invokegrpc.service.port", description = "The remote gRPC service port"),
        @WritesAttribute(attribute = "invokegrpc.service.cloudKey", description = "The remote gRPC service cloudKey"),
        @WritesAttribute(attribute = "invokegrpc.service.cloudSecret", description = "The remote gRPC service cloudSecret"),
        @WritesAttribute(attribute = "invokegrpc.java.exception.class", description = "The Java exception class raised when the processor fails"),
        @WritesAttribute(attribute = "invokegrpc.java.exception.message", description = "The Java exception message raised when the processor fails"),
})
@DeprecationNotice(reason = "No planned alternatives to be offered. Use custom processors instead.")
public class InvokeGRPC extends AbstractProcessor {
    public static final String RESPONSE_CODE = "invokegrpc.response.code";
    public static final String RESPONSE_BODY = "invokegrpc.response.body";
    public static final String SERVICE_HOST = "invokegrpc.service.host";
    public static final String SERVICE_PORT = "invokegrpc.service.port";
    public static final String ROUTING_CLOUD_KEY = "invokegrpc.service.cloudKey";
    public static final String ROUTING_CLOUD_SECRET = "invokegrpc.service.cloudSecret";
    public static final String EXCEPTION_CLASS = "invokegrpc.java.exception.class";
    public static final String EXCEPTION_MESSAGE = "invokegrpc.java.exception.message";

    // properties
    public static final PropertyDescriptor PROP_SERVICE_HOST = new PropertyDescriptor.Builder()
            .name("Remote gRPC service hostname")
            .description("Remote host which will be connected to")
            .required(true)
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
            .build();
    public static final PropertyDescriptor PROP_SERVICE_PORT = new PropertyDescriptor.Builder()
            .name("Remote gRPC service port")
            .description("Remote port which will be connected to")
            .required(true)
            .addValidator(StandardValidators.PORT_VALIDATOR)
            .build();
    public static final PropertyDescriptor PROP_ROUTING_CLOUD_KEY = new PropertyDescriptor.Builder()
            .name("Remote gRPC service key")
            .description("Remote key which will be connected to")
            .required(true)
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
            .build();
    public static final PropertyDescriptor PROP_ROUTING_CLOUD_SECRET = new PropertyDescriptor.Builder()
            .name("Remote gRPC service secret")
            .description("secret  which will be connected to")
            .required(true)
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
            .build();


    public static final PropertyDescriptor PROP_MAX_MESSAGE_SIZE = new PropertyDescriptor.Builder()
            .name("Max Message Size")
            .description("The maximum size of FlowFiles that this processor will allow to be received." +
                    " The default is 4MB. If FlowFiles exceed this size, you should consider using another transport mechanism" +
                    " as gRPC isn't designed for heavy payloads.")
            .defaultValue("4MB")
            .required(false)
            .addValidator(StandardValidators.DATA_SIZE_VALIDATOR)
            .build();
    public static final PropertyDescriptor PROP_USE_SECURE = new PropertyDescriptor.Builder()
            .name("Use SSL/TLS")
            .displayName("Use TLS")
            .description("Whether or not to use TLS to send the contents of the gRPC messages.")
            .required(false)
            .defaultValue("false")
            .allowableValues("true", "false")
            .build();
    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
            .name("SSL Context Service")
            .description("The SSL Context Service used to provide client certificate information for TLS (https) connections.")
            .required(false)
            .identifiesControllerService(SSLContextService.class)
            .dependsOn(PROP_USE_SECURE, "true")
            .build();
    public static final PropertyDescriptor PROP_SEND_CONTENT = new PropertyDescriptor.Builder()
            .name("Send FlowFile Content")
            .description("Whether or not to include the FlowFile content in the FlowFileRequest to the gRPC service.")
            .required(false)
            .defaultValue("true")
            .allowableValues("true", "false")
            .build();
    public static final PropertyDescriptor PROP_PENALIZE_NO_RETRY = new PropertyDescriptor.Builder()
            .name("Penalize on \"No Retry\"")
            .description("Enabling this property will penalize FlowFiles that are routed to the \"No Retry\" relationship.")
            .required(false)
            .defaultValue("false")
            .allowableValues("true", "false")
            .build();
    public static final PropertyDescriptor PROP_OUTPUT_RESPONSE_REGARDLESS = new PropertyDescriptor.Builder()
            .name("Always Output Response")
            .description("Will force a response FlowFile to be generated and routed to the 'Response' relationship regardless of what the server status code received is "
                    + "or if the processor is configured to put the server response body in the request attribute. In the later configuration a request FlowFile with the "
                    + "response body in the attribute and a typical response FlowFile will be emitted to their respective relationships.")
            .required(false)
            .defaultValue("false")
            .allowableValues("true", "false")
            .build();
    public static final List<PropertyDescriptor> PROPERTIES = Collections.unmodifiableList(Arrays.asList(
            PROP_SERVICE_HOST,
            PROP_SERVICE_PORT,
            PROP_ROUTING_CLOUD_KEY,
            PROP_ROUTING_CLOUD_SECRET,
            PROP_MAX_MESSAGE_SIZE,
            PROP_USE_SECURE,
            PROP_SSL_CONTEXT_SERVICE,
            PROP_SEND_CONTENT,
            PROP_OUTPUT_RESPONSE_REGARDLESS,
            PROP_PENALIZE_NO_RETRY
    ));

    // relationships
    public static final Relationship REL_SUCCESS_REQ = new Relationship.Builder()
            .name("Success")
            .description("The original FlowFile will be routed upon success. It will have new attributes detailing the "
                    + "success of the request.")
            .build();
    /*public static final Relationship REL_RESPONSE = new Relationship.Builder()
            .name("Response")
            .description("A Response FlowFile will be routed upon success. If the 'Output Response Regardless' property "
                    + "is true then the response will be sent to this relationship regardless of the status code received.")
            .build();
    public static final Relationship REL_RETRY = new Relationship.Builder()
            .name("Retry")
            .description("The original FlowFile will be routed on any status code that can be retried. It will have new "
                    + "attributes detailing the request.")
            .build();*/
    /*public static final Relationship REL_NO_RETRY = new Relationship.Builder()
            .name("No Retry")
            .description("The original FlowFile will be routed on any status code that should NOT be retried.  "
                    + "It will have new attributes detailing the request.")
            .build();*/
    public static final Relationship REL_FAILURE = new Relationship.Builder()
            .name("Failure")
            .description("The original FlowFile will be routed on any type of connection failure, timeout or general exception. "
                    + "It will have new attributes detailing the request.")
            .build();
    public static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            REL_SUCCESS_REQ,
            /*REL_NO_RETRY,
            REL_RESPONSE,
            REL_RETRY,*/
            REL_FAILURE
    )));

    private static final String USER_AGENT_PREFIX = "NiFi_invokeGRPC";
    // NOTE: you may need to add the sources generated after running `maven clean compile` to your IDE
    // configured source directories. Otherwise, the classes generated when the proto is compiled won't
    // be accessible from here. For IntelliJ, open this module's settings and mark the following as source directories:
    //
    // * target/generated-sources/protobuf/grpc-java
    // * target/generated-sources/protobuf/java
    //private final AtomicReference<FlowFileServiceGrpc.FlowFileServiceBlockingStub> blockingStubReference = new AtomicReference<>();
    private static final AtomicReference<EdgeRpcServiceGrpc.EdgeRpcServiceStub> blockingStubReference = new AtomicReference<>();
    private static final AtomicReference<ManagedChannel> channelReference = new AtomicReference<>();
    private static StreamObserver<RequestMsg> inputStream;

    private static final ReentrantLock uplinkMsgLock = new ReentrantLock();


    private final static ConcurrentMap<Integer, Boolean> pendingMsgsMap = new ConcurrentHashMap<>();

    @Override
    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
        return PROPERTIES;
    }

    @Override
    public Set<Relationship> getRelationships() {
        return RELATIONSHIPS;
    }

    @Override
    protected Collection<ValidationResult> customValidate(ValidationContext context) {
        List<ValidationResult> results = new ArrayList<>(super.customValidate(context));

        final boolean useSecure = context.getProperty(PROP_USE_SECURE).asBoolean();
        final boolean sslContextServiceConfigured = context.getProperty(PROP_SSL_CONTEXT_SERVICE).isSet();

        if (useSecure && !sslContextServiceConfigured) {
            results.add(new ValidationResult.Builder()
                    .subject(PROP_SSL_CONTEXT_SERVICE.getDisplayName())
                    .valid(false)
                    .explanation(String.format("'%s' must be configured when '%s' is true", PROP_SSL_CONTEXT_SERVICE.getDisplayName(), PROP_USE_SECURE.getDisplayName()))
                    .build());
        }

        return results;
    }

    /**
     * Whenever this processor is triggered, we need to construct a client in order to communicate
     * with the configured gRPC service.
     *
     * @param context the processor context
     */
    @OnScheduled
    public void initializeClient(final ProcessContext context) {
        connect(context);
    }


    private static StreamObserver<ResponseMsg> initOutputStream(String edgeKey,
                                                                Consumer<UplinkResponseMsg> onUplinkResponse,Consumer<Exception> onError) {
        return new StreamObserver<ResponseMsg>() {
            @Override
            public void onNext(ResponseMsg responseMsg) {
                if (responseMsg.hasConnectResponseMsg()) {
                    ConnectResponseMsg connectResponseMsg = responseMsg.getConnectResponseMsg();
                    System.out.println("收到服务端连接回复消息" + connectResponseMsg);
                } else if (responseMsg.hasUplinkResponseMsg()) {
                    System.out.println("收到服务端响应消息" + responseMsg.getUplinkResponseMsg());
                    onUplinkResponse.accept(responseMsg.getUplinkResponseMsg());
                }
            }

            @Override
            public void onError(Throwable t) {
                onError.accept(new RuntimeException(t));
                System.out.println("断开了" + edgeKey);
            }

            @Override
            public void onCompleted() {
                release();
                System.out.println("[{}] Stream was closed and completed successfully!" + edgeKey);
            }
        };
    }

    protected static void release(){
        channelReference.set(null);
        blockingStubReference.set(null);
        inputStream = null;
        pendingMsgsMap.clear();
    }

    protected boolean isConnected() {
        return (inputStream != null && blockingStubReference != null && channelReference != null);
    }

    private void onError(final Exception e){
        release();
    }

    private void onUplinkResponse(UplinkResponseMsg msg) {
        try {
            if (msg.getSuccess()) {
                pendingMsgsMap.put(msg.getUplinkMsgId(), true);
                getLogger().info("服务端响应处理了消息---success" +  msg);
            } else {
                pendingMsgsMap.put(msg.getUplinkMsgId(), false);
                getLogger().info("服务端响应处理了消息---failure"+ msg.getErrorMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Perform cleanup prior to JVM shutdown
     *
     * @param context the processor context
     * @throws InterruptedException if there's an issue cleaning up
     */
    @OnShutdown
    public void shutdown(final ProcessContext context) throws InterruptedException {
        // close the channel
        /*final ManagedChannel channel = channelReference.get();
        if (channel != null) {
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        }*/
        disconnect();
    }


    public void connect(final ProcessContext context){
        channelReference.set(null);
        blockingStubReference.set(null);
        final ComponentLog logger = getLogger();

        final String host =  context.getProperty(PROP_SERVICE_HOST).getValue();
        final int port = context.getProperty(PROP_SERVICE_PORT).asInteger(); //7070;
        final String routingKey = context.getProperty(PROP_ROUTING_CLOUD_KEY).getValue();//"key55555";
        final String routingSecret = context.getProperty(PROP_ROUTING_CLOUD_SECRET).getValue();//"s55555";
        final int maxMessageSize = context.getProperty(PROP_MAX_MESSAGE_SIZE).asDataSize(DataUnit.B).intValue();
        String userAgent = USER_AGENT_PREFIX;
        try {
            userAgent += "_" + InetAddress.getLocalHost().getHostName();
        } catch (final UnknownHostException e) {
            logger.warn("Unable to determine local hostname. Defaulting gRPC user agent to {}.", new Object[]{USER_AGENT_PREFIX}, e);
        }

        final NettyChannelBuilder nettyChannelBuilder = NettyChannelBuilder.forAddress(host, port)
                // supports both gzip and plaintext, but will compress by default.
                .compressorRegistry(CompressorRegistry.getDefaultInstance())
                .decompressorRegistry(DecompressorRegistry.getDefaultInstance())
                .maxInboundMessageSize(maxMessageSize)
                .userAgent(userAgent);

        // configure whether or not we're using secure comms
        final boolean useSecure = context.getProperty(PROP_USE_SECURE).asBoolean();
        final SSLContextService sslContextService = context.getProperty(PROP_SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class);

        if (useSecure) {
            final SslContext clientSslContext = SslContextProvider.getSslContext(sslContextService, true);
            nettyChannelBuilder.sslContext(clientSslContext);
        } else {
            nettyChannelBuilder.usePlaintext();
        }

        final ManagedChannel channel = nettyChannelBuilder.build();
        //EdgeRpcServiceGrpc.EdgeRpcServiceBlockingStub stub = EdgeRpcServiceGrpc.newBlockingStub(channel);
        EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel);
        inputStream = stub.withCompression("gzip").handleMsgs(initOutputStream(routingKey, this::onUplinkResponse,this::onError));
        inputStream.onNext(RequestMsg.newBuilder()
                .setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE)
                .setConnectRequestMsg(ConnectRequestMsg.newBuilder()
                        .setEdgeRoutingKey(routingKey)
                        .setEdgeSecret(routingSecret)
                        .setEdgeVersion(EdgeVersion.V_3_6_0)
                        .setMaxInboundMessageSize(419820)
                        .build())
                .build());
        //final FlowFileServiceGrpc.FlowFileServiceBlockingStub blockingStub = FlowFileServiceGrpc.newBlockingStub(channel);
        channelReference.set(channel);
        blockingStubReference.set(stub);
    }

    public void disconnect()  {
        final ComponentLog logger = getLogger();
        if (inputStream != null ) {
            try {
                inputStream.onCompleted();
            } catch (Exception e) {
                logger.error("Exception during onCompleted", e);
            }
        }
        final ManagedChannel channel = channelReference.get();
        if (channel != null) {
            channel.shutdown();
            int attempt = 0;
            do {
                try {
                    channel.awaitTermination(5, TimeUnit.SECONDS);
                } catch (Exception e) {
                    logger.error("Channel await termination was interrupted", e);
                }
                if (attempt > 5) {
                    logger.warn("We had reached maximum of termination attempts. Force closing channel");
                    try {
                        channel.shutdownNow();
                    } catch (Exception e) {
                        logger.error("Exception during shutdownNow", e);
                    }
                    break;
                }
                attempt++;
            } while (!channel.isTerminated());
        }
        release();
    }


    @Override
    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {


        FlowFile fileToProcess = null;
        if (context.hasIncomingConnection()) {
            fileToProcess = session.get();

            // If we have no FlowFile, and all incoming connections are self-loops then we can continue on.
            // However, if we have no FlowFile and we have connections coming from other Processors, then
            // we know that we should run only if we have a FlowFile.
            if (fileToProcess == null && context.hasNonLoopConnection()) {
                return;
            }
        }


        if (!isConnected()) {
            synchronized (this) {
                if (!isConnected()) {
                    connect(context);
                }
            }
        }

        final ComponentLog logger = getLogger();
        final EdgeRpcServiceGrpc.EdgeRpcServiceStub blockingStub = blockingStubReference.get();
        final String host = context.getProperty(PROP_SERVICE_HOST).getValue();
        final String port = context.getProperty(PROP_SERVICE_PORT).getValue();
        fileToProcess = session.putAttribute(fileToProcess, SERVICE_HOST, host);
        fileToProcess = session.putAttribute(fileToProcess, SERVICE_PORT, port);
        FlowFile responseFlowFile = null;
        try {
            final org.apache.nifi.processors.grpc.FlowFileRequest.Builder requestBuilder = org.apache.nifi.processors.grpc.FlowFileRequest.newBuilder()
                    .setId(fileToProcess.getId())
                    .putAllAttributes(fileToProcess.getAttributes());

            // if the processor is configured to send the content, turn the content into bytes
            // and add it to the request.
            final boolean sendContent = context.getProperty(PROP_SEND_CONTENT).asBoolean();
            String content = "";
            if (sendContent) {
                try (final InputStream contents = session.read(fileToProcess)) {
                    requestBuilder.setContent(ByteString.readFrom(contents));
                    content = requestBuilder.getContent().toStringUtf8();
                    getLogger().info("send data content " + content);
                }
                // emit provenance event
                session.getProvenanceReporter().send(fileToProcess, getRemote(host, port), true);
            }
            final org.apache.nifi.processors.grpc.FlowFileRequest flowFileRequest = requestBuilder.build();
            logRequest(logger, host, port, flowFileRequest);

            //final FlowFileReply flowFileReply = blockingStub.send(flowFileRequest);
            //logReply(logger, host, port, flowFileReply);

            org.apache.nifi.processors.grpc.FlowFileReply.ResponseCode responseCode = org.apache.nifi.processors.grpc.FlowFileReply.ResponseCode.SUCCESS;//flowFileReply.getResponseCode();
            final String body = "SUCCESS";//flowFileReply.getBody();

            fileToProcess = session.putAttribute(fileToProcess, RESPONSE_CODE, String.valueOf(responseCode));
            fileToProcess = session.putAttribute(fileToProcess, RESPONSE_BODY, body);

            //responseFlowFile = session.create(fileToProcess);

            ObjectMapper mapper = new ObjectMapper();
            Map jsonData = new HashMap();
            Map newContent = new HashMap();
            newContent.put("collectData",content);
            jsonData.put("data", mapper.readTree(mapper.writeValueAsString(newContent)));
            Gson gson = new GsonBuilder().create();
            content = gson.toJson(jsonData);
            JsonNode actualObj = mapper.readTree(content);
            UplinkMsg msg = buildUpLinkMsg(actualObj);
            RequestMsg requestMsg = RequestMsg.newBuilder()
                    .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
                    .setUplinkMsg(msg)
                    .build();
            final StopWatch stopWatch = new StopWatch(true);
            uplinkMsgLock.lock();
            try {
                inputStream.onNext(requestMsg);
            } catch (Exception e){
                logger.error("GRPC send msg to server {} due to exception: {}", host + ":" + port, e);
                logger.info("GRPC try reconnect to server {} ", host + ":" + port);
                release();
                logger.error("Routing to {} due to exception: {}", new Object[]{REL_FAILURE.getName(), e}, e);
                fileToProcess = session.penalize(fileToProcess);
                fileToProcess = session.putAttribute(fileToProcess, EXCEPTION_CLASS, e.getClass().getName());
                fileToProcess = session.putAttribute(fileToProcess, EXCEPTION_MESSAGE, e.getMessage());
                // transfer original to failure
                session.transfer(fileToProcess, REL_FAILURE);
            } finally {
                uplinkMsgLock.unlock();
            }
            pendingMsgsMap.put(msg.getUplinkMsgId(),false);

            Thread.sleep(500);

            if(pendingMsgsMap.get(msg.getUplinkMsgId())){
                responseCode = org.apache.nifi.processors.grpc.FlowFileReply.ResponseCode.SUCCESS;
            } else {
                responseCode = org.apache.nifi.processors.grpc.FlowFileReply.ResponseCode.ERROR;
            }
            session.getProvenanceReporter().send(fileToProcess, getRemote(host, port), stopWatch.getElapsed(TimeUnit.MILLISECONDS));
            route(fileToProcess, responseFlowFile, session, context, responseCode);
            pendingMsgsMap.remove(msg.getUplinkMsgId());

        } catch (final Exception e) {
            release();
            // penalize or yield
            if (fileToProcess != null) {
                logger.error("Routing to {} due to exception: {}", new Object[]{REL_FAILURE.getName(), e}, e);
                fileToProcess = session.penalize(fileToProcess);
                fileToProcess = session.putAttribute(fileToProcess, EXCEPTION_CLASS, e.getClass().getName());
                fileToProcess = session.putAttribute(fileToProcess, EXCEPTION_MESSAGE, e.getMessage());
                // transfer original to failure
                session.transfer(fileToProcess, REL_FAILURE);
            } else {
                logger.error("Yielding processor due to exception encountered as a source processor: {}", e);
                context.yield();
            }

            // cleanup
            /*try {
                if (responseFlowFile != null) {
                    session.remove(responseFlowFile);
                }
            } catch (final Exception e1) {
                logger.error("Could not cleanup response flowfile due to exception: {}", new Object[]{e1}, e1);
            }*/
        }
    }

    private UplinkMsg buildUpLinkMsg(JsonNode jsonNode) throws JsonProcessingException {
        EntityType entityType = EntityType.DEVICE;
        UUID entityId = UUID.randomUUID();
        EntityDataProto entityDataProto = EdgeUtils.convertTelemetryEventToEntityDataProto(entityId, entityType,jsonNode);
        return  UplinkMsg.newBuilder()
                .setUplinkMsgId(EdgeUtils.nextPositiveInt())
                .addEntityData(entityDataProto)
                .build();
    }



    /**
     * Route the {@link FlowFile} request and response appropriately, depending on the gRPC service
     * response code.
     *
     * @param request      the flowfile request
     * @param response     the flowfile response
     * @param session      the processor session
     * @param context      the processor context
     * @param responseCode the gRPC service response code
     */
    private void route(FlowFile request, FlowFile response, final ProcessSession session, final ProcessContext context, final org.apache.nifi.processors.grpc.FlowFileReply.ResponseCode responseCode) {
        /*boolean responseSent = false;
        if (context.getProperty(PROP_OUTPUT_RESPONSE_REGARDLESS).asBoolean()) {
            session.transfer(response, REL_RESPONSE);
            responseSent = true;
        }*/

        switch (responseCode) {
            // if the rpc failed, transfer flowfile to no retry relationship and penalize flowfile


            // if the rpc succeeded, transfer the request and response flowfiles
            case SUCCESS:
                session.transfer(request, REL_SUCCESS_REQ);
                getLogger().info("Flowfile {} transfer to {}",request.getId(),REL_SUCCESS_REQ);
                /*if (!responseSent) {
                    session.transfer(response, REL_RESPONSE);
                }*/
                break;

            // if the gRPC service responded requesting a retry, then penalize the request and
            // transfer it to the retry relationship. The flowfile contains attributes detailing this
            // rpc request.
            /*case RETRY:
                request = session.penalize(request);
                session.transfer(request, REL_RETRY);
                // if we haven't sent the response by this point, clean it up.
                if (!responseSent) {
                    session.remove(response);
                }
                break;*/

            case ERROR:
                session.transfer(request, REL_FAILURE);
                getLogger().info("Flowfile {} transfer to {}",request.getId(),REL_FAILURE);
                /*if (!responseSent) {
                    session.transfer(response, REL_RESPONSE);
                }*/
                break;
            case UNRECOGNIZED: // unrecognized response code returned from gRPC service
                session.transfer(request, REL_FAILURE);
                getLogger().info("Flowfile {} transfer to {}",request.getId(),REL_FAILURE);
                /*if (!responseSent) {
                    session.transfer(response, REL_RESPONSE);
                }*/
                break;
            default:
                final boolean penalize = context.getProperty(PROP_PENALIZE_NO_RETRY).asBoolean();
                if (penalize) {
                    request = session.penalize(request);
                }
                session.transfer(request, REL_FAILURE);
                getLogger().info("Flowfile {} transfer to {}",request.getId(),REL_FAILURE);
                // if we haven't sent the response by this point, clean it up.
                /*if (!responseSent) {
                    session.remove(response);
                }*/
                break;
        }
    }

    private String getRemote(final String host, final String port) {
        return host + ":" + port;
    }

    private void logRequest(final ComponentLog logger, final String host, final String port, final org.apache.nifi.processors.grpc.FlowFileRequest flowFileRequest) {
        logger.debug("\nRequest to remote service:\n\t{}\n{}",
                getRemote(host, port), flowFileRequest.toString());
    }

    private void logReply(final ComponentLog logger, final String host, final String port, final org.apache.nifi.processors.grpc.FlowFileReply flowFileReply) {
        logger.debug("\nResponse from remote service:\n\t{}\n{}",
                getRemote(host, port), flowFileReply.toString());
    }
}

核心代码说明

PropertyDescriptor 即页面展示的配置项,到时候processor 运行的时候,加载的配置项。

onTrigger 方法,即自定义processor的处理入口,真正的数据处理和发送逻辑。

initializeClient 方法,初始化grpc 连接等。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gzcsschen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值