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

org.joyqueue.api.Impl.OpenAPIServiceImpl Maven / Gradle / Ivy

/**
 * Copyright 2019 The JoyQueue Authors.
 *
 * Licensed 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.joyqueue.api.Impl;


import org.joyqueue.api.OpenAPIService;
import org.joyqueue.convert.CodeConverter;
import org.joyqueue.exception.ServiceException;
import org.joyqueue.model.ListQuery;
import org.joyqueue.model.PageResult;
import org.joyqueue.model.Pagination;
import org.joyqueue.model.QPageQuery;
import org.joyqueue.model.domain.Application;
import org.joyqueue.model.domain.ApplicationToken;
import org.joyqueue.model.domain.ApplicationUser;
import org.joyqueue.model.domain.BaseModel;
import org.joyqueue.model.domain.Broker;
import org.joyqueue.model.domain.BrokerGroup;
import org.joyqueue.model.domain.BrokerMonitorRecord;
import org.joyqueue.model.domain.Consumer;
import org.joyqueue.model.domain.Identity;
import org.joyqueue.model.domain.Namespace;
import org.joyqueue.model.domain.PartitionOffset;
import org.joyqueue.model.domain.Producer;
import org.joyqueue.model.domain.SlimApplication;
import org.joyqueue.model.domain.SlimTopic;
import org.joyqueue.model.domain.Subscribe;
import org.joyqueue.model.domain.SubscribeType;
import org.joyqueue.model.domain.Topic;
import org.joyqueue.model.domain.TopicPubSub;
import org.joyqueue.model.domain.User;
import org.joyqueue.model.exception.BusinessException;
import org.joyqueue.model.query.QBroker;
import org.joyqueue.model.query.QBrokerGroup;
import org.joyqueue.model.query.QTopic;
import org.joyqueue.monitor.PartitionAckMonitorInfo;
import org.joyqueue.monitor.PartitionLeaderAckMonitorInfo;
import org.joyqueue.monitor.PendingMonitorInfo;
import org.joyqueue.service.ApplicationService;
import org.joyqueue.service.ApplicationTokenService;
import org.joyqueue.service.ApplicationUserService;
import org.joyqueue.service.BrokerGroupService;
import org.joyqueue.service.BrokerMonitorService;
import org.joyqueue.service.BrokerService;
import org.joyqueue.service.ConsumeOffsetService;
import org.joyqueue.service.ConsumerService;
import org.joyqueue.service.LeaderService;
import org.joyqueue.service.ProducerService;
import org.joyqueue.service.TopicService;
import org.joyqueue.sync.ApplicationInfo;
import org.joyqueue.sync.SyncService;
import org.joyqueue.util.LocalSession;
import org.joyqueue.util.NullUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Service("openAPIService")
public class OpenAPIServiceImpl implements OpenAPIService {
    private final Logger logger = LoggerFactory.getLogger(OpenAPIServiceImpl.class);
    @Autowired
    private TopicService topicService;

    @Autowired
    private ConsumerService consumerService;

    @Autowired
    private ProducerService producerService;

    @Autowired
    private SyncService syncService;

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private BrokerGroupService brokerGroupService;

    @Autowired
    private BrokerService brokerService;

    @Autowired
    private ConsumeOffsetService consumeOffsetService;

    @Autowired
    private BrokerMonitorService brokerMonitorService;

    @Autowired
    private LeaderService leaderService;

    @Autowired
    private ApplicationTokenService applicationTokenService;
    @Autowired
    private ApplicationUserService applicationUserService;
    private Random random = new Random();
    private static final long MINUTES_MS = 60 * 1000;


    @Override
    public PageResult findTopicPubSubInfo(Pagination pagination) throws Exception {
        QPageQuery qPageQuery = new QPageQuery();
        qPageQuery.setQuery(new QTopic()); //empty
        qPageQuery.setPagination(pagination);
        PageResult topicPageResult = topicService.search(qPageQuery);
        List topics = topicPageResult.getResult();
        List pubSubs = new ArrayList(topics.size());
        for (Topic topic : topics) {
            try {
                pubSubs.add(findTopicPubsub(topic));
            }catch(Exception e){
                logger.error(String.format("Find Topic PubSub Info, topic:%s", topic.getName()), e);
            }
        }
        PageResult topicPubSubPageResult = new PageResult<>();
        topicPubSubPageResult.setPagination(topicPageResult.getPagination());
        topicPubSubPageResult.setResult(pubSubs);
        return topicPubSubPageResult;
    }


    /**
     * @return topic pub/sub info
     **/
    TopicPubSub findTopicPubsub(Topic topic) throws Exception {
        List consumers = consumerService.findByTopic(topic.getCode(), topic.getNamespace().getCode());
        List producers = producerService.findByTopic(topic.getNamespace().getCode(), topic.getCode());
        TopicPubSub pubSub = new TopicPubSub();

        List ips = new ArrayList<>();
        List brokers = leaderService.findLeaderBroker(topic.getCode(), topic.getNamespace().getCode());
        if (!NullUtil.isEmpty(brokers)) {
            String iport;
            for (Broker b : brokers) {
                iport = b.getIp() + ":" + b.getPort();
                ips.add(iport);
            }
        }
        SlimTopic slimTopic = new SlimTopic();
        slimTopic.setIps(ips);
        slimTopic.setCode(topic.getCode());
        pubSub.setTopic(slimTopic);
        List consumerList = new ArrayList<>();
        Identity identity;
        for (Consumer consumer : consumers) {
            identity = consumer.getApp();
            if (!NullUtil.isEmpty(identity)) {
                consumerList.add(String.valueOf(identity.getCode()));
            }
        }
        pubSub.setConsumers(appsToApplication(consumerList));
        List producerList = new ArrayList<>();
        for (Producer producer : producers) {
            identity = producer.getApp();
            if (!NullUtil.isEmpty(identity)) {
                producerList.add(String.valueOf(identity.getCode()));
            }
        }
        pubSub.setProducers(appsToApplication(producerList));
        return pubSub;
    }

    @Override
    public TopicPubSub findTopicPubSubInfo(String topic, String namespace) throws Exception {
        Topic topiC = new Topic();
        topiC.setCode(topic);
        topiC.setNamespace(new Namespace());
        topiC.getNamespace().setCode(namespace);
        return findTopicPubsub(topiC);
    }

    @Override
    public List queryConsumerTopicByApp(String app) throws Exception {
        return consumerService.findByApp(app);
    }

    @Override
    public List findConsumers(String topic, String namespace) throws Exception {
        List consumers = consumerService.findByTopic(topic, namespace);
        return consumers;
    }

    @Override
    public List findProducers(String topic, String namespace) throws Exception {
        List producers = producerService.findByTopic(namespace, topic);
        return producers;
    }

    @Override
    public Producer publish(Producer producer) throws Exception {
        Topic topic = topicService.findByCode(producer.getNamespace().getCode(), producer.getTopic().getCode());
        Application application = applicationService.findByCode(producer.getApp().getCode());
        if (NullUtil.isEmpty(topic) || NullUtil.isEmpty(application)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format("topic %s or app %s not exist!", producer.getTopic().getCode(), producer.getApp().getCode()));
        }
        producer.setTopic(topic);
        producer.setApp(new Identity(producer.getApp().getCode()));
        producerService.add(producer);
        return producerService.findByTopicAppGroup(producer.getNamespace().getCode(), producer.getTopic().getCode(), producer.getApp().getCode());
    }

    @Override
    public Consumer subscribe(Consumer consumer) throws Exception {
        Topic topic = topicService.findByCode(consumer.getNamespace().getCode(), consumer.getTopic().getCode());
        Application application = applicationService.findByCode(consumer.getApp().getCode());
        if (NullUtil.isEmpty(topic) || NullUtil.isEmpty(application)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format("topic %s or app %s not exist!", consumer.getTopic().getCode(), consumer.getApp().getCode()));
        }
        consumer.setTopic(topic);
        consumer.setNamespace(consumer.getNamespace());
        consumer.setApp(consumer.getApp());
        consumerService.add(consumer);
        return consumerService.findByTopicAppGroup(consumer.getNamespace().getCode(), consumer.getTopic().getCode(), consumer.getApp().getCode(), consumer.getSubscribeGroup());
    }

    @Override
    public boolean unPublish(Producer producer) throws Exception {
        List producers = findProducers(producer.getTopic().getCode(), producer.getNamespace().getCode());
        List consumers = findConsumers(producer.getTopic().getCode(), producer.getNamespace().getCode());
        if (NullUtil.isEmpty(producers) || (producers.size() == 1 && consumers.size() > 0)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format("no subscribe or please unSubscribe all the consumers of topic %s before cancel publish",
                    producer.getTopic().getCode()));
        }
        Producer p = findProducer(producers, producer.getApp().getCode());
        if (NullUtil.isEmpty(p))
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format(" %s haven't publish to the topic %s ",
                    producer.getApp().getCode(), CodeConverter.convertTopic(producer.getNamespace(), producer.getTopic()).getFullName()));

        return producerService.delete(p) > 0 ? true : false;
    }

    @Override
    public Consumer uniqueSubscribe(Consumer consumer) throws Exception {
        String namespace = consumer.getNamespace() == null ? null : consumer.getNamespace().getCode();
        Topic topic = topicService.findByCode(namespace, consumer.getTopic().getCode());
        Application application = applicationService.findByCode(consumer.getApp().getCode());
        if (NullUtil.isEmpty(topic) || NullUtil.isEmpty(application)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format("topic %s or app %s not exist!", consumer.getTopic().getCode(), consumer.getApp().getCode()));
        }
        User user = LocalSession.getSession().getUser();
        ApplicationUser applicationUser = applicationUserService.findByUserApp(user.getCode(), consumer.getApp().getCode());
        if (NullUtil.isEmpty(applicationUser)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format("user %s app %s no permission!", user.getCode(), consumer.getApp().getCode()));
        }
        Consumer exist = consumerService.findByTopicAppGroup(namespace, consumer.getTopic().getCode(), consumer.getApp().getCode(), consumer.getSubscribeGroup());
        if (NullUtil.isNotEmpty(exist) && NullUtil.isNotEmpty(exist.getSubscribeGroup())) {
            return exist;
        }
        int group = random.nextInt((int) MINUTES_MS);
        consumer.setSubscribeGroup(String.valueOf(group));
        consumer.setTopic(topic);
        consumer.setNamespace(consumer.getNamespace());
        consumer.setApp(consumer.getApp());
        consumerService.add(consumer);
        return consumerService.findByTopicAppGroup(namespace, consumer.getTopic().getCode(), consumer.getApp().getCode(), consumer.getSubscribeGroup());
    }

    /**
     *  find the app producer
     *
     **/
    Producer findProducer(List producers, String app) {
        for (Producer p : producers) {
            if (p.getApp().getCode().equals(app)) {
                return p;
            }
        }
        return null;
    }

    @Override
    public boolean unSubscribe(Consumer consumer) throws Exception {
        Consumer c = consumerService.findByTopicAppGroup(consumer.getNamespace().getCode(), consumer.getTopic().getCode(),
                consumer.getApp().getCode(), consumer.getSubscribeGroup());
        if (NullUtil.isEmpty(c))
            throw new ServiceException(ServiceException.BAD_REQUEST, String.format(" %s haven't subscribe to the topic %s ",
                    CodeConverter.convertApp(new Identity(consumer.getApp().getCode()), consumer.getSubscribeGroup()),
                    CodeConverter.convertTopic(consumer.getNamespace(), consumer.getTopic()).getFullName()));
        // check pending message

        return consumerService.delete(c) > 0 ? true : false;
    }

    @Override
    public Application syncApplication(Application application) throws Exception {
        User user = LocalSession.getSession().getUser();
        application.setErp(user.getCode());
        ApplicationInfo info = syncService.syncApp(application);
        if (NullUtil.isEmpty(info) || NullUtil.isEmpty(user)) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "sync application failed or illegal erp " + application.getErp());
        }
        info.setUser(new Identity(user));
        syncService.addOrUpdateApp(info);
        return applicationService.findByCode(info.getCode());
    }

    @Override
    public boolean delApplication(Application application) throws Exception {
        if (!NullUtil.isEmpty(consumerService.findByApp(application.getCode())) || !NullUtil.isEmpty(producerService.findByApp(application.getCode())))
            throw new ServiceException(ServiceException.BAD_REQUEST, "please unSubscribe/Publish  all  topics you have !");
        application = applicationService.findByCode(application.getCode());
        return applicationService.delete(application) > 0 ? true : false;
    }

    @Override
    public Topic createTopic(Topic topic, QBrokerGroup brokerGroup, Identity operator) throws Exception {
//        topic.setElectType(PartitionGroup.ElectType.raft);
        List brokers = allocateBrokers(topic, brokerGroup);
        if (brokers.size() == 0) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "select broker is empty");
        }

        //计算总数
        topic.setPartitions(topic.getPartitions() * brokers.size());
        topic.setBrokers(brokers);
        topicService.addWithBrokerGroup(topic, topic.getBrokerGroup(), topic.getBrokers(), operator);
        return topicService.findById(topic.getId());
    }

    public void removeTopic(String namespace, String topicCode) throws Exception {
        Topic topic = topicService.findByCode(namespace, topicCode);
        if (topic == null) {
            throw new BusinessException("topic is not exist");
        }
        topicService.delete(topic);
    }

    /**
     * Random allocate broker group and broker  for topic
     **/
    List allocateBrokers(Topic topic, QBrokerGroup qBrokerGroup) throws Exception {
        //校验分组是否存在
        qBrokerGroup.setRole(0);//1管理员 0 普通用户
        List brokerGroupList = brokerGroupService.findByQuery(new ListQuery<>(qBrokerGroup));
        if (brokerGroupList == null || brokerGroupList.size() == 0) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "broker group is empty");
        }
        BrokerGroup brokerGroup = brokerGroupList.get(0);
        topic.setBrokerGroup(brokerGroup); // broker group

        QBroker qBroker = new QBroker();
        qBroker.setGroup(new Identity(brokerGroup.getId(), brokerGroup.getCode()));
        List brokers = brokerService.queryBrokerList(qBroker);

        if (brokers.size() == 0) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "select broker is empty");
        }
        //如果用户设置broker数量,则校验broker数量是否能满足
        if (topic.getBrokerNum() != 0 && topic.getBrokerNum() > brokers.size()) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "实际可用broker数量小于指定broker数量");
        }
        //如果用户没设置broker数量 默认是3个
        if (topic.getBrokerNum() == 0) {
            topic.setBrokerNum(3);
        }

        Random random = new Random();
        List selectBroker = new ArrayList<>();
        int startId = random.nextInt(brokers.size());
        int endId = startId + topic.getBrokerNum();
        for (int i = startId; i < endId; i++) {
            Broker broker = brokers.get(i % brokers.size());
            if (selectBroker.contains(broker)) {
                continue;
            }
            selectBroker.add(broker);
        }
        return selectBroker;
    }

    @Override
    public List findOffsets(Subscribe subscribe) {
        List partitionAckMonitorInfos = new ArrayList<>();
        subscribe.setType(SubscribeType.CONSUMER);
        isLegalSubscribe(subscribe);
        List partitionLeaderAckMonitorInfos = consumeOffsetService.offsets(subscribe);
        for (PartitionLeaderAckMonitorInfo p : partitionLeaderAckMonitorInfos) {
            if (p.isLeader()) {
                partitionAckMonitorInfos.add(p);
            }
        }
        return partitionAckMonitorInfos;
    }

    @Override
    public boolean resetOffset(Subscribe subscribe, short partition, long offset) {
        subscribe.setType(SubscribeType.CONSUMER);
        isLegalSubscribe(subscribe);
        return consumeOffsetService.resetOffset(subscribe, partition, offset);
    }

    @Override
    public List timeOffset(Subscribe subscribe, long timeMs) {
        return consumeOffsetService.timeOffset(subscribe, timeMs);
    }

    @Override
    public boolean resetOffset(Subscribe subscribe, long timeMs) {
        subscribe.setType(SubscribeType.CONSUMER);
        isLegalSubscribe(subscribe);
        return consumeOffsetService.resetOffset(subscribe, timeMs);
    }

    @Override
    public boolean resetOffset(Subscribe subscribe, List offsets) {
        subscribe.setType(SubscribeType.CONSUMER);
        isLegalSubscribe(subscribe);
        return consumeOffsetService.resetOffset(subscribe, offsets);
    }

    @Override
    public PendingMonitorInfo pending(Subscribe subscribe) {
        subscribe.setType(SubscribeType.CONSUMER);
        isLegalSubscribe(subscribe);
        BrokerMonitorRecord record = brokerMonitorService.find(subscribe, true);
        if (NullUtil.isEmpty(record) || NullUtil.isEmpty(record.getPending())) {
            throw new ServiceException(ServiceException.INTERNAL_SERVER_ERROR, "data not found");
        }
        return record.getPending();
    }

    @Override
    public int queryPartitionByTopic(String namespaceCode, String topicCode) throws Exception {
        Topic topic = topicService.findByCode(namespaceCode, topicCode);
        return topic.getPartitions();
    }


    /**
     *
     * @param apps  can't be null
     *
     **/
    List appsToApplication(List apps) {
        if (NullUtil.isEmpty(apps)) return null;
        List applications = applicationService.findByCodes(apps);
        List slimApplications = new ArrayList<>();
        SlimApplication slimApplication;
        for (Application a : applications) {
            slimApplication = new SlimApplication();
            slimApplication.setCode(a.getCode());
            slimApplication.setOwner(a.getOwner());
            slimApplication.setDepartment(a.getDepartment());
            slimApplications.add(slimApplication);
        }
        return slimApplications;
    }


    @Override
    public List add(ApplicationToken token) {
        String app = token.getApplication().getCode();
        Application application = applicationService.findByCode(app);
        if (NullUtil.isEmpty(application) || application.getStatus() == BaseModel.DELETED) {
            throw new ServiceException(ServiceException.BAD_REQUEST, "app not exist");
        }
        token.setApplication(new Identity(application));
        try {
            applicationTokenService.add(token);
            return tokens(app);
        } catch (Exception e) {
            throw new ServiceException(ServiceException.INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

    @Override
    public List tokens(String app) {
        try {
            return applicationTokenService.findByApp(app);
        } catch (Exception e) {
            throw new ServiceException(ServiceException.INTERNAL_SERVER_ERROR, e.getMessage());
        }

    }

    /**
     *
     *  Check the subscription legal or not
     *
     *  @return true if exist
     *
     **/
    private boolean isLegalSubscribe(Subscribe subscribe) {
        if (subscribe.getType() == SubscribeType.CONSUMER) {
            Consumer c = consumerService.findByTopicAppGroup(subscribe.getNamespace().getCode(), subscribe.getTopic().getCode(),
                    subscribe.getApp().getCode(), subscribe.getSubscribeGroup());
            if (NullUtil.isEmpty(c))
                throw new ServiceException(ServiceException.BAD_REQUEST, String.format(" %s haven't subscribe the topic %s ",
                        CodeConverter.convertApp(subscribe.getApp(), subscribe.getSubscribeGroup()), CodeConverter.convertTopic(subscribe.getNamespace(), subscribe.getTopic()).getFullName()));
        } else {
            Producer producer = producerService.findByTopicAppGroup(subscribe.getNamespace().getCode(), subscribe.getTopic().getCode(), subscribe.getApp().getCode());
            if (NullUtil.isEmpty(producer)) {
                throw new ServiceException(ServiceException.BAD_REQUEST, String.format(" %s haven't publish the topic %s ",
                        subscribe.getApp().getCode(), CodeConverter.convertTopic(subscribe.getNamespace(), subscribe.getTopic()).getFullName()));
            }
        }
        return true;
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy