基于WebSocket的实时消息传递设计

目录
  • 概述
  • 整体架构
  • 设计
    • 流程设计
    • 程序设计
      • WebSocketServer
        • 概述
        • 新增pom
        • 新增配置类
        • 创建websocket端点
      • WebSocketClient
        • 概述
        • 安装WebSocketSharp
        • 初始化client
        • 创建连接
        • 跨线程更新UI
          • 获取当前线程名字
          • 示例代码
    • 接口设计
      • 新增接口
        • 概述
        • 核心代码
  • 风险
    • 分布式风险
    • 资源风险
    • 高可用风险

概述

web管理系统中可以对业务数据执行新增和删除,现在需要当业务数据发生新增或删除操作后,尽可能实时的反应到WPF客户端上面。

web管理系统用VUE编写,后端服务为SpringBoot,WPF客户端基于.Netframework4.8编写。

整体架构

sequenceDiagram title: 交互时序图 web前台->>+web后端服务:新增数据 Note over web前台,web后端服务:caremaId,labelInfo,...... web后端服务->>+WebSocketServer:创建websocker消息 Note over web后端服务,WebSocketServer:Must:cameraId=clientId WPF客户端1-->>+WebSocketServer:创建监听 Note over WPF客户端1,WebSocketServer:clientId WPF客户端2-->>+WebSocketServer:创建监听 Note over WPF客户端2,WebSocketServer:clientId WebSocketServer->>WPF客户端1:分发websocker消息 Note over WebSocketServer,WPF客户端1:依据:cameraId=clientId WebSocketServer->>WPF客户端2:分发websocker消息 Note over WebSocketServer,WPF客户端2:依据:cameraId=clientId

设计

流程设计

  • 用户在浏览器界面执行新增业务数据操作,调用后端新增接口
  • WPF客户端在启动的时候初始化websocket客户端,并创建对server的监听
  • 后端新增接口先将数据落库,而后调用websocket服务端产生消息,消息在产生后立马被发送到了正在监听中的websocket-client
  • websocket-server和websocket-client是一对多的关系,如何保证业务数据被正确的分发?监听的时候给server端传递一个全局唯一的clientId,业务数据在产生的时候关联到一个BizId上面,只要保证clientId=BizId就可以了。
  • 删除流程和新增类似

程序设计

WebSocketServer

概述

WebSocketServer端采用SpringBoot框架实现,通过在springboot-web项目中集成 org.springframework.boot:spring-boot-starter-websocket
实现websocket的能力。

新增pom



<!--  websocket  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>


新增配置类



import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.config.annotation.EnableWebSocket;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
@EnableWebSocket
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}



创建websocket端点



import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/ws/label/{clientId}")
@Component
public class LabelWebSocket {
    /**
     * session list
     */
    private static ConcurrentHashMap<String, Session> sessionList = new ConcurrentHashMap<>();
    /**
     * 当前 clientId
     */
    private String currentClientId = "";
    @OnOpen
    public void open(Session session, @PathParam("clientId") String clientId) throws IOException {
        if (sessionList.containsKey(clientId)) {
            sessionList.remove(clientId);
        }
        sessionList.put(clientId, session);
        currentClientId = clientId;
        this.sendMsg(session, "connectok");
    }
    @OnClose
    public void close(Session session) throws IOException {
        sessionList.remove(currentClientId);
        System.out.println("连接关闭,session=" + JSON.toJSONString(session.getId()));
    }
    @OnMessage
    public void receiveMsg(Session session, String msg) throws IOException {
        this.sendMsg(session, "接收到的消息为:" + msg);
//        throw new RuntimeException("主动抛异常");
    }
    @OnError
    public void error(Session session, Throwable e) throws IOException {
        System.out.println("连接异常,session=" + JSON.toJSONString(session.getId()) + ";currentClientId=" + currentClientId);
        this.sendMsg(session, "发生异常,e=" + e.getMessage());
        e.printStackTrace();
    }
    /**
     * @param clientId
     * @param msg
     */
    public boolean sendMsg(String clientId, String msg) throws IOException {
        if (sessionList.containsKey(clientId)) {
            Session session = sessionList.get(clientId);
            this.sendMsg(session, msg);
            return true;
        } else {
            return false;
        }
    }
    private void sendMsg(Session session, String msg) throws IOException {
        session.getBasicRemote().sendText(msg);
    }
}



WebSocketClient

概述

WebSocketClient端集成在WPF应用客户端中,通过前期调研,选中 WebSocketSharp 作为websocketclient工具,WebSocketSharp 是托管在Github的开源项目,MITLicense,目前4.9K的star。

安装WebSocketSharp


//nuget

Install-Package WebSocketSharp -Pre

初始化client


WebSocket ws = new WebSocket("ws://127.0.0.1:8083/ws/xx/clientId");


创建连接



private void InitWebSocket()
{
    ws.OnOpen += (sender, e) =>
    {
        Console.WriteLine("onOpen");
    };
    //允许ping
    ws.EmitOnPing = true;
    //接收到xiaoxi
    ws.OnMessage += (sender, e) =>
    {
        ReceiveMessage(sender, e);
    };
    ws.Connect();
    //发送消息
    //ws.Send("BALUS")
    ;
}
private void ReceiveMessage(object sender, MessageEventArgs e)
{
    if (e.IsText)
    {
        // Do something with e.Data.like jsonstring
        Console.WriteLine(e.Data);
        return;
    }
    if (e.IsBinary)
    {
        // Do something with e.RawData. like  byte[]
        return;
    }
    if (e.IsPing)
    {
        // Do something to notify that a ping has been received.
        return;
    }
}



跨线程更新UI

由于 WebSocketSharp 会创建线程来处理 ReceiveMessage ,而WPF中子线程是无法更新UI的,所以需要引入 Dispatcher 来实现跨线程更新UI。

获取当前线程名字

 //当前线程

string name = Thread.CurrentThread.ManagedThreadId.ToString();


示例代码


private void ReceiveMessage(object sender, MessageEventArgs e)
{
    if (e.IsText)
    {
        // Do something with e.Data.like jsonstring
        Console.WriteLine(e.Data);
        //当前线程
        string name = Thread.CurrentThread.ManagedThreadId.ToString();
        App.Current.Dispatcher.Invoke((Action)(() =>
        {
            Image lab = new Image();
            lab.Uid = "123456";
            lab.Name = "labName";
            lab.Width = 50; lab.Height = 50;
            string url = "http://xxx:xxx/img/louyu.png";
            BitmapImage bitmapImage = HttpUtil.getImage(url);
            lab.Source = bitmapImage;
            lab.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(LabelClick));
            Canvas.SetTop(lab, 800);
            Canvas.SetLeft(lab, 600);
            this.cav.Children.Add(lab);
        }));
        return;
    }
}



接口设计

新增接口

概述

目前WebSocketServer和web后端服务是在同一个SpringBoot的工程中,所以只要将WebSocketServer托管到SpringContainer中,web后端服务可以通过 DI 的方式直接访问 WebSocketEndPoint。

如果考虑程序的低耦合,可以在WebSocketServer和web后端服务之间架设一个MQ。

核心代码


    @Autowired
    private LabelWebSocket ws;
    @GetMapping("/create")
    public boolean createLabel() throws IOException {
        String cameraId = "cml";
        //todo
        boolean result = ws.sendMsg(cameraId, "新增标签");
        return result;
    }


风险

分布式风险

当前在 WebSocketServer 中,已经连接的client信息是记录在当前进程的cache中,如果服务做横向扩容,cache信息无法在多实例进程中传递,将导致无法正确的处理业务数据,并可能会发生意想不到的异常和bug,此问题在并发越高的情况下造成的影响越大

资源风险

web后端服务为基于java语言的springboot程序,这种类型程序的特点是内存消耗特别严重。WebSocketServer服务在本项目中仅用作消息中间件,连通web后端服务和WPF客户端。

首先WebSocketServer没有太多的计算能力的消耗,内存消耗会随着连接客户端数量的增长而增长,网络将是最大的开销,一方面需要转发来自web后端服务的业务数据,并和WPF客户端保持长连接;另一方面WebSocketServer和WPF客户端的交互可能会走公网,而其和web后端服务必然是在局域网环境。

综上,将web后端服务和WebSocketServer分开部署对于硬件资源成本和利用率来说是最好的选择。

高可用风险

未引入重试机制,当某一个环节失败之后,将导致异常情况发生。

邮箱:cnaylor@163.com
技术交流QQ群:1158377441
本文转载于网络 如有侵权请联系删除

相关文章

  • 【Java基础】方法、类、对象

    方法方法的定义什么是方法?简而言之,方法就是解决问题的办法。在Java语言中,方法大多用于处理一些数据并得到结果,其包括以下几种要素:修饰符:用于限定方法的权限,常用的有public、private、static。返回值类型:方法运行后,得出的结果的数据类型;如果没有返回值,那么返回值类型为void。方法名:自定义的标识符,满足标识符规范,使用骆驼峰命名法。参数列表:方法在运行过程中,若有未知的参数,需要定义在参数列表中。return:用来结束方法并返回方法运行的结果,若无返回结果,可以没有return。知道了方法的必要元素,可得知方法的定义格式如下:修饰符返回值类型方法名(参数列表){ 方法体; return; }复制下面我们举几个例子:定义一个公有静态方法,打印传入的参数 //由上题我们可以看出,方法的修饰符是公有的、静态的,有一个参数,只是打印所以并没有返回值。//那么这个方法可以这样定义publicstaticvoidprintParam(Stringparam){System.out.println(param);}定义一个私有方法,传入两个参数,返回两个数的和 //同上我们

  • 外卖商品的标准化建设与应用

    总第452篇2021年第022篇外卖菜品命名个性化程度高,为运营分析、召回排序、后台管理等业务带来一定的困难。本文系外卖美食知识图谱系列的第二篇文章,介绍了外卖从零到一建设菜品标准化体系的过程及方案,涉及的主要技术包括NLP领域的实体抽取、文本匹配、关系分类,以及CV领域的图像匹配等。最后,通过标准名在外卖业务中的应用实践,验证了标准名体系建设的价值和意义。1.背景及目标商品作为外卖交易过程中的核心要素,决定了供需匹配的精准度,直接影响交易行为是否可以达成。外卖平台美食、甜点、饮品类在线商品有亿级之多,其中很多是属性信息一致的相同商品。建立对商品的标准化描述、聚合相同商品,是很多业务场景的诉求。①供销分析场景:想分析一下望京的商家都售卖哪些菜品,有多少商家卖“西红柿炒鸡蛋”?遇到的问题:由于菜品是非标品,并且商家对菜品命名的个性化程度也较高,因此在外卖平台,同一个菜品名出现不同的命名方式;例如“西红柿炒鸡蛋”有西红柿炒蛋、小番茄炒蛋、西红柿鸡蛋、京城三绝~番茄炒蛋【正价小份菜】等,没有办法简单通过关键字进行聚合。②主题推荐场景:想出一个菜品粒度的主题,快速筛选“小龙虾”、“烤鱼”、“鸡

  • 看完后,我才明白 Redis 为什么默认 16 个数据库?

    导读:在实际项目中Redis常被应用于做缓存,分布式锁、消息队列等。但是在搭建配置好Redis服务器后很多朋友应该会发现和有这样的疑问,为什么Redis默认建立了16个数据库,如下图所示。 一、16个数据库的由来Redis是一个字典结构的存储服务器,一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与在一个关系数据库实例中可以创建多个数据库类似(如下图所示),所以可以将其中的每个字典都理解成一个独立的数据库。以MySQL实例为例Redis默认支持16个数据库,可以通过调整Redis的配置文件redis/redis.conf中的databases来修改这一个值,设置完毕后重启Redis便完成配置。客户端与Redis建立连接后会默认选择0号数据库,不过可以随时使用SELECT命令更换数据库。在实际项目中则可以通过以Redis配置文件的形式指定数据库,如下图所示二、正确理解Redis的“数据库”概念由于Redis不支持自定义数据库的名字,所以每个数据库都以编号命名。开发者则需要自己记录存储的数据与数据库的对应关系。另外Redis也不支持为每个数据库设置不

  • WebAssembly之emcc编译命令

    image.pngemcc使用语法:emcc[options]file...复制Emscripten是用于编译为使用LLVM构建的asm.js和WebAssembly的工具链,可让您以几乎本机的速度在Web上运行C和C++,而无需插件。image.pngemcc用于从命令行调用Emscripten的编译器,它实际上是标准编译器gcc或者clang的一个替换。emcc参数介绍#显示信息 emcc--help #显示编译器版本信息 emcc--version复制大部分clang或者gcc的选项(option)都是可以工作的。 首先是一些编译优化flag,它们-O0,-O1,-O2,-Os,-Oz,-O3。-O0: 不进行编译优化(这是默认情况)。当你刚开始移植项目是推荐使用它,因为它会包含许多断言。-O1: 简单优化。推荐你在既想缩短编译时间又想编译优化时使用。它毕竟比-O2级别的优化编译起来快多了。它会进行asm.js和llvm的-O1进行优化,它会relooping,会删除运行时断言和C++异常捕获,它也会使得-sALIASING_FUNCTION_POINTERS=1。 想要C++异

  • 【译】一种新的 JavaScriptCoere 字节码规范

    在r237547版本我们介绍过一种新的JavaScriptCore(JSC)字节码规范。对比之前的规范为了提高编译器的吞吐量而增加了内存使用,这个新的规范提高内存的利用率并且允许字节码可以被硬盘缓存起来。在这篇文章中,我们准备从一份JSC的字节码示例开始讲起,旧版字节码规范的主要作用和他使用到的优化。接着,我们会看看新规范是怎么优化编译器的。最后我们会看下新规范是如何影响内存和性能,还有这样重写之后怎样提高JSC的类型安全性。背景在JSC执行任何JS代码之前,必须经过编译和生成字节码的过程。JSC有四个执行层级:低阶编译器(LLInt):编译器启动基础JIT:一个模板JITDFGJIT:一个低延迟的最优编译器FTLJIT:一个高吞吐量的最优编译器执行器在最底层从编译字节码开始,编译完的字节码就越来越合适高阶层去使用。这个可以在(这篇FTL相关的博客)https://webkit.org/blog/3362/introducing-the-webkit-ftl-jit/中找到更多的细节。字节码是整个引擎的驱动源泉。LLInt执行这些字节码。底层是一个把每一个字节码指令执行成为机器码片段的

  • 大数据“预测”下一个潜在的高危城市

    这几天,武汉的疫情牵动着全国人民紧张的内心,从每天公布的确诊人数和疑似病例来看,数量也是在每日剧增。而在今日,武汉也已经关闭机场、火车站等各种交通枢纽,试图遏制疫情蔓延的势头,但是在此之前,春运大潮已经启动,而春运所代表的中国每年最大人口迁徙也成为了病毒蔓延至其他省份最大的导火索。今天小编为大家盘点一下未来疫情可能的走势以及需要重点防控的几个城市。“这将是一场持久战”时间线拉到2003年的非典疫情,从第一例报告病例始于广东省的河源到最后卫生部宣布中国非典零病例历时长达8个月左右,而大规模的防控工作则在2003年的4月才大规模的展开,而反观此次的冠状病毒,最早是开始于去年的12月月底,武汉已经发现了27例的肺炎病例,到本月的20号得到中国卫健委的高度重视到今日武汉正式关闭各种交通枢纽(当然我们要是能够更早的重视疫情和采取措施,结果会更好。)可见还是比较的“及时与迅速”,但是我们还是应该做好与疫情打长期持久战的准备,尤其是赶上了春运高峰等特殊情况。根据冠状病毒的潜伏期最长在14天左右,在未来的大半个月内,每日的确诊病例和疑似病例还是会继续上升(ormaybenot?),但是在之后的时间里会

  • 程序员、架构师、技术经理、技术总监和CTO都是干什么的?

    程序员  程序员,英文名coder/programmer,大家常自嘲叫码农的阶段。这个角色职责是把需求或产品实现为用户可用的软件产品。  此职位为执行级别。另外因为经验较少,一般需要求助别人,或与别人一起完(ban)成(zhuan)一个任务。  此阶段大概要经历3年,程序员的职责如下:  1、负责公司运营系统的设计与开发工作  2、运营数据处理和分析高级程序员  高级程序员学名工程师。到了这个level,英文名可改叫做engineer或developer。此时你的功力开始增强,这与你平时的积累努力是分不开的,祝贺你~  此时的你不仅可以完成任务,开始注重代码的质量,能够写出工业级的代码。  你的经验可胜任模块级的系统设计,承担完成较为复杂的技术,能有效的自我管理,有帮助别人快速解决问题(troubleshooting)的能力。  此阶段你需要经历到7、8年左右的体验,中间要经历一段深刻自我历练的过程。  有时给人致命一击其实是心里的小蟊贼。一般人在5年前后遇到一个门槛,碰到天花板+彷徨期,或者你打心眼里不在喜欢编程,可尝试转为其它角色,如产品经理,售前售后支持等岗位,也不失为好选择。 

  • BigDecimal的使用

    BigDecimal加减乘除方法//创建 BigDecimala=BigDecimal.valueOf(10); BigDecimalb=newBigDecimal(10); BigDecimalc=newBigDecimal("0.1111111"); System.out.println("加法:"+a.add(b)); System.out.println("减法:"+a.subtract(b)); System.out.println("乘法:"+a.multiply(b)); //小数点后的位数RoundingMode.HALF_DOWN:取舍模式 System.out.println("除法:"+a.divide(b,10,RoundingMode.HALF_DOWN)); System.out.println("设置小数点后两位:"+c.setScale(2,BigDecimal.ROUND_HALF_UP));复制常用BigDecimal比较BigDe

  • 关于ASP.NET Core WebSocket实现集群的思考

    前言     提到WebSocket相信大家都听说过,它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议。在没有WebSocket之前只能通过浏览器到服务端的请求应答模式比如轮询,来实现服务端的变更响应到客户端,现在服务端也可以主动发送数据到客户端浏览器。WebSocket协议和Http协议平行,都属于TCP/IP四层模型中的第四层应用层。由于WebSocket握手阶段采用HTTP协议,所以也需要进行跨域处理。它的协议标识是ws或wss对应了常规标识和安全通信协议标识。本文重点并不是介绍WebSocket协议相关,而是提供一种基于ASP.NETCore原生WebSocket的方式实现集群的实现思路。关于这套思路其实很早之前我就构思过了,只是之前一直没有系统的整理出来,本篇文章就来和大家分享一下,由于主要是提供一种思路,所以涉及到具体细节或者业务相关的可能没有体现出来,还望大家理解。 实现 咱们的重点关键字就是两个WebSocket和集群,实现的框架便是基于ASP.NETCore,我也基于golang实现了

  • 【全网最全】springboot整合JSR303参数校验与全局异常处理

    一、前言 我们在日常开发中,避不开的就是参数校验,有人说前端不是会在表单中进行校验的吗?在后端中,我们可以直接不管前端怎么样判断过滤,我们后端都需要进行再次判断,为了安全。因为前端很容易拜托,当测试使用PostMan来测试,如果后端没有校验,不就乱了吗?肯定会有很多异常的。今天小编和大家一起学习一下JSR303专门用于参数校验的,算是一个工具吧! 二、JSR303简介 JSR-303是JAVAEE6中的一项子规范,叫做BeanValidation,官方参考实现是HibernateValidator。 HibernateValidator提供了JSR303规范中所有内置constraint的实现,除此之外还有一些附加的constraint。 Hibernate官网 官网介绍: 验证数据是一项常见任务,它发生在从表示层到持久层的所有应用程序层中。通常在每一层都实现相同的验证逻辑,这既耗时又容易出错。为了避免重复这些验证,开发人员经常将验证逻辑直接捆绑到域模型中,将域类与验证代码混在一起,而验证代码实际上是关于类本身的元数据。 JakartaBeanValidation2.0-为实体和方法

  • MVC模式简介

        MVC架构是一个复杂的架构,其实现也显得非常复杂。但是,我们已经总结出了很多可靠的设计模式,多种设计模式结合在一起,使MVC架构的实现变得相对简单易行。Views可以看作一棵树,显然可以用CompositePattern来实现。Views和Models之间的关系可以用ObserverPattern体现。Controller控制Views的显示,可以用StrategyPattern实现。Model通常是一个调停者,可采用MediatorPattern来实现。   现在让我们来了解一下MVC三个部分在J2EE架构中处于什么位置,这样有助于我们理解MVC架构的实现。MVC与J2EE架构的对应关系是:View处于WebTier或者说是ClientTier,通常是JSP/Servlet,即页面显示部分。Controller也处于WebTier,通常用Servlet来实现,即页面显示的逻辑部分实现。Model处于MiddleTier,通常用服务端的javaBean或者EJB实现,即业务逻辑部分的实现。 MVC的产生   MVC架构最早是smalltalk语言研究团提出的,应用于用户交互应

  • 《第一行代码》学习笔记37-服务Service(4)

    一个比较完整的自定义AsyncTask写成如下: classDownloadTaskextendsAsyncTask<Void,Integer,Boolean>{ @Override protectedvoidonPreExecute(){ progressDialog.show();//显示进度对话框 } @Override protectedBooleandoInBackground(Void...params){ try{ while(true){ intdownloadPercent=doDownload();//这是一个虚构的方法 publishProgress(downloadPercent); if(downloadPercent>=100){ break; } } }catch(Exceptione){ returnfalse; } returntrue; } @Override protectedvoidonProgressUpdate(Integer...values){ //在这里更新下载进度 progressDialog.setMessa

  • 正则表达式

    [ 子表达式的开始,如果要表示[符号需要用/[,[0-9]表示0-9的任意字符。 ] 子表达式的结束,如果要表示]符号需要用/]。 + 表示表达式需要出现1次或多次,例如[/+-]+表示+或者-必须出现至少1次,+符合,-也符合,a不符合。 ? 表示表达式需要出现0次或最多1次,例如[/+-]?表示+或者-最多出现1次,+符合,+-不符合。 *  表示表达式可以出现0次或多次,例如[/+-]*表示+和-可以出现0次或多次。 {n} {n}表示表达式必须出现n次,例如[ab]{1}表示[]中的字符必须出现1次,a符合,b符合,ab不符合。 {n,m} {n,m}表示表达式至少出现n次,最多出现m次。?就相当于{0,1}。

  • oracle创建表空间并赋予权限

    CREATETEMPORARYTABLESPACE表空间         TEMPFILE 数据存储路径('D://oracle//NEW_NAMESPACE.DBF')         SIZE32M         AUTOEXTENDON         NEXT32MMASIZEUNLIMITED         EXTENTMANAGEMENTLOCAL; createuser用户名identifiedby密码default tablespace 表空间名;复制 grantconnect,resourceto用户名;复制

  • c语言在线测试题

    http://www.233.com/ncre2/C/

  • 10+ powerful debugging tricks with Visual Studio

    10+powerfuldebuggingtrickswithVisualStudio Originallink: http://www.codeproject.com/Articles/359801/plus-powerful-debugging-tricks-with-Visual-Studi  

  • 组合数学--容斥原理&amp;鸽巢原理

    一次会议由1990位数学家参加,每人至少有1327位合作者.证明可以找到4位数学家,他们当中每两个人都合作优质解答这题可以分两步来做.第一,先证明一定有三个人,他们相互合作过.可以先找两个相互合作过的数学家A和B,他们每个人除去对方以外还和1326个人合作过,而1326*2>1990-2,所以一定有一个人,假设是C和A、B都合作过.第二,证明有4个数学家,他们之间相互合作过.考虑A、B、C的组合,他们每个人都和组合外的1325个人合作过,1325*2-1987=663,也就是说同时和A、B合作过的组合外的人有663个,而和C合作过的组合外的有1325个,1325+663>1987,所以,一定有一个人D和A、B、C都合作过,这样A、B、C、D他们就相互合作过.

  • 寒假学习02

    2

  • java中io和nio的区别

  • 如何用软件设计思维编写自动化测试脚本?

    如何用软件设计思维编写自动化测试脚本?   1、什么是自动化测试脚本? 自动化测试脚本是代替人工完成测试执行的工具,而应用软件的诞生也是为了帮助人完成一些复杂、冗余的工作。本质上来说,自动化测试脚本与应用软件的定位是一样的,只是使用是专业的测试人员而已。概括起来就是:自动化测试脚本是一款帮助测试人员完成测试执行工作的应用软件。 2、什么是软件设计呢? 概括起来,软件设计就是构建一套模型,制定一组规范。 (1)模型是一个软件的骨架,是定义一个软件之所以是这个软件的核心。举个栗子,我们都知道淘宝和知乎是两个不同的软件,为什么不同呢?因为他们的功能不一样,淘宝可以进行购物、支付等功能,是一个大型在线购物平台,而知乎可以发帖、阅读、讨论,是一个大型知识分享平台。把功能再继续抽象出来,就能得到淘宝的模型是:挑选商品--购买商品--售后服务。从功能上来说,知乎的模型抽象出来就是:查阅、分享、讨论。淘宝的模型是时序的,而知乎的模型是独立的。所以模型是区分软件的核心。反过来想,是不是很多软件的模型是差不多?是的,比如京东、天猫就和淘宝的模型是大同小异的。 (2)模型的粒度可大可小。一个模型在

  • Java深入学习28:Runnable和Callable

    Java深入学习28:Runnable和Callable  创建线程的四种方式(前两种没有返回值,后两种有) 继承Thread类 实现Runnable接口 实现Callable接口通过FutureTask包装器来创建Thread线程 使用ExecutorService、Callable、Future实现有返回结果的多线程 Runnable和Callable的区别 Runnable执行方法是run(),Callable是call() 实现Runnable接口的任务线程无返回值;实现Callable接口的任务线程能返回执行结果 call方法可以抛出异常,run方法若有异常只能在内部消化 注意 Callable接口支持返回执行结果,需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞! 如果线程出现异常,Future.get()会抛出throwsInterruptedException或者ExecutionException;如果线程已经取消,会爬出CancellationException   示

相关推荐

推荐阅读