项目下载中心-超简单版解决方案

简单的下载中心的设计流程

直接上设计流程:
项目下载中心-超简单版解决方案_第1张图片

以上就是步骤了,至于每个步骤怎么实现,那方法就很多了 ,随意,达到目的就行。

至于各种问题,比如队列性能,消息重复或丢失,等等,碰到的再说 再处理吧 ╮(╯_╰)╭ 反正这是超简单的版本 不想了

以下是,我使用的实现方式,可以参考。【仅参考,毕竟大家的需求不同】


MQ 搭建-RocketMQ

这里的队列用了RocketMQ,至于为什么用这个,理由是这个我以前没玩过 ,试试(~ ̄▽ ̄)~

项目用的是spring-cloud-alibaba,所以还是用它的东西试试吧。

参考资料:

  • RocketMQ官方:https://rocketmq.apache.org/docs/quick-start/
  • springCloud官方:https://spring.io/projects/spring-cloud-alibaba/
  • https://blog.csdn.net/m0_46689235/article/details/120945490
  • https://blog.51cto.com/zhangxueliang/2984247

Start Name Server

docker pull rocketmqinc/rocketmq:latest 
docker run -d -p 9876:9876 -v /u01/logs/rocketmq/namesrv/logs:/root/logs -v     /u01/date/rocketmq/namesrv/store:/root/store --name rmqnamesrv rocketmqinc/rocketmq:latest sh mqnamesrv

Start Broker


创建broker配置文件(可能需要切换全新 mac[sudo -s])
mkdir -p /u01/docker/rocketmq/conf //递归创建文件
cd /u01/docker/rocketmq/conf  //进入文件位置
vim broker.conf (通过vim编辑文件 没有会自动创建)

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# brokerIP1 = 127.0.0.1
brokerIP1 = 192.168.0.198

通过指定broker.conf配置文件来启动容器
docker run -d -p 10911:10911 -p 10909:10909 -v  /u01/logs/rocketmq/broker/logs:/root/logs -v  /u01/date/rocketmq/broker/store:/root/store -v  /u01/docker/rocketmq/conf/broker.conf:/opt/rocketmq-latest/conf/broker.conf --name rmqbroker --link rmqnamesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq:latest sh mqbroker -c /opt/rocketmq-latest/conf/broker.conf
	

start look View

# 下面这个 选一个就可以了 没啥差别 
docker pull pangliang/rocketmq-console-ng //拉取镜像
docker run -d -e "JAVA_OPTS=-Drocketmq.namesrv.addr=172.17.192.173:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8082:8080 -t pangliang/rocketmq-console-ng:latest
# 访问:http://192.168.0.198:8082/#/ops

docker pull styletang/rocketmq-console-ng:1.0.0
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=172.16.55.185:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8082:8080 -t styletang/rocketmq-console-ng:1.0.0

小结

MQ就只是个MQ,在这个方案里面就只传递一个消息,异步一下,控制一下处理速度就行,至于别用其他的功能无所谓了。
所以用别的RabbitMQkafka 也都是可以了,没差,这里用不到多少功能与性能。
除非系统的量级大上去了。那,时候这个简单的方案也不能用了不是 ╮(╯_╰)╭


MQ使用与简单分发

rocketMQ本身的发送,就没什么要说的了,官方等等 导出都有栗子,这里直接就 贴 这个方案用到的。

参考:

  • spring 测试类:https://www.jb51.net/article/233246.htm
  • https://github.com/alibaba/spring-cloud-alibaba/blob/2.2.x/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md

发送下载消息

@Component
public class DownloadMessageSendUtil {
... 精简已来一些代码了
    private static String NAMW_SERVER; // 名称服务地址
    private static String PRODUCER_NAME; // 生产者注册名称
    private static String LISTENER_TOPIC; //监听 的topic 名称

    private static DefaultMQProducer producer = null;

    private static DefaultMQProducer init() throws MQClientException {
        if (producer == null) {
            producer = new
                    DefaultMQProducer(PRODUCER_NAME);
            producer.setNamesrvAddr(NAMW_SERVER);
            producer.start();
        }
        return producer;
    }

	// 简单发送MQ消息
    public static SendResult simpleSend(String message,IDownloadResourceService downloadResourceService) throws Exception {
        DefaultMQProducer producer = init();
        Message msg = new Message(LISTENER_TOPIC,
                "TagA",
                (message).getBytes(RemotingHelper.DEFAULT_CHARSET)
        );
        MsgInfo msgInfoId = JSON.parseObject(message, MsgInfo.class);
        SendResult sendResult = producer.send(msg);
        downloadResourceService.update(new LambdaUpdateWrapper<DownloadResource>()
                .eq(DownloadResource::getId,msgInfoId.getId())
                .set(DownloadResource::getMessageId,sendResult.getMsgId()));
        System.out.printf("%s%n", sendResult);
        return sendResult;
    }

	// 创建下载中心基础数据对象,这个就是那个文件数据表了,这个只是方便创建数据而已 
    public static DownloadResource createDownloadResource(int belongType, Agent agent, Long id, String createBy) {
        DownloadResource downloadResource = new DownloadResource();
        downloadResource.setBelongType(belongType);
        downloadResource.setSysId(agent.getSysId());
        。。。
        return downloadResource;
    }

    /**  发送消息 对外使用 */
    public static AjaxResult sendDownloadMsg(IDownloadResourceService downloadResourceService, DownloadResource downloadResource, String downloadServiceName, Object paramObject) throws Exception {
        // 插入下载资源表数据
        MsgInfo msgInfo = new MsgInfo();
        String paramObjectJson = JSONObject.toJSONString(paramObject);
        msgInfo.setDownloadServiceName(downloadServiceName);
        msgInfo.setParamJson(paramObjectJson);
        downloadResource.setParamJson(JSON.toJSONString(msgInfo));
        downloadResourceService.save(downloadResource);
        msgInfo.setId(downloadResource.getId());
        //消息拦截
        AjaxResult ajaxResult = intercept(downloadResourceService, downloadResource);
        if (Integer.parseInt(ajaxResult.get("code").toString()) == 200){
            // 发送MQ消息
            String msgInfoJson = JSONObject.toJSONString(msgInfo);
            simpleSend(msgInfoJson,downloadResourceService);
        }
        return ajaxResult;
    }

    /** 消息拦截 做一些限制 不嫌麻烦这里可以做各种的限制*/
    public static AjaxResult intercept(IDownloadResourceService downloadResourceService, DownloadResource downloadResource){
        //下载中心表的存放的参数
        List<String> paramsList = new ArrayList<>();
        int count = downloadResourceService.count(new LambdaQueryWrapper<DownloadResource>()
                .eq(DownloadResource::getAgentId, downloadResource.getAgentId())
                .eq(DownloadResource::getSysId, downloadResource.getSysId())
                .eq(DownloadResource::getFileType, downloadResource.getFileType())
                //未完成和已完成需要进行判断
                .eq(DownloadResource::getStatus, 0)
                //取时间为今天的数据
                .apply("date_format(create_time,'%Y-%m-%d') = {0}", DateUtil.today())
        );
        //未完成超过十条直接返回失败
        if(count>=10){
            return AjaxResult.error("当前代理商未完成的下载数量不能超过10条");
        }
        return  AjaxResult.success("导出成功,请至下载中心进行下载至本地");
    }

}

监听以及分发【主要】

@Component
public class DownLoadBusinessBase {
... 精简的一些代码了
    private static String NAMW_SERVER; // 名称服务地址
    private static String PRODUCER_NAME; // 生产者注册名称
    private static String LISTENER_TOPIC; //监听 的topic 名称

    public void run() throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_NAME);
        consumer.setNamesrvAddr(NAMW_SERVER);
        consumer.subscribe(LISTENER_TOPIC, "*");
        // 现在消息监听处理的线程池配置
        consumer.setConsumeThreadMax(1);
        consumer.setConsumeThreadMin(1);
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @SneakyThrows
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                            ConsumeConcurrentlyContext context) {
                // 这里就是监听的方法了,下面是具体怎么处理消息的
                System.out.println("=================msgs.size:"+msgs.size());
                System.out.printf("%s Receive New Messages: %s %n %n", Thread.currentThread().getName(), msgs,context);
                Long downloadId = null;
                try {
                    // 这里需要分发的操作 。。。demo 当做一条数据处理
                    String body = new String(msgs.get(0).getBody());
                    MsgInfo msgInfo = JSONObject.parseObject(body, MsgInfo.class);
                    downloadId = msgInfo.getId();

                    // 判断下载业务是否为待处理  并更新为处理中
                    DownloadResourceServiceImpl downloadResourceService= SpringUtils.getBean("downloadResourceServiceImpl");
                    DownloadResource downloadResource = downloadResourceService.getOne(new LambdaQueryWrapper<DownloadResource>().eq(DownloadResource::getId,msgInfo.getId()),false);
                    if (downloadResource==null || !DownloadResource.Constants.STATUS_WAIT.equals(downloadResource.getStatus()) ){
                        // 不进行处理
                    }else{
                        downloadResourceService.lambdaUpdate().eq(DownloadResource::getId, downloadResource.getId())
                                .set(DownloadResource::getStatus, DownloadResource.Constants.STATUS_PROCESSING)
                                .update();
						
                        // 以下4行就是简单的分发了,获取到名称,调用spring管理的具体业务处理服务的Bean (~ ̄▽ ̄)~
                        // 这里要求名称与业务处理名一致
                        String serviceName = msgInfo.getDownloadServiceName();
                        serviceName = serviceName.substring(0, 1).toLowerCase() + serviceName.substring(1);
                        BaseBusiness business= SpringUtils.getBean(serviceName);
                        business.download(msgInfo.getParamJson(),msgInfo.getId());
                    }
                }catch (Exception e){ // 异常处理
                    if (downloadId!=null){
                        DownloadResourceServiceImpl downloadResourceService= SpringUtils.getBean("downloadResourceServiceImpl");
                        downloadResourceService.lambdaUpdate().eq(DownloadResource::getId, downloadId)
                                .set(DownloadResource::getStatus, DownloadResource.Constants.STATUS_PROCESSING_FAIL)
                                .set(DownloadResource::getRemarks,"下载数据接受处理异常:"+e.getMessage())
                                .update();
                    }
                }finally {
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }

            }
        });
        consumer.start();
        System.out.println("Consumer Started.%n");
        System.out.println("~ (~ ̄▽ ̄)~     。。。下载队列监听中。。。。。。。  ~  (~ ̄▽ ̄)~ ");
    }
}
// 这一行代码,需要在项目启动的时候执行,在项目Application.main加上
new DownLoadBusinessBase().run();

业务的定义类处理

// 这个就只是方便前面的获取Bean与调用,所以定义了接口与基础实现
public interface BaseBusinessInterface {
    void download(String param,Long downloadId) throws IOException;
}

public class BaseBusiness implements BaseBusinessInterface{
    @Override
    public void download(String param,Long downloadId) throws IOException {

    }
}

@Slf4j
@Service
@Component
public class MerchantBusiness extends BaseBusiness{
    // 业务处理中需要用到,因为这里已经是在spring管理中了,所以随便注入,能用的
    @Autowired
    private IDownloadResourceService downloadResourceService; 

    @Override
    public void download(String param,Long downloadId) throws RuntimeException {
    	// 这里就可以处理下载查询等等的操作了
        // 然后吧处理好的文件压缩保存到文件服务就行了
    }
}

其他内容

文件下载

  1. 这里使用远程文件服务的话,就是直接调用接口就可以了。
  2. 使用本地的话,可以使用nginx做静态文件资源映射 就可以了。
  3. 如果这里的业务服务与Nginx服务,不在同一个服务器里面,那做个Linux下的远程文件挂载就可以了。

其他问题 等 遇到了再说了

你可能感兴趣的:(java,经验分享,项目方案,下载中心)