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

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

直接上设计流程:
在这里插入图片描述

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

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

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


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下的远程文件挂载就可以了。

其他问题 等 遇到了再说了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7464.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

130亿参数,8个A100训练,UC伯克利发布对话模型Koala

平替再平替&#xff0c;可以在消费级 GPU 上运行的 Koala 模型能实现 ChatGPT 一半的性能。 自从 Meta 发布并开源了 LLaMA 系列模型&#xff0c;来自斯坦福大学、UC 伯克利等机构的研究者们纷纷在 LLaMA 的基础上进行「二创」&#xff0c;先后推出了 Alpaca、Vicuna 等多个「…

Solon v2.2.7 发布,支持 Java 20

Solon 是一个高效的 Java 应用开发框架&#xff1a;更快、更小、更简单。也是一个有自己接口标准规范的开放生态。 150来个生态插件&#xff0c;覆盖各种不同的应用开发场景&#xff1a; 相对于 Spring Boot 和 Spring Cloud 的项目&#xff1a; 启动快 5 &#xff5e; 10 倍…

【无功优化】基于改进遗传算法的电力系统无功优化研究【IEEE30节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

4.5--计算机网络之基础篇--1.模型分层--(复习+深入)---好好沉淀,加油呀

1.TCP/IP模型的分层 1.1.为什么要有 TCP/IP 网络模型&#xff1f; 对于同一台设备上的进程间通信&#xff0c;有很多种方式&#xff0c;比如有管道、消息队列、共享内存、信号等方式&#xff1b; 而对于不同设备上的进程间通信&#xff0c;就需要网络通信&#xff0c;而设备是…

2021蓝桥杯真题大写 C语言/C++

题目描述 给定一个只包含大写字母和小写字母的字符串&#xff0c;请将其中所有的小写字母转换成大写字母后将字符串输出。 输入描述 输入一行包含一个字符串。 输出描述 输出转换成大写后的字符串。 输入输出样例 示例 输入 LanQiao 输出 LANQIAO 评测用例规模与约定 对于…

python 生成器、迭代器、动态新增属性及方法

目录 一、生成器 1、生成器定义 2、生成器存在的意义 3、创建生成器方式一&#xff08;生成器表达式&#xff09; 4. 创建生成器方式二&#xff08;生成器函数&#xff09; 1. 生成器函数 2. 生成器函数的工作原理 5. 总结 1. 什么是生成器 2. 生成器特点 二、迭代器…

365天深度学习训练营-第J8周:Inception v1算法实战与解析

目录 一、前言 二、论文解读 1. Inception 模块 2. 网络深度问题 3. 全局平均池化 4. 卷积层的并行结构 5. 1x1 卷积核 6.详细的网络结构 三、代码复现 一、前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#x…

微服务+springcloud+springcloud alibaba学习笔记【Eureka服务注册中心】(3/9)

Eureka服务注册中心 3/91、服务注册与发现1.1 什么是服务治理&#xff1a;1.2 什么是服务注册与发现&#xff1a;1.3 Eureka服务注册与发现2、单机版eureka2.1 创建module2.2改pom依赖2.3写yml配置文件:2.4主启动类2.5 修改服务提供者 cloud-provider-payment8001 模块&#xf…

Github库中的Languages显示与修改

目录 前言 【.gitattributes】文件 修改GitHub语言 前言 上传一个项目到GitHub时&#xff0c;发现显示的语言并非是自己项目所示的语言&#xff0c;这样的情况是经常发生的&#xff0c;为了能到达自己所需快速检索&#xff0c;或者是外部访问者能很好的搜索我们的项目&#…

Sentinel滑动时间窗限流算法原理及源码解析(中)

文章目录 MetricBucketMetricEvent数据统计的维度WindowWrap样本窗口实例 范型T为MetricBucket windowLengthInMs 样本窗口长度 windowStart 样本窗口的起始时间戳 value 当前样本窗口的统计数据 其类型为MetricBucket MetricBucket MetricEvent数据统计的维度 1、首先计算27t位…

【花雕学AI】09:发挥ChatGPT最大潜力——产生高质量内容的九种方法和建议

人工智能&#xff08;AI&#xff09;是当今科技领域最热门和最有前景的话题之一&#xff0c;它已经渗透到了我们生活和工作的方方面面&#xff0c;给我们带来了许多便利和惊喜。而在AI的众多分支中&#xff0c;自然语言处理&#xff08;NLP&#xff09;是最贴近人类的一个领域&…

6-python异常、错误、模块、包

文章目录1.异常2.错误3.模块4.包[综合练习]1.异常 打开一个不存在的文件会引发异常 FileNotFoundError: [Errno 2] No such file or directory: ‘D:/不存在的文件.txt’ fopen(file"D:/不存在的文件.txt",mode"r", encodingutf-8) print(f.read())&…

springboot-gateway注册nacos失败,控制台没有报错

目录 前言现象描述前言 最近springboot的gateway注册到nacos上,没有注册成功 现象描述 我是在common里面引入了nacos的依赖,依赖如下: <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-confi…

文件的随机读写

fseek fseek这个函数的作用是什么&#xff1f; 我们知道&#xff0c;如果对一个文件指针进行加减操作的话&#xff0c;这个文件指针会指向下一个文件&#xff0c;那么问题来了&#xff0c;我们如果想访问这个文件中的某一个内容&#xff0c;这个时候该怎么办呢&#xff0c;我们…

基于支持向量机SVM的脑部肿瘤识别,脑电波样本熵提取

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 SVM应用实例,基于SVM的的脑部肿瘤识别分类预测 代码 结果分析 展望 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它…

下一代的新操作系统就是ChatGPT!

什么是CHatgpt&#xff1f; ChatGPT是人工智能研究实验室OpenAI在2022年11月30日推出的聊天机器人模型&#xff0c;它使用Transformer神经网络架构&#xff0c;训练数据来自包括维基百科&#xff0c;以及真实对话在内的庞大语料库。2023年1月30日消息称&#xff0c;中国搜索巨…

数据分析-统计基础

day1&#xff1a;集中趋势&#xff0c;离散测度&#xff0c;均值&#xff0c;中位数&#xff0c;众数&#xff0c;方差等。点估计&#xff0c;区间估计等相关的知识 现代数据分析&#xff1a;把数据放到一个数据空间中&#xff0c;通过这个空间的个各种形变与分析而挖掘除数据…

Android RenderScript 浅谈

前言 RenderScript是一个Google出品的&#xff0c;在Android平台上的并行计算框架&#xff0c;官方的简介是说RenderScript运行时可在设备上提供的多个处理器&#xff08;如多核 CPU 和 GPU&#xff09;间并行调度工作。在日常Android开发中&#xff0c;RenderScript主要用于图…

数据库:Redis哨兵及cluster集群部署

一、redis数据库哨兵模式 目录 一、redis数据库哨兵模式 1、什么是哨兵模式 2、哨兵的作用 3、哨兵结构组成 4、哨兵故障转移机制 5、哨兵工作、切换原理 6、哨兵主节点选举原则 7、哨兵模式部署 二、redis数据库cluster集群 1、cluster集群优点、数据存储及同步方式…

Chapter2 : SpringBoot配置

尚硅谷SpringBoot顶尖教程 1. 全局配置文件 SpringBoot使用一个全局的配置文件 application.properties 或者 application.yml &#xff0c;该配置文件放在src/main/resources目录或者类路径/config目录下面&#xff0c; 可以用来修改SpringBoot自动配置的默认值。 yml是YA…