精华内容
参与话题
问答
  • 政务区块链电子证照应用场景

    万次阅读 2020-08-09 10:14:35
    政务区块链对于电子证照共享的应用场景 区块链电子证照系统场景,所解决的是证照共享的问题。 在预防各部门自己的证照被批量的被盗用或被篡改。采用区块链证照模式,将各个部门的证照共享。 解决的问题 证件被...

    政务区块链对于电子证照共享的应用场景

    区块链电子证照系统场景,所解决的是证照共享的问题。

    在预防各部门自己的证照被批量的被盗用或被篡改。采用区块链证照模式,将各个部门的证照共享。

    解决的问题

    证件被批量盗取

    证件被他方恶意修改

    证件共享难

     

    实现模式

    各个部门将证件的哈希值、关联主键(身份证等)、获取地址、摘要保存到区块链中,通过区块链同步到各个部门。

    在各个部门中如果需要用到证照信息,通过关联主键获取单张证件。

     

     

     

    电子证明共享的应用场景

     

    通过构建政务区块链电子证照共享平台,以解决以上的难题。

     

          先来描述一个场景,当A部门需要提供给B部门证明,用以证明办证人员的有效性来说。来B部门办证的人员选要去A部门开具有效证明。然后那这A部门开具的纸质盖章证明来B部门办证。

    通过以上场景会出现几种问题:

    证明丢失

    证明作假

    证明格式不正确

    证明无效

    那么上了政务区块链以后会发生什么变化呢?

    1、场景一 二维码哈希值确认证明

              从上图看出办事人员的操作步骤并没有减少,但是其中一个无纸化和防丢失优化的整个办证的流程。

     2、场景二 网上申请开具证明,部门之间自动同步。

    从上述场景优化出第二个场景。

                在新的过程中,需办证人员值需要在B部门等待办理,并不需要去A部门多跑一趟。

    3、场景三 证明验证和历史证明追溯

    在某种情况下,B部门办理了有效证件,但是在办理过程中其实没有拿到A部门的证明。或者B部门因为办理某个有效证件并没保存A部门的证明。

    在以上情况下,两个部门都无法追溯这个事情的具体真想。那么通过调用区块链中的历史记录就可以还原整个事情的过程。

    展开全文
  • 转发和重定向区别详解 作为一名程序员,特别是java web开发的程序员,在使用... 1、RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法不仅可以重定...

    转发和重定向区别详解

            作为一名java web开发的程序员,在使用servlet/jsp的时候,我们必须要知道实现页面跳转的两种方式的区别和联系:即转发和重定向的区别。

          1、request.getRequestDispatcher().forward()方法,只能将请求转发给同一个WEB应用中的组件;而response.sendRedirect() 方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。

    如果传递给response.sendRedirect()方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建request.getRequestDispatcher()对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。

          2、重定向访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。

          3、HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求,这个过程好比有个绰号叫“浏览器”的人写信找张三借钱,张三回信说没有钱,让“浏览器”去找李四借,并将李四现在的通信地址告诉给了“浏览器”。于是,“浏览器”又按张三提供通信地址给李四写信借钱,李四收到信后就把钱汇给了“浏览器”。

    由此可见,重定向的时候,“浏览器”一共发出了两封信和收到了两次回复,“浏览器”也知道他借到的钱出自李四之手。

    request.getRequestDispatcher().forward()方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。这个过程好比绰号叫“浏览器”的人写信找张三借钱,张三没有钱,于是张三找李四借了一些钱,甚至还可以加上自己的一些钱,然后再将这些钱汇给了“浏览器”。

    由此可见,转发的时候,“浏览器”只发 出了一封信和收到了一次回复,他只知道从张三那里借到了钱,并不知道有一部分钱出自李四之手。

           4、request.getRequestDispatcher().forward()方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;

    而response.sendRedirect()方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。对于同一个WEB应用程序的内部资源之间的跳转,特别是跳转之前要对请求进行一些前期预处理,并要使用HttpServletRequest.setAttribute方法传递预处理结果,那就应该使用request.getRequestDispatcher().forward()方法。不同WEB应用程序之间的重定向,特别是要重定向到另外一个WEB站点上的资源的情况,都应该使用response.sendRedirect()方法。

            5、无论是request.getRequestDispatcher().forward()方法,还是response.sendRedirect()方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中。

    以上五点的论述来源于:点击查看原文论述

    转发和重定向的图解

    两种跳转获得对象的方式

    //获得转发对象getRequestDispatcher()
    HttpServletRequest(httpServletRequest).getRequestDispatcher
    ServletContext.getRequestDispatcher();
    
    //获得重定向对象sendRedirect()
    HttpServletResponse(httpServletResponse).sendRedirect();

    转发和跳转的小结

          1、转发使用的是getRequestDispatcher()方法;重定向使用的是sendRedirect();

          2、转发:浏览器URL的地址栏不变。重定向:浏览器URL的地址栏改变;

          3、转发是服务器行为,重定向是客户端行为;

          4、转发是浏览器只做了一次访问请求。重定向是浏览器做了至少两次的访问请求;

          5、转发2次跳转之间传输的信息不会丢失,重定向2次跳转之间传输的信息会丢失(request范围)。

    转发和重定向的选择

          1、重定向的速度比转发慢,因为浏览器还得发出一个新的请求,如果在使用转发和重定向都无所谓的时候建议使用转发。

          2、因为转发只能访问当前WEB的应用程序,所以不同WEB应用程序之间的访问,特别是要访问到另外一个WEB站点上的资源的情况,这个时候就只能使用重定向了。

    转发和重定向的应用场景

           在上面我已经提到了,转发是要比重定向快,因为重定向需要经过客户端,而转发没有。有时候,采用重定向会更好,若需要重定向到另外一个外部网站,则无法使用转发。另外,重定向还有一个应用场景:避免在用户重新加载页面时两次调用相同的动作。

           例如,当提交产品表单的时候,执行保存的方法将会被调用,并执行相应的动作;这在一个真实的应用程序中,很有可能将表单中的所有产品信息加入到数据库中。但是如果在提交表单后,重新加载页面,执行保存的方法就很有可能再次被调用。同样的产品信息就将可能再次被添加,为了避免这种情况,提交表单后,你可以将用户重定向到一个不同的页面,这样的话,这个网页任意重新加载都没有副作用;

           但是,使用重定向不太方便的地方是,使用它无法将值轻松地传递给目标页面。而采用转发,则可以简单地将属性添加到Model,使得目标视图可以轻松访问。由于重定向经过客户端,所以Model中的一切都会在重定向时丢失。但幸运的是,在Spring3.1版本以后,我们可以通过Flash属性,解决重定向时传值丢失的问题。

           要使用Flash属性,必须在Spring MVC的配置文件中添加一个<annotation-driven/>。然后,还必须再方法上添加一个新的参数类型:org.springframework.web.servlet.mvc.support.RedirectAttributes。

           如下所示:

    @RequestMapping(value="saveProduct",method=RequestMethod.POST)
    public String saveProduct(ProductForm productForm,RedirectAttributes redirectAttributes){
    
         //执行产品保存的业务逻辑等
      
         //传递参数
           redirectAttributes.addFlashAttribute("message","The product is saved successfully");
       
         //执行重定向
          return "redirect:/……";
    }

     

     

     

     

    展开全文
  • 本课程主要是介绍并实战一款java中间件~redisson,介绍redisson相关的核心技术栈及其典型的应用场景,其中的应用场景就包括布隆过滤器、限流器、短信发送、实时/定时邮件发送、数据字典、分布式服务调度等等,在...
  • SpringBoot整合RabbitMQ之 典型应用场景实战一

    万次阅读 多人点赞 2018-10-09 15:08:04
    特别是在一些典型的应用场景以及业务模块中具有重要的作用,比如业务服务模块解耦、异步通信、高并发限流、超时业务、数据延迟处理等。 其中课程的学习链接地址:https://edu.csdn.net/course/detail/9314 ...

    实战前言

    RabbitMQ 作为目前应用相当广泛的消息中间件,在企业级应用、微服务应用中充当着重要的角色。特别是在一些典型的应用场景以及业务模块中具有重要的作用,比如业务服务模块解耦、异步通信、高并发限流、超时业务、数据延迟处理等。

    其中课程的学习链接地址:https://edu.csdn.net/course/detail/9314 

    RabbitMQ 官网拜读

    首先,让我们先拜读 RabbitMQ 官网的技术开发手册以及相关的 Features,感兴趣的朋友可以耐心的阅读其中的相关介绍,相信会有一定的收获,地址可见:

    http://www.rabbitmq.com/getstarted.html

    阅读该手册过程中,我们可以得知 RabbitMQ 其实核心就是围绕 “消息模型” 来展开的,其中就包括了组成消息模型的相关组件:生产者,消费者,队列,交换机,路由,消息等!而我们在实战应用中,实际上也是紧紧围绕着 “消息模型” 来展开撸码的!

    下面,我就介绍一下这一消息模型的演变历程,当然,这一历程在 RabbitMQ 官网也是可以窥览得到的!

    enter image description here

    enter image description here

    enter image description here

    上面几个图就已经概述了几个要点,而且,这几个要点的含义可以说是字如其名!

    1. 生产者:发送消息的程序
    2. 消费者:监听接收消费消息的程序
    3. 消息:一串二进制数据流
    4. 队列:消息的暂存区/存储区
    5. 交换机:消息的中转站,用于接收分发消息。其中有 fanout、direct、topic、headers 四种
    6. 路由:相当于密钥/第三者,与交换机绑定即可路由消息到指定的队列!

    正如上图所展示的消息模型的演变,接下来我们将以代码的形式实战各种典型的业务场景!

    SpringBoot 整合 RabbitMQ 实战

    工欲善其事,必先利其器。我们首先需要借助 IDEA 的 Spring Initializr 用 Maven 构建一个 SpringBoot 的项目,并引入 RabbitMQ、Mybatis、Log4j 等第三方框架的依赖。搭建完成之后,可以简单的写个 RabbitMQController 测试一下项目是否搭建是否成功(可以暂时用单模块方式构建)

    紧接着,我们进入实战的核心阶段,在项目或者服务中使用 RabbitMQ,其实无非是有几个核心要点要牢牢把握住,这几个核心要点在撸码过程中需要“时刻的游荡在自己的脑海里”,其中包括:

    1. 我要发送的消息是什么
    2. 我应该需要创建什么样的消息模型:DirectExchange+RoutingKey?TopicExchange+RoutingKey?等
    3. 我要处理的消息是实时的还是需要延时/延迟的?
    4. 消息的生产者需要在哪里写,消息的监听消费者需要在哪里写,各自的处理逻辑是啥

    基于这样的几个要点,我们先小试牛刀一番,采用 RabbitMQ 实战异步写日志与异步发邮件。当然啦,在进行实战前,我们需要安装好 RabbitMQ 及其后端控制台应用,并在项目中配置一下 RabbitMQ 的相关参数以及相关 Bean 组件。

    RabbitMQ 安装完成后,打开后端控制台应用:http://localhost:15672  输入guest guest 登录,看到下图即表示安装成功

    enter image description here

    然后是项目配置文件层面的配置 application.properties

    spring.rabbitmq.host=127.0.0.1
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=guest
    spring.rabbitmq.password=guest
    spring.rabbitmq.listener.concurrency=10
    spring.rabbitmq.listener.max-concurrency=20
    spring.rabbitmq.listener.prefetch=5

    其中,后面三个参数主要是用于“并发量的配置”,表示:并发消费者的初始化值,并发消费者的最大值,每个消费者每次监听时可拉取处理的消息数量。

    接下来,我们需要以 Configuration 的方式配置 RabbitMQ 并以 Bean 的方式显示注入 RabbitMQ 在发送接收处理消息时相关 Bean 组件配置其中典型的配置是 RabbitTemplate 以及 SimpleRabbitListenerContainerFactory,前者是充当消息的发送组件,后者是用于管理  RabbitMQ监听器listener 的容器工厂,其代码如下:

    @Configuration
        public class RabbitmqConfig {
        private static final Logger log= LoggerFactory.getLogger(RabbitmqConfig.class);
    
        @Autowired
        private Environment env;
    
        @Autowired
        private CachingConnectionFactory connectionFactory;
    
        @Autowired
        private SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;
    
        /**
         * 单一消费者
         * @return
         */
        @Bean(name = "singleListenerContainer")
        public SimpleRabbitListenerContainerFactory listenerContainer(){
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            factory.setMessageConverter(new Jackson2JsonMessageConverter());
            factory.setConcurrentConsumers(1);
            factory.setMaxConcurrentConsumers(1);
            factory.setPrefetchCount(1);
            factory.setTxSize(1);
            factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
            return factory;
        }
    
        /**
         * 多个消费者
         * @return
         */
        @Bean(name = "multiListenerContainer")
        public SimpleRabbitListenerContainerFactory multiListenerContainer(){
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factoryConfigurer.configure(factory,connectionFactory);
            factory.setMessageConverter(new Jackson2JsonMessageConverter());
            factory.setAcknowledgeMode(AcknowledgeMode.NONE);
            factory.setConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.concurrency",int.class));
            factory.setMaxConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.max-concurrency",int.class));
            factory.setPrefetchCount(env.getProperty("spring.rabbitmq.listener.prefetch",int.class));
            return factory;
        }
    
        @Bean
        public RabbitTemplate rabbitTemplate(){
            connectionFactory.setPublisherConfirms(true);
            connectionFactory.setPublisherReturns(true);
            RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
            rabbitTemplate.setMandatory(true);
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    log.info("消息发送成功:correlationData({}),ack({}),cause({})",correlationData,ack,cause);
                }
            });
            rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
                @Override
                public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                    log.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}",exchange,routingKey,replyCode,replyText,message);
                }
            });
            return rabbitTemplate;
        }}

    RabbitMQ 实战:业务模块解耦以及异步通信

    在一些企业级系统中,我们经常可以见到一个执行 function 通常是由许多子模块组成的,这个 function 在执行过程中,需要 同步的将其代码从头开始执行到尾,即执行流程是 module_A -> module_B -> module_C -> module_D,典型的案例可以参见汇编或者 C 语言等面向过程语言开发的应用,现在的一些 JavaWeb 应用也存在着这样的写法。

    而我们知道,这个执行流程其实对于整个 function 来讲是有一定的弊端的,主要有几点:

    1. 整个 function 的执行响应时间将很久;
    2. 如果某个 module 发生异常而没有处理得当,可能会影响其他 module 甚至整个 function 的执行流程与结果;
    3. 整个 function 中代码可能会很冗长,模块与模块之间可能需要进行强通信以及数据的交互,出现问题时难以定位与维护,甚至会陷入 “改一处代码而动全身”的尴尬境地!

    故而,我们需要想办法进行优化,我们需要将强关联的业务模块解耦以及某些模块之间实行异步通信!下面就以两个场景来实战我们的优化措施!

    场景一:异步记录用户操作日志

    对于企业级应用系统或者微服务应用中,我们经常需要追溯跟踪记录用户的操作日志,而这部分的业务在某种程度上是不应该跟主业务模块耦合在一起的,故而我们需要将其单独抽出并以异步的方式与主模块进行异步通信交互数据。

    下面我们就用 RabbitMQ 的 DirectExchange+RoutingKey 消息模型也实现“用户登录成功记录日志”的场景。如前面所言,我们需要在脑海里回荡着几个要点:

    • 消息模型:DirectExchange+RoutingKey 消息模型
    • 消息:用户登录的实体信息,包括用户名,登录事件,来源的IP,所属日志模块等信息
    • 发送接收:在登录的 Controller 中实现发送,在某个 listener 中实现接收并将监听消费到的消息入数据表;实时发送接收

    首先我们需要在上面的 RabbitmqConfig 类中创建消息模型:包括 Queue、Exchange、RoutingKey 等的建立,代码如下:

    enter image description here

    上图中 env 获取的信息,我们需要在 application.properties 进行配置,其中 mq.env=local

    enter image description here

    此时,我们将整个项目/服务跑起来,并打开 RabbitMQ 后端控制台应用,即可看到队列以及交换机及其绑定已经建立好了,如下所示:

    enter image description here

    enter image description here

    接下来,我们需要在 Controller 中执行用户登录逻辑,记录用户登录日志,查询获取用户角色视野资源信息等,由于篇幅关系,在这里我们重点要实现的是用MQ实现 “异步记录用户登录日志” 的逻辑,即在这里 Controller 将充当“生产者”的角色,核心代码如下:

    @RestController
        public class UserController {
    
        private static final Logger log= LoggerFactory.getLogger(HelloWorldController.class);
    
        private static final String Prefix="user";
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private UserLogMapper userLogMapper;
    
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
        @Autowired
        private Environment env;
    
        @RequestMapping(value = Prefix+"/login",method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        public BaseResponse login(@RequestParam("userName") String userName,@RequestParam("password") String password){
            BaseResponse response=new BaseResponse(StatusCode.Success);
            try {
                //TODO:执行登录逻辑
                User user=userMapper.selectByUserNamePassword(userName,password);
                if (user!=null){
                    //TODO:异步写用户日志
                    try {
                        UserLog userLog=new UserLog(userName,"Login","login",objectMapper.writeValueAsString(user));
                        userLog.setCreateTime(new Date());
                        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
                        rabbitTemplate.setExchange(env.getProperty("log.user.exchange.name"));
                        rabbitTemplate.setRoutingKey(env.getProperty("log.user.routing.key.name"));
    
                        Message message=MessageBuilder.withBody(objectMapper.writeValueAsBytes(userLog)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
                        message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME, MessageProperties.CONTENT_TYPE_JSON); 
                        rabbitTemplate.convertAndSend(message);         
                    }catch (Exception e){
                        e.printStackTrace();
                    }
    
                    //TODO:塞权限数据-资源数据-视野数据
                }else{
                    response=new BaseResponse(StatusCode.Fail);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return response;
        }}

    在上面的“发送逻辑”代码中,其实也体现了我们最开始介绍的演进中的几种消息模型,比如我们是将消息发送到 Exchange 的而不是 Queue,消息是以二进制流的形式进行传输等等。当用 postman 请求到这个 controller 的方法时,我们可以在 RabbitMQ 的后端控制台应用看到一条未确认的消息,通过 GetMessage 即可看到其中的详情,如下:

    enter image description here

    最后,我们将开发消费端的业务代码,如下:

     @Component
        public class CommonMqListener {
    
        private static final Logger log= LoggerFactory.getLogger(CommonMqListener.class);
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private UserLogMapper userLogMapper;
    
        @Autowired
        private MailService mailService;
    
        /**
         * 监听消费用户日志
         * @param message
         */
        @RabbitListener(queues = "${log.user.queue.name}",containerFactory = "singleListenerContainer")
        public void consumeUserLogQueue(@Payload byte[] message){
            try {
                UserLog userLog=objectMapper.readValue(message, UserLog.class);
                log.info("监听消费用户日志 监听到消息: {} ",userLog);
                //TODO:记录日志入数据表
                userLogMapper.insertSelective(userLog);
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    将服务跑起来之后,我们即可监听消费到上面 Queue 中的消息,即当前用户登录的信息,而且,我们也可以看到“记录用户登录日志”的逻辑是由一条异于主业务线程的异步线程去执行的:

    enter image description here

    “异步记录用户操作日志”的案例我想足以用于诠释上面所讲的相关理论知识点了,在后续篇章中,由于篇幅限制,我将重点介绍其核心的业务逻辑!

    场景二:异步发送邮件

    发送邮件的场景,其实也是比较常见的,比如用户注册需要邮箱验证,用户异地登录发送邮件通知等等,在这里我以 RabbitMQ 实现异步发送邮件。实现的步骤跟场景一几乎一致!

    1. 消息模型的创建

    enter image description here

    2. 配置信息的创建

    enter image description here

    3. 生产端

    enter image description here

    4. 消费端

    enter image description here

    RabbitMQ 实战:并发量配置与消息确认机制

    实战背景

    对于消息模型中的 listener 而言,默认情况下是“单消费实例”的配置,即“一个 listener 对应一个消费者”,这种配置对于上面所讲的“异步记录用户操作日志”、“异步发送邮件”等并发量不高的场景下是适用的。但是在对于秒杀系统、商城抢单等场景下可能会显得很吃力!

    我们都知道,秒杀系统跟商城抢单均有一个共同的明显的特征,即在某个时刻会有成百上千万的请求到达我们的接口,即瞬间这股巨大的流量将涌入我们的系统,我们可以采用下面一图来大致体现这一现象:

    enter image description here

    当到了“开始秒杀”、“开始抢单”的时刻,此时系统可能会出现这样的几种现象:

    • 应用系统配置承载不了这股瞬间流量,导致系统直接挂掉,即传说中的“宕机”现象;
    • 接口逻辑没有考虑并发情况,数据库读写锁发生冲突,导致最终处理结果跟理论上的结果数据不一致(如商品存库量只有 100,但是高并发情况下,实际表记录的抢到的用户记录数据量却远远大于 100);
    • 应用占据服务器的资源直接飙高,如 CPU、内存、宽带等瞬间直接飙升,导致同库同表甚至可能同 host 的其他服务或者系统出现卡顿或者挂掉的现象;

    于是乎,我们需要寻找解决方案!对于目前来讲,网上均有诸多比较不错的解决方案,在此我顺便提一下我们的应用系统采用的常用解决方案,包括:

    • 我们会将处理抢单的整体业务逻辑独立、服务化并做集群部署;
    • 我们会将那股巨大的流量拒在系统的上层,即将其转移至 MQ 而不直接涌入我们的接口,从而减少数据库读写锁冲突的发生以及由于接口逻辑的复杂出现线程堵塞而导致应用占据服务器资源飙升;
    • 我们会将抢单业务所在系统的其他同数据源甚至同表的业务拆分独立出去服务化,并基于某种 RPC 协议走 HTTP 通信进行数据交互、服务通信等等;
    • 采用分布式锁解决同一时间同个手机号、同一时间同个 IP 刷单的现象;

    下面,我们用 RabbitMQ 来实战上述的第二点!即我们会在“请求” -> "处理抢单业务的接口" 中间架一层消息中间件做“缓冲”、“缓压”处理,如下图所示:

    enter image description here

    并发量配置与消息确认机制

    正如上面所讲的,对于抢单、秒杀等高并发系统而言,如果我们需要用 RabbitMQ 在 “请求” - “接口” 之间充当限流缓压的角色,那便需要我们对 RabbitMQ 提出更高的要求,即支持高并发的配置,在这里我们需要明确一点,“并发消费者”的配置其实是针对 listener 而言,当配置成功后,我们可以在 MQ 的后端控制台应用看到 consumers 的数量,如下所示:

    enter image description here

    其中,这个 listener 在这里有 10 个 consumer 实例的配置,每个 consumer 可以预监听消费拉取的消息数量为 5 个(如果同一时间处理不完,会将其缓存在 mq 的客户端等待处理!)

    另外,对于某些消息而言,我们有时候需要严格的知道消息是否已经被 consumer 监听消费处理了,即我们有一种消息确认机制来保证我们的消息是否已经真正的被消费处理。在 RabbitMQ 中,消息确认处理机制有三种:Auto - 自动、Manual - 手动、None - 无需确认,而确认机制需要 listener 实现 ChannelAwareMessageListener 接口,并重写其中的确认消费逻辑。在这里我们将用 “手动确认” 的机制来实战用户商城抢单场景。

    1.在 RabbitMQConfig 中配置确认消费机制以及并发量的配置

    enter image description here

    2.消息模型的配置信息

    enter image description here

    3.RabbitMQ 后端控制台应用查看此队列的并发量配置

    enter image description here

    4.listener 确认消费处理逻辑:在这里我们需要开发抢单的业务逻辑,即“只有当该商品的库存 >0 时,抢单成功,扣减库存量,并将该抢单的用户信息记录入表,异步通知用户抢单成功!”

    enter image description here

    enter image description here

    紧接着我们采用 CountDownLatch 模拟产生高并发时的多线程请求(或者采用 jmeter 实施压测也可以!),每个请求将携带产生的随机数:充当手机号 -> 充当消息,最终入抢单队列!在这里,我模拟了 50000 个请求,相当于 50000 手机号同一时间发生抢单的请求,而设置的产品库存量为 100,这在 product 数据库表即可设置

    enter image description here

    6.将抢单请求的手机号信息压入队列,等待排队处理

    enter image description here

    7.在最后我们写个 Junit 或者写个 Controller,进行 initService.generateMultiThread(); 调用模拟产生高并发的抢单请求即可

    @RestController
        public class ConcurrencyController {
    
        private static final Logger log= LoggerFactory.getLogger(HelloWorldController.class);
    
        private static final String Prefix="concurrency";
    
        @Autowired
        private InitService initService;
    
        @RequestMapping(value = Prefix+"/robbing/thread",method = RequestMethod.GET)
        public BaseResponse robbingThread(){
            BaseResponse response=new BaseResponse(StatusCode.Success);
            initService.generateMultiThread();
            return response;
        }}

    8.最后,我们当然是跑起来,在控制台我们可以观察到系统不断的在产生新的请求(线程)– 相当于不断的有抢单的手机号涌入我们的系统,然后入队列,listener 监听到请求之后消费处理抢单逻辑!最后我们可以观察两张数据库表:商品库存表、商品成功抢单的用户记录表 - 只有当库存表中商品对应的库存量为 0、商品成功抢单的用户记录刚好 100 时 即表示我们的实战目的以及效果已经达到了!!

    enter image description here

    总结:如此一来,我们便将 request 转移到我们的 mq,在一定程度缓解了我们的应用以及接口的压力!当然,实际情况下,我们的配置可能远远不只代码层次上的配置,比如我们的 mq 可能会做集群配置、负载均衡、商品库存的更新可能会考虑分库分表、库存更新可能会考虑独立为库存 Dubbo 服务并通过 Rest Api 异步通信交互并独立部署等等。这些优化以及改进的目的其实无非是为了能限流、缓压、保证系统稳定、数据的一致等!而我们的 MQ,在其中可以起到不可磨灭的作用,其字如其名:“消息队列”,而队列具有 “先进先出” 的特点,故而所有进入 MQ 的消息都将 “乖巧” 的在 MQ 上排好队,先来先排队,先来先被处理消费,由此一来至少可以避免 “瞬间时刻一窝蜂的 request 涌入我们的接口” 的情况!

    附注:在用 RabbitMQ 实战上述高并发抢单解决方案,其实我也在数据库层面进行了优化,即在读写存库时采用了“类似乐观锁”的写法,保证:抢单的请求到来时有库存,更新存库时保证有库存可以被更新!

    彩蛋:本博文介绍了几个典型的RabbitMQ实战的业务场景,相关源码数据库可以来这里下载

    (1)https://download.csdn.net/download/u013871100/10654482

    (2)https://pan.baidu.com/s/1KUuz_eeFXOKF3XRMY2Jcew
    学习过程有任何问题均可以与我交流,技术交流群:605610429(Java实战基地交流1群)

    下一篇博文将继续典型应用场景实战之死信队列的应用。感兴趣的童鞋可以关注一下我的微信公众号!

    展开全文
  • 悲观锁和乐观锁的区别和应用场景

    万次阅读 2018-08-14 21:46:31
    悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁...

    悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

     

    乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

     

     

     

    为什么需要锁(并发控制)?

    在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。

    典型的冲突有:

    l 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。

    l 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。

    为了解决这些并发带来的问题。 我们需要引入并发控制机制。

    并发控制机制

    悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。[1]

    乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。

    乐观锁应用

    1.      使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。

    2.      使用时间戳来实现.

    注:对于以上两种方式,Hibernate自带实现方式:在使用乐观锁的字段前加annotation: @Version, Hibernate在更新时自动校验该字段。

    悲观锁应用

    需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL  Server  将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

    SqlServer中使用

    Begin Tran
    select top 1 @TrainNo=T_NO
             from Train_ticket   with (UPDLOCK)   where S_Flag=0

          update Train_ticket
             set T_Name=user,
                 T_Time=getdate(),
                 S_Flag=1
             where T_NO=@TrainNo
    commit

    我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

    结论

    在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.

     

     

    两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

    展开全文
  • 区块链七大应用场景

    万次阅读 多人点赞 2019-09-05 18:47:11
    一、应用场景:信息共享 这应该是区块链最简单的应用场景,就是信息互通有无。 1、传统的信息共享的痛点 要么是统一由一个中心进行信息发布和分发,要么是彼此之间定时批量对账(典型的每天一次),对于有时效性...
  • Netty应用场景

    万次阅读 2018-11-28 23:02:49
    随着网站规模的不断扩大,系统并发访问量也越来越高,传统基于 Tomcat 等 Web 容器的垂直架构已经无法满足需求,需要拆分应用进行服务化,以提高开发和维护效率。从组网情况看,垂直的架构拆分之后,系统采用分布式...
  • 区块链:典型应用场景

    万次阅读 2019-05-17 18:17:03
    其中很关键的一点便是能否找到合适的应用场景。 以比特币网络为代表的大规模数字货币系统,长时间自治运行,支持了传统金融系统都难以实现的全球范围即时可靠交易。这为区块链技术的应用潜力引发了无限遐想。如果...
  • nginx应用场景

    千次阅读 2018-07-04 15:18:23
    一:nginx简介 ... Nginx (engine x).... 是一款轻量级的Web服务器 、反向代理服务器、电子邮件(IMAP/POP3)代理服务器、通用TCP/UDP代理服务器、也可以作为静态服务器,提供静态内容服务(静态网站)。...
  • Spring简介、框架核心、优缺点、应用场景

    万次阅读 多人点赞 2019-10-29 23:34:33
    文章目录Spring简介Spring的设计目标Spring的优缺点优点缺点Spring的应用场景 Spring简介 Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题...
  • redis应用场景

    千次阅读 2020-01-03 16:19:18
  • 共同点 ​ 1.都是放在内存中,是内存数据库 ​ 2.都可以做分布式集群,可以一主多从,也可以一主一从 区别 1.redis不仅仅支持k/v类型的数据,还支持hash list set sortedset类型数据结构的存储 ,memcached 支持简单...
  • 5G 应用及应用场景总结

    千次阅读 2020-04-30 00:56:05
    1.概述 5G技术演进方式与前几代...先要确定应用的需求和场景,包括 “生活”和“社会”两方面内容,然后再去探索发现相对应的具体技术,例如:5G 新空口(New Radio, NR)设计标准就是来源于全新应用场景,其中包括: ...
  • 共同点 A.两者都是抽象类,都不能实例化 B.Interface实现类和abstract继承类都必须实现抽象方法 不同点 A.Interface需要实现,用implements;Abstract 需要继承,用exends B.一个类可以实现多个...D.Interf...
  • 动态代理是什么?应用场景

    万次阅读 2019-06-17 10:00:39
    应用场景? 动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。 Java 中实现动态的方式:JDK 中的动态代理和 Java类库 CGLib。 应用场景如: 统计每个 api 的请求耗时 统一的日志输出 校验被...
  • Flink应用场景

    千次阅读 2019-08-17 14:11:07
    Apache Flink 功能强大,支持开发和运行多种不同种类的应用程序。它的主要特性包括:批流一体化、精密的状态管理、事件时间支持以及精确一次的状态一致性保障等。Flink 不仅可以运行在包括 YARN、 Mesos、Kubernetes...
  • String类型 ​ 可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512M ​ 实现方式: 一个字符串,被redisObject所引用...
  • 区块链应用场景

    千次阅读 2019-05-31 09:32:53
    听见身边有人说,区块链可以应用现在生活的方方面面,这个话本身没什么太大的毛病,但是不一定所有的场景都必须用区块链,那什么场景需要用区块链呢? 书中说,区块链应用是需要跨越组织边界,,也就是说,在区块链...
  • 5G十大细分应用场景研究报告

    万次阅读 多人点赞 2019-06-22 17:38:27
    从全球视角来看,目前5G无论是在技术、标准、产业生态还是网络部署等方面都取得了阶段性的成果,5G落地的最后一环——应用场景正逐渐在各行各业实现。5G应用落地有哪些规律?哪些应用最先走向成熟?本报告立足ITU...
  • Elasticsearch Top5典型应用场景

    万次阅读 2018-09-16 19:56:50
    刚接触Elasticsearch的朋友,或多或少会遇到一个问题,Elasticsearch在实际公司应用中除了搜索到底能做什么? 本文给出了答案。 除了“You Know, for Search”,Elasticsearch的使用会不断增长和变化。...
  • ZooKeeper典型应用场景

    万次阅读 2017-09-27 17:51:27
    网上对ZK的应用场景也有不少介绍,本文将结合作者身边的项目例子,系统地对ZK的应用场景进行一个分门归类的介绍。 值得注意的是,ZK并非天生就是为这些应用场景设计的,都是后来众多开发者根据其框架的特性
  • Redis介绍及常用应用场景介绍

    千次阅读 多人点赞 2019-08-14 00:36:32
    1. 基础与协议 Redis是一种常用来做缓存的工具,遵循BSD协议。BSD协议是五大开源协议的一种,它允许使用者在使用产品的基础上,可以对源代码进行修改和重新发布,并且可以发布为商业软件。需要注意的是,要在源代码...
  • SpringCloud应用场景

    千次阅读 2020-04-24 19:10:54
    这里提供了Eureka、Consul、Nacos三种解决方案。 配置中心 配置中心主要用于提供统一的外部配置管理,微服务架构中的服务可以从配置中心获取配置信息,同时支持动态刷新配置。这里提供了Spring Cloud Config、Consul...
  • NLP经典应用场景

    2020-04-24 15:14:30
    NLP的应⽤场景 1、问答系统 智能客服 2、情感分析 3、机器翻译(seq2seq + Attention) 4、文本自动摘要 Text Summarization 5、聊天机器人 (闲聊的 or 目标导向的task oriented) 订机票任务等 6、信息抽取...
  • LoRa技术应用场景

    2020-03-22 13:30:06
    温湿度、二氧化碳、盐碱度等传感器的应用对于农业提高产量、减少水资源的消耗等有重要的意义,而这些数据指标短时间内不会产生明显变化,数据量小且对实时性的要求不高,因此LoRa无疑是最佳选择。 智慧建筑 对于建筑...
  • BIO、NIO、AIO 区别和应用场景

    万次阅读 多人点赞 2018-09-27 11:14:08
    前边简单介绍过IO的基本情况Java IO流 简单回顾 对于IO我们应该非常熟悉了,IO不仅仅针对文件的操作,网络编程socket的通信,就是IO操作。 输入、输出流(InputStream、OutputStream)用于读取或写入字节,如操作...
  • 5G通信系统应用场景与消费物联网、工业物联网应用场景综合分析 一、5G通信系统应用场景 5G的到来意味着什么?高速率(增强型移动宽带,eMBB)、大容量(大规模机器通信,mMTC)、低时延(高可靠低时延通信,URLLC)是5G...
  • OCR原理浅析及应用场景

    千次阅读 2019-01-08 20:42:08
    OCR是技术,基于OCR的应用场景就非常多了。比如:ocr综合文字识别、ocr视频文字识别、人脸识别、证件识别、票据识别、车牌Vin码识别、银行卡识别、云识别 文档识别 到底哪款OCR软件识别率最高?答案你一定想不到,...
  • ETCD应用场景

    千次阅读 2017-03-13 15:10:04
    etcd是什么? 很多人对这个问题的第一反应可能是,它是一个键值存储仓库,却没有重视官方定义的后半句,用于配置共享和服务发现。 A highly-available key value store for shared configuration
  • socket应用场景

    千次阅读 2019-02-15 19:54:08
    模拟urllib实现网页内容的获取 实现http客户端的程序, 获取百度...import socket # 实例化socket对象; 默认参数指定为IPv4协议, 和TCP传输协议; client = socket.socket() # 连接服务器端 ...# 给百度服务器发送请求...

空空如也

1 2 3 4 5 ... 20
收藏数 138,517
精华内容 55,406
关键字:

应用场景