软件测试岗位职责

软件测试岗位职责,明确着这个岗位的技术劳工,应该做什么,你在各种招聘明细中,可以查阅到这方面的信息。简单来讲,从劳工责任的角度,工作内容实际上就是不断的找到软件中的bug,并监督开发人员修复它们。造成这种简单的原因,从工业生产的方面,有不同的声音,大部分人觉得,这是利于工业发展的,让测试人员不断地找到软件问题,已经实现了他们的价值;但另有小部分人觉得,软件测试岗位还应该有更大的商业价值,甚至社会价值,而作者本人更倾向于小部分人的看法。 下面,我想更换一下“职责”这个词语,用“权责”来临时替换,说明一些观点。 软件测试岗位权责,并不是为了发现更多的bug,而是要预防更多的问题被产生。软件测试岗位有预防问题发生的权力,因此,它才有软件质量保证的责任。权力越大,责任越大,这个道理,很多软件公司并没有想清楚,测试应该在其中起到的作用和好处。我在一些小公司任职测试经理的时候,技术部的老大经常问到:“为什么我们的软件总是有那么多的bug?”,“为什么那么多个版本过后,还是有很多问题?”之后,便通过开发人员和测试人员加班来试图解决这一问题。这种现象,在很多公司并不少见。 那到底问题的关键在哪里呢? 问题的关键也许就在于测试在其中的作用,到底是“发现”,还是“预防”。如果只是“发现”,那么测试人员就会对bug出现的原因不会那么关心,即使关心,也会最终因为没有责任而自省麻烦,而机械的发现bug,对于专业的测试人员,并不是一件难事;与此同时,有了帮忙“发现”问题的队友,开发人员自然而然会降低开发中的自省成本,更快的完成工作任务,即使存在较多问题,也可以随理成章的进入到bug修复阶段来补偿。但如果是“预防”,测试人员更多的责任会重新发生变化,测试人员会更多的思考bug出现的原因,怎么样“尽早”发现问题,而不至于在下一个阶段,问题的数量变成10倍。 问题的关键还有另一个原因,这个原因同时也制约着“预防”的实施,这就是“权力”。软件测试在整个软件的生产线中,一般公司都让其处于比较被动的位置上,既要发现问题,又没有太多权力去解决问题,这里的问题,更多的还涉及到了一些由于管理不当,而产生的软件问题。很多老测试会有这样的经验,当测试一个软件产品一段时间后,自然而然就能发现很多公司管理上的本质问题,而解决这些问题,也许比督促开发同事修复bug,对产品的良好上线更有效果,但最终,有权力的解决者,也并没有解决好相关的问题。 软件测试岗位权责,在目前的软件生产中,更多的倾向于“责”,缺少“权”的结构,导致了软件质量本身更倾向于“被动式发现”,而缺少“主动预防”。主动预防的成本相对较高,一方面是本身预防性工作在问题被成功预防时,人们才能感觉到它的价值,另一方面,软件测试岗位对预防性人才的培育环境太过缺乏,虽然,目前任何一个软件测试招聘简章上,都要求自动化、性能、安全性的测试能力,但真正到岗后,能进行相关工作的机会却非常稀少,大部分都是黑盒测试。 软件测试岗位,应该获得更多的“权力”来真正实现“责任”。对于整个软件生产过程,我相信是更有利的一件事情。

chrome小恐龙作弊代码

chrome断网后的小恐龙游戏 F12打开开发者工具->console->输入如下代码,分数要多少有多少 Runner.instance_.setSpeed(99999); 试试 瞬间 满分 window.tempGameOver = Runner.instance_.gameOver; Runner.instance_.gameOver = function(){}// 不会死亡 经测试,执行完后自动跑分,不会死亡 js几个有趣的bug 1、使用JavaScript计算表达式时,会遇到输出结果是NaN的情况,这说明计算的表达式中含有非数字类型。 var aa = “abcd123”; isNaN(aa);//返回值为true 2、js过滤字符串中的数字 var a = “abc123”; parseInt(a);//返回值为数字123 3、常见运算错误 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 0.7999999999999999 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 0.8999999999999999 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 0.9999999999999999 console.log( 1 - 0.8 ); //输出 0.19999999999999996 console.log( 6 * 0.7 ); //输出 4.199999999999999 console.log( 0.1 + 0.2 ); //输出 0.30000000000000004 console.log( 0.1 + 0.7 ); //输出 0.7999999999999999 console.log( 1.2 / 0.2 ); //输出 5.999999999999999

STM32变量定义及位带操作

stm32变量定义 不需要自己定义,库函数中有声明 (stm32f10x.h),只需要写一个(stm32f10x.h)的头文件就可以了。 char 通常被定义成 8 位宽; int 通常被定义成 16 位或 32 位宽(或更高),它取决于平台(编译器将在这两者间选择最合适的字宽); short 通常被定义成 16 位宽; long 通常被定义成 32 或 64位宽。 参考链接:关于stm32数据类型的问题 参考链接:关于stm32变量定义的问题stm32位带操作 可类比51中的位操作:sbit LED = P1^0;,可直接对一组GPIO的某一位进行单独的定义和操作,如:PAout(1) = 1;相当于:GPIO_SetBits(GPIOA, GPIO_Pin_1);,要使用位带操作,需要加入以下代码: //位带操作,实现51类似的GPIO控制功能 //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页). //IO口操作宏定义 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 //IO口操作,只对单一的IO口!

SQL如何添加字段

添加字段: alter table 表名 add 字段名 类型; 删除字段: alter table 表名 drop column 字段名; alter table newexample add address varchar(110) after stu_id; 再来说明一下,首先使用alter table语句,然后跟要添加到的那张表,接着是要添加的字段,跟一个关键词 after 然后是要跟随的字段名。 mysql在指定的一个字段后面添加一个字段 alter table `manager` add `status` varchar(3) after `time`;

电脑cpu风扇转一下就停,不停重复,无法开机。

其实这个问题很简单。 从简单的开始排除, 1,检查内存条,擦一下金属脚即可。 2,把主板电池放电。 如果上面2个方法还是无法解决问题。 3,直接换掉电源就可以搞定。 我试验过。就这样结局问题的。 希望能帮到大家。

利用XSS进行网页钓鱼

【XSS漏洞】利用XSS进行网页钓鱼 Hello 各位小伙伴周末晚上好 这里是你们微胖的小编Monster。 话说小编的老妈今天给小编打电话,叮嘱我平时吃饭少吃一点… 说昨天看我朋友圈发的照片,已经从以前的瓜子脸,变成国字脸了… Whatever,让我们一起来看看今天的内容吧 一、实验概述 实验拓扑: 利用XSS漏洞,我们可以在网页中插入恶意js代码,通过js代码,我们可以干很多事情,例如伪造一个登陆页面。 当用户访问该网页时,就会自动弹出登陆页面,如果用户信以为真,输入了用户名与密码,密码就会传输到攻击者的服务器中,完成账号窃取。 二、编写登陆窗口代码 首先我们在攻击者服务器(192.168.211.1)编写一段弹窗代码,如下: 本段代码使用的是 PHP 的 HTTP 认证机制,说明如下。 首先,利用 header() 函数,我们可以向客户端浏览器发送 Authentication Required 信息,使其弹出一个用户名/密码输入窗口。 当用户输入用户名和密码后,包含有 URL 的 PHP 脚本将会和预定义变量 PHP_AUTH_USER、PHP_AUTH_PW 和 AUTH_TYPE 一起被调用,这三个变量分别被设定为用户名,密码和认证类型。 这三个预定义变量会被保存在 $_SERVER 数组中,我们再通过Get方法,将用户名、密码传递给record.php页面,等待下一步处理。 尝试访问一下这个页面,弹出登录框: 输入账号、密码123/123,通过GET方式发送给record.php: 这样,我们的登陆窗口页面就完成了,接下来编写记录页面。 三、用户信息记录 伪造的登陆界面,会把用户名、密码信息发送给record.php页面。 通过该页面,我们将用户名、密码保存到攻击者服务器本地,代码如下: 代码说明: 通过$_GET接收传过来的username和password的值 通过getenv方法,可以获取用户的ip地址和信息来源 通过date函数,记录下获取到信息的时间 通过fopen函数,通过a(追加)的方式,打开user.txt文件 通过fwrite的方式,将我们获取到的信息记录到user.txt文件中 最后,fclose函数关闭文件。 再访问一次登陆伪造页面,并输入用户名、密码123/123: 攻击者本地生成user.txt,成功记录账号、密码信息: 四、插入恶意js脚本 Web页面以DVWA平台 存储型XSS为例,我们来插入一个恶意JS代码。 代码构造如下: 通过插入iframe标签,让用户访问漏洞页面时,自动访问攻击者服务器上的钓鱼页面fish.php,出现登陆弹窗。 选择low安全等级,打开dvwa XSS(stored)页面 : 在Name栏、Message栏均存在存储型XSS,在Message中输入上面的恶意代码,并提交,会发现有输入长度限制: 不过这里是前端长度限制,我们直接修改当前网页代码即可,将maxlength改大: 再次输入,可顺利输入,点击提交即可: 当前页面马上出现弹窗: 现在我们使用靶机来模拟一次攻击行为,使用用户主机访问low安全等级的dvwa XSS(stored)页面,因为impossible等级不存在XSS漏洞, 立刻出现弹窗: 使用用户名、密码,点击确认,页面恢复正常: 查看攻击者服务器user.txt文件: 同时,输入用户名、密码后,当前页面已存在PHP_AUTH_USER、PHP_AUTH_PW 变量,因此再次访问该页面,不会再出现弹窗,避免露馅:

react 16.7 hook概述

Hook是向后兼容的,有react开发经验的看起来会更顺畅。 State Hook 看下面的例子,他是一个计数器 import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 在这里useState是一个Hook(我们将在稍后讨论这意味着什么)。可以看到,在这个函数组件里,我们向他添加一些本地状态。React将在重新渲染之间保留这状态。 useState返回一对:当前状态值(count)和允许你更新状态的函数(setCount)。你可以从事件处理程序或其他位置调用此函数。这个函数类似于类中的this.setState,但是它不会将旧状态和新状态合并在一起。(我们将在使用State Hook中显示一个将useState与this.state进行比较的示例。) useState的唯一参数是初始状态。 在上面的例子中,它是0,因为我们的计数器从零开始。请注意,与this.state不同,此处的状态不必是对象 - 尽管可以是任何你想要的。初始状态参数仅在第一次渲染期间使用。 声明多个state 你可以在一个组件中多次使用State Hook: function ExampleWithManyStates() { const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // .

Mac下使用ll命令

国庆前期,公司对于我们这波程序猿提供了4个mac机子,当然是别人淘汰下来的。对于我这种土鳖,从来都没有mac机子。不过所幸对Linux还是比较熟悉。但是mac机子上没有ll这个命令很是让我蛋疼。因为Linux命令用惯了,最后网上搜罗一番,终于找到了解决方案。这里记录一波: 打开终端,cd到home路径 cd ~ 编辑.bash_profile文件 vim .bash_profile 添加以下代码 alias ll=’ls -alF’ alias la=’ls -A’ alias l=’ls -CF’ 然后source文件 source .bash_profile 到这里就可以畅所欲为的在mac机子下面执行ll命令了。 Be the First to comment.

微信小程序开发--如何在swiper中显示两个item以及下一个item的部分内容

如何在swiper中显示两个item以及下一个item的部分内容 我所实现的效果 我实现的代码 <!--图片轮播图--> <!--要展示两个item 以及下一个item的部分内容,circular需要设置为false,或者不设置,display- multiple-items设置为2,next-margin按照自己的需求设置--> <swiper indicator-dots = false vertical = false autoplay = false display-multiple-items='2' next-margin='150rpx' class="image_group"> <block wx:for="{{item.images}}" wx:for-item="photo" wx:key="*this"> <swiper-item> <view class='swiper_item'> <image src="{{photo}}" mode="aspectFill"></image> </view> </swiper-item> </block> </swiper> /*轮播图*/ .image_group{ position: relative; margin-left: 20rpx; margin-top: 20rpx; height: 260rpx; } .swiper_item { width: 250rpx; height: 250rpx; overflow: hidden; border-radius: 10rpx; } .swiper_item image{ width: 250rpx; height: 250rpx; } 注意细节 要展示两个item 以及下一个item的部分内容,circular需要设置为false,或者不设置,否则会出现滑动时部分item不显示问题,display-multiple-items设置为2,next-margin按照自己的需求设置,它的数值决定了下一个item所展示部分的宽度 展示3.5、4.5、5.5个item实现同理 以上内容均为个人通过测试所得的效果,如果不妥,欢迎评论纠错

Java8 List 与以逗号分割String类型相互转换

//java8特性 String ids= "1,2,3,4,5,6"; List<Long> idList = Arrays.asList(ids.split(",")).stream().map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); //将以逗号分割的字符串转换成List<Long>类型 String ids = StringUtils.join(idsList, ",") StringUtils.strip(list.toString(),"[]");

jmeter的时间戳函数使用

原文:https://blog.csdn.net/jocleyn/article/details/83414433 1、__time:获取时间戳、格式化时间 (1)、${__time(yyyy-MM-dd HH:mm:ss:SSS,time)} :格式化生成时间格式 2018-10-26 11:08:23:635 (2)、${__time(,)}:默认该公式精确到毫秒级别, 13位数 1527822855323 (3)、${__time(/1000,)}:该公式精确到秒级别, 10位数 1527822871 (4)、${__time(yyyy-MM-dd,)}:该公式格式化生成的时间为:2018-10-26 (5)、${__time(yyMMdd,)}:该公式格式化生成的时间为:181026 13位数时间点 //start_time 开始时间 当前时间 String start_time = "${__timeShift(,,,,)}"; vars.put("Start_time",start_time); log.info("Pre_start_time++++++++++++=========="+start_time); //end_time 当前时间点后2天 String end_time = "${__timeShift(,,P2D,,)}"; vars.put("End_time",end_time); log.info("Pre_end_time++++++++++++=========="+end_time); 接口获取时间点前后一定间隔的时间函数: __timeShift(时间格式, 特定时间点(缺省当前时间),时间间隔,地区格式(默认),变量名( 可不填,填写后其他地方用${变量名}引用 )) 举例: 1 特定时间点后一周:${__timeShift(yyyy-MM-dd,2018-11-30,P7D,,)} 2 当前时间点后1分钟:${__timeShift(yyyy-MM-dd HHmmss,,PT1M,,)} 3 当前时间点前30秒 :${__timeShift(yyyy-MM-dd HHmmss,,PT-30S,,)} 或 ${__timeShift(yyyy-MM-dd HHmmss,,-PT30S,,)} 4 当前时间点后12小时: ${__timeShift(yyyy-MM-dd HHmmss,,PT12H,,) 5 当前时间点前1个半小时:${__timeShift(yyyy-MM-dd HHmmss,,-PT1H30M,,)} 6 当前时间 2天6小时前的时间点:${__timeShift(yyyy-MM-dd HH:mm,,-P2DT6H,,)} -2天-6小时 7 注意负号的位置 ${__timeShift(yyyy-MM-dd HH:mm,,P-2DT6H,,)} -2天+6小时 8 地区格式 ${__timeShift(dd.

Spring中使用LocalDateTime、LocalDate等参数作为入参数据转换问题

0x0 背景 项目中使用LocalDateTime系列作为dto中时间的类型,但是spring收到参数后总报错,为了全局配置时间类型转换,尝试了如下3中方法。 注:本文基于Springboot2.0测试,如果无法生效可能是spring版本较低导致的。PS:如果你的Controller中的LocalDate类型的参数啥注解(RequestParam、PathVariable等)都没加,也是会出错的,因为默认情况下,解析这种参数使用ModelAttributeMethodProcessor进行处理,而这个处理器要通过反射实例化一个对象出来,然后再对对象中的各个参数进行convert,但是LocalDate类没有构造函数,无法反射实例化因此会报错!!! 0x1 当LocalDateTime作为RequestParam或者PathVariable时 这种情况要和时间作为Json字符串时区别对待,因为前端json转后端pojo底层使用的是Json序列化Jackson工具(HttpMessgeConverter);而时间字符串作为普通请求参数传入时,转换用的是Converter,两者有区别哦。 在这种情况下,有如下几种方案: 1. 使用Converter import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.http.converter.HttpMessageConverter; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Configuration public class DateConfig { @Bean public Converter<String, LocalDate> localDateConverter() { return new Converter<>() { @Override public LocalDate convert(String source) { return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd")); } }; } @Bean public Converter<String, LocalDateTime> localDateTimeConverter() { return new Converter<>() { @Override public LocalDateTime convert(String source) { return LocalDateTime.parse(source, DateTimeFormatter.

codeforces 787D Legacy (线段树)

题目链接:http://codeforces.com/problemset/problem/787/D Description Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them. There are n planets in their universe numbered from 1 to n. Rick is in planet number s (the earth) and he doesn't know where Morty is. As we all know, Rick owns a portal gun.

我的STM32学习日记(一)

1.网络学习资源 开源电子网 www.openedv.com ST中国官方技术论坛 www.stmcu.org 2.常用词 MCU单片机 WDT看门狗定时器 SRAM静态随机存取存储器 DMA直接内存存取 TIM定时器

论文投稿系列之Cover Letter写法(一)

论文投稿系列之Cover Letter写法(一) 研之成理 微信公众号:研之成理(ID: rationalscience) 取消关注 PytLab酱 等 87 人赞同了该文章 作者:ZSH 1. 什么是Cover letter? Cover Letter, 即投稿信,是论文投递时与论文一起发送给编辑的信件,其目的是让编辑在阅读你的论文之前,简单了解你文章的基本情况。Cover letter是编辑对论文的第一印象,也是初步评判你论文是否可以被期刊接收的重要依据(如果编辑看完Cover letter之后一点兴趣也没有,就没有下文了),因此Cover letter的撰写是非常重要的。如何用简明扼要的语言抓住编辑的心是Cover letter撰写时最关键的一点! 2. Cover letter应该包含哪些内容? 完整的Cover letter一般包含以下内容: A. 期刊编辑的姓名(不知道编辑是谁的情况下直接用Dear editor); B. 投稿文章的标题 C. 投稿文章的类型(Letter, communications, article, review还是comments) D. 文章简介,包括:(1)研究背景;(2)论文的重要发现;(3)论文可以发表在期刊上的原因:引发读者兴趣的地方,与期刊的契合之处等等 E. 稿件出版道德规范的免责说明(这个部分很多期刊在网站上投稿时会进行确认,cover letter里面可以不出现)以及对稿件有无特殊处理要求(一般指屏蔽某些竞争者成为审稿人) F. 作者信息:一般为通讯作者姓名,所属机构,通讯地址,联系电话,邮箱等 G. 推荐审稿人名单(注:目前很多杂志社已经将这一部分挪到了投稿网站系统上,Cover letter中可以不再列出) 3. Cover Letter的具体写法示例: A. 论文通讯作者的信息,可以放到前面也可以放到后面,这个主要看个人习惯: i) 放到最前面时,一般采用如下格式: 底下紧接Dear Editor... ii) 放到后面的话,紧接作者署名的后面,可以简单地用下面的格式: B. Cover letter第一段 Dear editor(如果确切知道编辑名称,可以直接写其名字), We are submitting a manuscript entitled “论文标题” for your consideration for publication as a communication in 期刊名.

FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明

FatFs 是用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 模块的编写符合 ANSI C(C89),并与磁盘 I/O 层完全分离,因此它独立于硬件平台。 它可以集成到资源有限的小型微控制器中,例如 8051,PIC,AVR,ARM,Z80,RX 等。此外,还提供用于微型微控制器的 Petit FatFs 模块。 看本文时需要有点 FAT 文件系统的基础,可以参考FatFs 之三 FAT文件系统基础、FAT 数据格式、引导、编码。 变更记录 具体参见源码文件中的 /source/00history.txt 即可!也可以去官网查看。从中我们可以看到修复的各问题,尤其是源码文件的变动。例如:在 R0.13c 中,原来独立文件 integer.h 中的内容被直接包含在了 ff.h 中,原来的 integer.h 被删除!对比如下图所示: 源码目录文件 目前,最新版本为 R0.13c。相比于之前的版本,源码有了一定的变化(参见上图)。FatFs 的源码包中,文件非常简单。其源码目录结构如下所示(对于简单的文件以注释的形式给出,核心源码下文会详细说明): FatFs R0.13c │ LICENSE.txt // 版权说明 ├─documents // 配套的说明文档 └─source 00history.txt // 更新历史记录 00readme.txt // 对于以下每个文件的功能简介 diskio.c // FATFS 与硬件的接口实现文件模板 diskio.h // FATFS 与硬件的接口实现文件模板 ff.c // FATFS 核心源代码 ff.h // FATFS 核心源代码 ffconf.

内存图片二进制数据直接转为 OpenCV 数据格式的方法

想了多种方法解决这个问题,还是百度的力量大。 在很多应用中,经常会直接把图片的二进制数据进行交换,比如说利用 socket 通信传送图片二进制数据,或者直接用内存数据库(例如 Redis)来传递图片二进制数据。 这个时候,当你的应用程序读到内存里的二进制图片数据时,怎么样直接转为 OpenCV 可以使用的图片格式呢,答案是用 cv::imdecode 这个函数。 即先构造一个 char 字符串序列的 vector,用来存储图片的二进制数据,然后再转为 cv::Mat 成为可以被 cv::imdecode 使用的数据格式,然后直接类型转换为 IplImage 数据格式。 同样,如果你需要把 IplImage 或 cv::Mat 压缩并写到一段内存块里时,就需要使用 cv::imencode 这个函数,使用方法类似。 具体的实现代码参考: string fname = "D:/image.jpg"; //! 以二进制流方式读取图片到内存 FILE* pFile = fopen(fname.c_str(), "rb"); fseek(pFile, 0, SEEK_END); long lSize = ftell(pFile); rewind(pFile); char* pData = new char[lSize]; fread(pData, sizeof(char), lSize, pFile); fclose(pFile); //! 解码内存数据,变成cv::Mat数据 cv::Mat img_decode; vector<uchar> data; for (int i = 0; i < lSize; ++i){ data.

.net Core Jwt登录验证刷新Token

使用 jwt做登录验证的时候,token的过期时间的固定的, 也就是只要经过了一定的时间肯定会过期。有可能用户用着系统就突然提示登录失效了。这里通过在行为结果过滤器Result Filter中刷新Token。例如设置token失效时间为20分钟, 那么只要用户在20分钟之内没有访问后台接口,token将失效。 代码只是个范例,不严谨。小伙伴们自己修改吧!别报空引用哦~~~ public class MyResultFilter : IResultFilter { public void OnResultExecuted(ResultExecutedContext context) { Console.WriteLine("AddHeaderResultFilter:OnResultExecuted"); } /// <summary> /// 在结果过滤器中刷新jwt的token /// </summary> /// <param name="context"></param> public void OnResultExecuting(ResultExecutingContext context) { //获取当前请求的Token string tokenOld = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault().Split(' ')[1]; var tokens = new JwtSecurityTokenHandler().ReadJwtToken(tokenOld); var temp = tokens.Claims; var claims = new List<Claim>(); claims.AddRange(temp.Where(t => t.Type != JwtRegisteredClaimNames.Iat)); //重置token的发布时间为当前时间 string time = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); claims.Add(new Claim(JwtRegisteredClaimNames.Iat, time, ClaimValueTypes.Integer64)); var now = DateTime.

TCP传输协议中如何解决丢包问题

TCP在不可靠的网络上实现可靠的传输,必然会有丢包。TCP是一个“流”协议,一个详细的包将会被TCP拆分为好几个包上传,也是将会把小的封裝成大的上传,这就是说TCP粘包和拆包难题。 但是许多人有不同的理解。TCP协议本身确保传输的数据不会丢失完整性。如果在传输过程中发现数据丢失或数据包丢失,最大的可能性是在发送或接收程序的过程中出现问题。 例如,服务器向客户端发送大量数据,并且发送频率非常高,因此发送链接中很可能会出现错误(1、程序处理逻辑错误;2、多线程同步问题;3、缓冲区溢出等)如果发送失败得不到处理,那么客户端收到得数据将少于理论数据,这将导致数据丢失与数据包丢失。这种现象,其实本质上来说不是丢包,也不是丢数据,只是因为程序处理有错误,导致有些数据没有成功地被socket发送出去。 关于send函数的问题: 首先必须明确send函数做了什么。 他是将数据传递给本地TCP层,还是将数据传递给应用层,确认接收方TCP层后再返回。在后者的情况下,你说的没错,其实不然。 那是由于nagle算法不能使用了,即该算法将send函数接收的小数据汇总成大数据包发送。 即使send函数能进行数据发送,对方也不一定被接受。 TCP协议只是在传输层履行义务,send函数只是应用层起到向TCP层传递数据的作用,除此之外与TCP层没有任何关系。 常见的解决方案包括拆包、添加包头和发送组合包。如果服务器或客户端断开连接,一般会使用心跳测试。 心跳测试:每隔一段时间向服务器发送数据包。为了节省资源,通常会发送空数据包。如果发送失败表明套接字已断开,此时需要根据特定条件释放资源并重新连接。 TCP传输可以保证数据交换的可靠性,这意味着一台主机将数据正确地传输到目标计算机,目标计算机的协议栈有一定的限制,如果不及时处理在目标计算机上接收到的数据,堆栈就会溢出。 这种溢出不是由TCP协议本身引起的,而是由系统的IP协议栈的缓冲区溢出引起的!

CSS 中奇数选择器与偶数选择器

div标签下的奇数行与偶数行 div:nth-child(odd){} //奇数行 div:nth-child(even){} //偶数行 :nth-child(n) 选择器匹配属于其父元素的第 N 个子元素,不论元素的类型。

古月居ROS入门21讲笔记

ROS入门21讲笔记——古月居 1 C++&Python极简基础1.1 安装编译/解析器1.2 for循环1.3 while循环1.4 面向对象 2. ROS基础2.1 ROS概念2.2 创建工作空间与功能包2.3 发布者Publisher的编程实现2.4 订阅者Subscriber的编程实现2.5 话题消息的定义与使用2.6 客户端Client的编程实现2.7 服务端Server的编程实现2.8 服务数据的定义与使用2.9 参数的使用与编程方法2.10 tf坐标系广播与监听的编程实现2.11 launch启动文件的使用方法2.12 常用可视化工具的使用2.12.1 rqt2.12.2 Rviz2.12.3 Gazebo 1 C++&Python极简基础 1.1 安装编译/解析器 sudo apt-get install g++ sudo apt-get install python 1.2 for循环 Python for a in range(5,10): if a< 10: print 'a = ',a a+=1 else: break 使用Python解析器运行py程序 python fileName.py C++ 略 使用g++编译*.cpp文件 g++ fileName.cpp -o exeFileName 运行编译后的二进制文件 ./exeFileName 1.3 while循环 C++ 略

GN介绍

GN INTRODUCTION What’s GN 1、GN Generate Ninja,是Google为Ninja专门开发的上层编译框架,可以生成Ninja可以识别的输入文件。GN由c++编译,相比于基于python的gyp,速度快接近20倍。 2、GN Language 由于GN和GYP都可以生成Ninja,所以我们可以稍稍将GN和GYP进行对比。风格方面,GYP有Json格式的文件组成,可以将所有的GYP文件和GYPI文件看成一个大的Json格式文件。而GN是函数式风格,最后的依赖关系可以由栈式函数调用来组成。 变量 和大多数脚本语言一样,GN的变量种类不多,包含字符串(string),整形(int64),布尔(Boolean),列表(List),字典(dictionary)。使用方式也和脚本语言一样,不需要声明变量类型,直接对变量赋值即可,例如 a=”hello world”。 函数 和GYP是基于JSON格式的组织方式不同,GN使用函数来组织依赖。 例如: a:简单函数调用: print("hello, world") b:复试函数调用 static_library("mylibrary") { sources = [ "a.cc" ] } 这种调用乍一看以为是函数定义,其实是一种函数调用,可以将函数体的返回值理解为函数的一个参数,上述例子的含义就是使用源文件a.cc编译出静态库mylibrary 目标 和Makefile中的目标类似,GN中的目标通常也就是一种可执行文件或者依赖库。一个目标可以依赖另一个目标,从而形成依赖树。 action: Run a script to generate a file. action_foreach: Run a script once for each source file. bundle_data: Declare data to go into a Mac/iOS bundle. create_bundle: Creates a Mac/iOS bundle. executable: Generates an executable file. group: A virtual dependency node that refers to one or more other targets.

电商项目-订单模块

72. 显示确认订单信息页 显示“确认订单信息”页面中,需要显示2种数据:当前用户的所有收货地址列表;用户在前序页面中选择的购物车中的商品。 首先,完成“显示当前用户的所有收货地址的列表”,在此前开发“收货地址”相关功能时,已经可以通过/addresses/这个URL获取收货地址列表!则直接在orderConfirm.html中通过$.ajax()获取数据并显示即可! 接下来,应该“显示用户在前序页面中选择的购物车中的商品的列表”,对应的查询功能的SQL语句大致是: select * from t_cart left join t_product on t_cart.pid=t_product.id where cid in (?,?,?) 所以,需要在CartMapper.java接口中添加: List<CartVO> findByCids(Integer[] cids); 在CartMapper.xml中配置以上抽象方法的映射: <select id="findByCids" resultType="xx.xx.xx.CartVO"> SELECT cid, uid, pid, t_cart.num, t_cart.price, t_product.price AS realPrice, title, image FROM t_cart LEFT JOIN t_product ON t_cart.pid=t_product.id WHERE cid IN <foreach collection="array" item="cid" seperator="," open="(" close=")"> #{cid} </foreach> ORDER BY t_cart.created_time DESC </select> 完成持久层后,接下来,需要在ICartService业务层接口中添加抽象方法,以对外提供数据访问功能: List<CartVO> getByCids(Integer[] cids, Integer uid); 然后,在实现类中,先私有化编写持久层的方法: private List<CartVO> findByCids(Integer[] cids) { return cartMapper.

aardio类的例子

论坛里面相关资料太少,这里贴一下 库需要在工程的lib目录下,在ide里面就是用户库目录,比如 my_lib namespace my_lib{ import console class MyLibClass { ctor(...){ ..console.log("in ctor of mylibclass"); } my_lib_function=function(...){ ..console.log("in my_lib_function"); return "my_lib_function"; } } } main文件 import my_lib import console test = my_lib.MyLibClass() info = test.my_lib_function() console.log(info) console.pause() 转载于:https://www.cnblogs.com/ziyouchutuwenwu/p/11203147.html

字节跳动秋招提前批(计算机视觉工程师)

一面 做过的所有项目:巴拉巴拉 池化有哪些方法,分别用在什么场景下BN 的原理激活函数有哪些?ReLU 的优缺点第 K 大的数 一面项目问得多,其它问题比较少,可能我比较菜,莫名其妙就过了。。。 二面 最近的项目:巴拉巴拉 focal loss 如何实现难分样本训练,如何解决正负样本不均衡?anchor-free 论文读过哪些?NMS 的原理,什么情况下不 work? 如何解决?如何根据实例分割的标注,圈出实例的边界框?Kmeans 原理介绍下深度学习框架如何实现数据的加载?编程:计算两个框的 IOU有什么问题问我? A:下一场面试时间 B:公司员工培养模式 C:有什么建议 就记得这么多,有得有失,但是总体氛围还好,一面的面试官感觉可能是面试太多有些疲惫,二面的体验很好。 最后面试官给的建议很有用: 做 CV 的需要懂一些图像处理的知识。(膨胀、腐蚀看我没做过就没问)基础原理要懂,知其然知其所以然(所以准备撸一下源码了。。。)数据结构与算法的能力(这里我基本都写出来了,比较简单,所以勉强算亮点?)

后台挂起,让服务器运行,客户端崩溃也可以继续运行

nohup表示挂起, > loginfo_trainer.txt 2>&1 &表示将trainer里面的print信息自动写入当前目录的loginfo_trainer.txt文件(如果该文件不存在则会自动创建)。暂时可改的为py文件,要写入的文件名字和目录 nohup python -u trainer.py > loginfo_trainer.txt 2>&1 & 终端查看上面写入的txt文件的信息: tail -f loginfo_trainer.txt # 动态更新查看信息 tail loginfo_trainer.txt # 查看当前信息

小程序中navigator和wx.navigateTo,wx.redirectTo,wx.reLaunch,wx.switchTab,wx.navigateBack的用法

一句话说明就是: navigator是在 wxml 中用标签添加open-type属性来达到和wx.xxx系列一样的效果. wx.switchTab(Object object) 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面(官网是这么说的,具体是先关闭再跳转还是先跳转后关闭,本人不知道。) wx.reLaunch(Object object) 关闭所有页面,打开到应用内的某个页面 wx.redirectTo(Object object) 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。 wx.navigateTo(Object object) 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。 使用 wx.navigateBack 可以返回到原页面。小程序中页面栈最多十层。 wx.navigateBack(Object object) 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层。 遇到的bug 写小程序,遇到页面跳转时,发现有几次失败。查询资料已解决,总结一下知识点: 一、如下,第5层到到6层时失败(评论页⑤–>返回商品详情页⑥) 登陆①–>主页②–>商品列表页③–>商品详情页④–>评论页⑤–>返回商品详情页⑥ wx.navigate :可以基本满足页面之间跳转需求,但是层级关系不要超过5层,最多5层。wx.redirectTo :当层级关系超过5层时,页面跳转采用这个。 二、第2层到到3层时失败(反馈建议②–>返回"我的"页③) 我的①–>反馈建议②–>返回"我的"页③ 此时,没有超过5层,但wx.navigateTo和wx.redirectTo都无效, 后来查了小程序的API,找到了原因,因为"我的"页是tabbar页面。 wx.switchTab: 需要跳转到tabbar页面,需要用 wx.switchTab 三个跳转的使用方法一致,(只是是否能带参数问题)均为: wx.switchTab(Object object) 需要跳转的 tabBar 页面的路径(需在 app.json 的 tabBar 字段定义的页面),路径后不能带参数。 wx.switchTab({ url: '/index' }) wx.reLaunch(Object object) 需要跳转的应用内页面路径,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 ‘path?key=value&key2=value2’ wx.reLaunch({ url: 'test?id=1' }) 需要跳转的应用内非 tabBar 的页面的路径, 路径后可以带参数。参数与路径之间使用 ?

类似携程,飞猪机票列表滚动的日期带价格

近期在优化我们公司的机票查询的价格日历(跟携程,飞猪的机票列表类似),之前是将所有切换的日期全部展示在网页中,由于默认查询日期是一年,会导致页面上存在大量的<li>,假如哪天产品要求日期默认是10年呢,估计页面要卡死了. 优化结果先看下最终的效果图: 可以看到以下两点: 1.不管怎么滚动,下面的<li>数量永远是固定的,不会随着滚动日期的增加 而累加<li>的数量,大大提高了页面的性能 2.点击了日期后,请求当天的价格,不替换<li>的情况下将"查看价格"赋值为具体的金额 技术支持: 对日期的处理主要使用的是moment.js 好了话不多说,上代码: 首先对日期做处理,设置了初始日期和截止日期,会返回这两个日期之间所有的日期,另外还会补齐已经过期的时间,和不可选择的时间,如果用户设置了当前日期,也会有标识出来,具体代码如下(下面代码要使用moment.js): // 只负责处理日期,不负责处理业务 /*返回某时间段的所有日期数据,并格式化,并根据是否今天还是明天还是后天加了classname*/ var dateJson = [] function Calendar() { this.settings = { startDate: '2019-05-10', // 开始时间 endDate: '2019-10-15',// 结束时间 dateJson: dateJson, // 日期上的标识数据,class名字要和这个标识字段一摸一样,例如json里面的“折”是discount,那么css名字也叫.discount defaultSelectDate: ['2019-07-02', '2019-07-09'] // 选中的日期 } } Calendar.prototype.init = function (opt) { $.extend(this.settings, opt); return (this.getAllDate(this.settings.startDate, this.settings.endDate)); } Calendar.prototype.pushTag = function (yearMonthDay) { var tags = {}; for (var i = 0; i < this.settings.dateJson.length; i++) { if (moment(yearMonthDay).

推荐几个清华交大学霸的公众号

技术开发过程中,及时关注圈内动态,高效获取优质资源,对我们非常重要。认真推荐下面这几个公众号,它们经常发布优质信息(比如互联网前沿、技术干货等),非常大家学习,大佬们都在关注,需要就扫码关注吧! Python爱好者社区 Python爱好者社区,你想学的这里基本都有,已经积淀了数千篇优秀文章,例如技术方面的:Python入门,进阶,数据结构与算法,面向对象编程,数据分析挖掘,机器学习,深度学习,自然语言处理。业务方面的:数据分析师能力培养系列等。还有不定期的免费公开课和送书活动等你来撩。 我爱计算机视觉 一个纯粹的计算机视觉技术公众号,秉持“有价值、有深度”的原则,专注分享计算机视觉、机器学习、深度学习技术的最前沿,技术方向包括:OCR、SLAM、GAN、目标检测、语义分割、姿态估计、目标跟踪、人脸识别、医学影像处理与识别、遥感与航空影像处理识别、计算机视觉竞赛等。欢迎关注! 网站主页:www.52cv.net Python那些事 人生苦短,我用 Python。Python 越来越受广大程序员的喜爱。「Python那些事」致力于做最好的Python公众号,只为爱Python的你!公众号主要分享 Python 开发相关的技术文章、面试算法、工具资源和热门教程等。 宏基因组 宏基因组/微生物组是当今世界科研最热门的研究领域之一,为加强宏基因组学技术和成果交流传播,推动全球华人微生物组领域发展,中科院青年科研人员创立“宏基因组”公众号,联合海内外同行共同打造本领域纯干货技术及思想交流平台。 公众号每日推送,工作日分享宏基因组领域最新成果、科研思路、实验和分析技术,R语言统计绘图,理论过硬实战强;周末科普和生活专栏,轻松读文看片涨姿势。目前分享900+篇原创文章,49000+小伙伴在这里一起交流学习,欢迎投稿,感兴趣的赶快关注吧! AINLP 致力于做一个有趣,有AI的NLP公众号,作者是我爱自然语言处理博客博主,曾在腾讯从事文本挖掘相关工作。AINLP关注深度学习、机器学习、NLP、Python相关技术,职位和课程;公众号直接对话双语聊天机器人、调戏夸夸机器人、尝试自动对联、作诗机,使用中英机器翻译,查询相似词,计算相似度,玩词语加减游戏,测试NLP相关工具包,欢迎来聊,欢迎关注。 人工智能爱好者社区 专注人工智能、机器学习、自然语言处理、图像识别等顶尖技术前沿科技成果研究、实战技巧。每周会有书豪采访记系列文章和原创漫画文章。立即关注,掌握人工智能最新资讯与成果。 计算机视觉life 计算机视觉是人工智能时代的眼睛。公众号兼具系统性,严谨性,易读性,已发布一系列原创文章包括:视觉SLAM技术、深度/机器学习、深度相机、入门科普、CV方向简介、手机双摄、全景相机、相机标定、医学图像、前沿会议、机器人、ARVR、行业趋势等。不定期会有大牛来直播,欢迎关注置顶,加实名微信群,一起进步。 机器学习算法工程师 致力于为机器学习、深度学习、数据挖掘等AI技术的“初学者”或者“爱好者”,进行基础理论与实战技能的介绍和学习。我们团队成员既有各个著名院校的在校硕士生、博士生,也有BAT一线资深工程师,我们会竭诚为您服务! ▲ 人工智能头条 既关注「技术 」又关注「技术人 」,本公众号主要分享AI领域前沿资讯和干货文章,关注“人工智能头条”,还可以跟可爱的AI机器人聊天!欢迎投稿或吐槽。 前程序员陈彼得 我是陈彼得,一名聪慧不绝顶的资深(中年)程序员,欢迎扫码关注我的公众号,获取更多程序员 生发 生存秘笈。

深入浅出Flask(4) --Flask run and Python py

讨论一个小问题,关于启动Flask程序的两种方式。 对于Flask run指令,如果你只是配置了app.config['debug']=True的话,会发现并没有启动调试模式; 对于Python demo.py,如果你使用了flask-dotenv扩展后,创建了0.11版本之后Flask推荐使用的".env"和'.flaskenv"文件,并在".flaskenv"种配置了:"FLASK_ENV=development",你会发现同样没有启动调试模式。 看完两个例子,大家心中应该有了答案,也就是说Flask run指令对应的是.env的配置;普通的Python指令使用的是Debug=True配置,那么这两个到底有什么区别呢? 简单来说,Flask run是服务器层面上的操作,告诉你使用的是development服务器;而普通的Python指令只是从config中读取到Debug==True而已,他并不知道服务器到底有没有运行,也不关心这个。 从代码层面上猜测,Flask-cli.py中配置了run指令的方法,即使也使用到了config中的变量,并不影响开发服务器的运行,同样我们使用普通的python指令时也仅仅读一下Debug而已。殊途同归, 他们都能启动开发服务器。 之所以没有贴源码分析,毕竟这两种方式都不是在生产环境中使用的,毕竟Werkzeug提供的只是简易的服务器,到最后更多是使用Nginx,因此也没必要分析的太深,等详细学习源码的时候再做扩展。 还有: 除此之外,对于.flaskenv,尽管我们可以设置FLASK_ENV等值,但要注意如果运行前我们在外部更改了这个值,外部更改得到的值优先度是最高的,其次是.env中的值,最后才是.flaskenv中的值。 这也是为什么在Pycharm集成的Flask Cli中,FLASK_ENV和FLASK_DEBUG的值是固定的,不会因为在.flaskenv中改变而改变,调整这个值必须通过下图中设置改变: 最后: FLASK_DEBUG值一般不推荐手动设置,毕竟development开启时一般是默认设置Debug开启的 以上

qt显示较大图片时速度很慢怎么处理

qt显示较大图片时速度很慢怎么办 QImage myImage(pBuffer, Width, Height, QImage::Format_RGB888); QPixmap pixmap = QPixmap::fromImage(myImage); ui->displayLabel->setPixmap(pixmap);</span> 我是在scroll area中用Qlabel来显示图片的,显示的是图像原始大小,不进行缩放操作。pBuffer存储的是我读取到内存中图像数据。因为图像比较大,显示的速度很慢,一般一幅100M左右的图像显示要10s左右。发现主要耗时的是QPixmap pixmap = QPixmap::fromImage(myImage);这一步。不知道有没有什么办法可以加快速度。 ------解决方案-------------------- 真够大的,100M放内存。 直接用QWidget加上QPainter,使用drawImage()看看有没有提升,少一步到QPixmap的转换。 ------解决方案-------------------- 处理过大图,还真没见过不能缩放的需求,我之前做的都是可以缩放的,对图片做了处理,比如金字塔、分块加载显示之类的

Redis的三种集群结构

redis有三种集群方式:主从复制,哨兵模式和集群。 一,主从复制 主从复制是指让一个服务器去复制另一个服务器的数据,使得双方的数据保存一致,其中被复制的服务器为主服务器,复制的服务器为从服务器。当主服务器的数据发生改变时,主服务器会通知从服务器,保存数据的一致性。 在Redis中,使用slaveof命令来执行主从复制。 (一)旧版的主从复制实现 在旧版中,Redis的复制功能分为两步:同步和命令传播。 同步操作用于将从服务器的数据状态更新为主服务器的数据状态。 命令传播操作用于在主服务器的数据被修改时,通知从服务修改对应的数据,从而达到数据一致。 从服务器向主服务器发送slaveof命令后: 1. 从服务器向主服务器发送sync命令,主服务器会执行bgsave命令,在后台生成一个RDB文件,并用一个缓冲区记录从现在开始的所有写命令。 2. 从服务器接收并载入RDB文件,更新自己的数据。更新完成之后,主服务器会发送缓冲区中的命令,从服务器执行这些命令,最终主从服务器的数据保存一致。 3. 主从服务器保存一致后,当主服务器的数据发生修改时,会对从服务器执行命令传播操作,即将修改命令发送给从服务器,使得数据继续保存一致。 缺陷: 旧版的主从复制的实现有个很大的缺陷:在从服务器发生断线并重连之后,主从服务器之间会重新执行同步操作,即将主服务器的所有数据重新传到从服务器中。但实际上,在重连之后,主从服务器之间可能只有少量数据不同,大部分保存一致。这就使得有很多数据是无效的,效率极低。 (二)新版的主从复制实现 为了解决主从复制效率底下的问题,Redis从2.8开始,使用psync命令替代sync。 psync命令有两种模式:完整重同步和部分重同步。 完整重同步用于初次复制情况,和同步操作一致。 部分重同步用于断线重连后复制情况。 部分重同步的实现: 部分重同步,当从服务器断线重连后,如果条件允许,主服务器可以将从服务器断线期间执行的写命令发送给从服务器,从服务器只要接收并执行命令,就可以更新数据,更主服务器实现数据一致。 部分重同步使用了复制偏移量和复制挤压缓冲区来实现。 复制偏移量:主服务器和从服务器都各自维持着一个复制偏移量。主服务器每次向从服务器发送N个字节的数据时,就会在自己的复制偏移量上加N,从服务器每次接受都主服务器的N个字节的数据时,就会在自己的复制偏移量上加N。通过对比主从服务器的复制偏移量就很容易知道主从服务器的数据是否处于一致。 当从服务器断线重连后,根据主从服务器之间复制偏移量的差,主服务器就可以发送相应的数据给从服务器,即从服务器在断线之后被修改的数据。 而主服务器发送给从服务器的数据是存在复制积压缓冲区中。 复制积压缓冲区是一个固定长度的先进先出队列,由于其长度固定,当队列已满时,在数据入队时,队列最前面的数据必须要先出队,即复制积压缓冲区值保留固定大小的数据。 当主服务器进行命令传播操作时,不仅将写命令发送给从服务器,还会写入到复制积压缓冲区中,并记录下缓冲区中每个字节数据的复制偏移量。当从服务器重连后,如果从服务器的复制偏移量大于缓冲区中队首的复制偏移量,就会以部分重同步的方式来进行复制;如果从服务器的复制偏移量小于缓冲区中队首的复制偏移量,则会以完整重同步的方式来进行复制。 二,Redis哨兵模式(Sentinel) (一)哨兵模式 哨兵模式是Redis高可用性的解决方案:通过一个或多个sentinel组成sentinel系统,监控任意多个主服务器,以及这些主服务器下的所有从服务器,并在被监控的主服务器下线后,自动将主服务器下的一个子服务器升级为新的主服务器,然后由新的主服务器执行命令。 Sentinel系统主要有三个功能: 监控(Monitoring): Sentinel会以一定的频率检查主服务器和从服务器是否运作正常。 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。 (二)监控方式 每隔10s,每个哨兵节点会向主节点和从节点发送info命令获取最新的拓扑结构。 每隔2s,每个哨兵节点通过订阅连接向服务器频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个Sentinel节点也会订阅该频道,并接收到信息。注意:Sentinel在连接主服务器或从服务器时,会同时建立命令连接和订阅连接,但连接其他Sentinel节点时,只会创建命令连接;这是因为Sentinel通过服务器的订阅连接来发现新的Sentienel和更新其它Sentinel的状态,而对于已知的Sentinel,可以直接使用命令连接来通信。 每隔1s,每个哨兵节点会向主节点、从节点、其余哨兵节点发送一条ping命令做一次心跳检测(心跳检测机制),来确认这些节点当前是否可达。 (三)判断主服务器是否下线 判断主服务器是否下线有两步骤:主观下线和客观下线。 主观下线:单个Sentinel认为服务器已经下线。 客观下线:多个Sentinel认为服务器已经下线。 主观下线: 当一个实例(包括主从节点,哨兵节点)在一定时间对于Sentinel的回复为无效回复(除了+PONG,-LOADING,-MASTERDOWN这三种回复之外的所有回复,以及没有回复),则该Sentinel会把该实例标为主观下线。 注意:由于每个Sentinel设置的超时时间不同,所以可能有的Sentinel认为服务器已经主观下线,但另外一些Sentinel认为服务器没有主观下线,即还处于存活状态。 客观下线(只有主服务器才会进行客观下线判断): 当Sentinel将一个主服务器判断为主观下线后,会向其它同样监视着该主服务器的Sentinel进行询问,看他们是否认为主服务器已经进入了下线状态(可以是主观下线或者是客观下线)。当Sentinel收到一定数量的肯定回复后,会将该服务器标识为客观下线,并对主服务器执行故障转移操作。 注意:每个Sentinel设置的肯定回复数量不同,所以可能有的Sentinel认为服务器已经客观下线,而有的Sentinel认为服务器还没客观下线,甚至还有的Sentinel认为服务器还处于存活状态。但只要一个 Sentinel 认为某个主服务器进入了客观下线状态, 就会对失效的主服务器执行自动故障迁移操作。

C 判断字符串中是否包含某字符(串)

std::string a = "abjahjhaskajs_kajks"; std::string b = "_"; string::size_type idx; idx = a.find(b); if(idx == string::npos ) cout<<"字符串中存在字符"_" <<endl; else cout<<"不存在" <<endl; string a="abcdefghigklmn"; char *b="def"; char *c="123"; if(strstr(a.c_str(), b) == NULL)//在a中查找b,如果不存在, cout << "not found\n"; else//否则存在。 cout <<"found\n";

Pycharm+TensorFlow编写CNN实现Mnist手写数据集识别

# 导入本次需要的模块 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # 下载并加载数据 mnist = input_data.read_data_sets(r'F:\手写数字识别\新建文件夹\data', one_hot=True) def compute_accuracy(v_xs, v_ys): global prediction y_pre = sess.run(prediction, feed_dict={xs: v_xs, keep_prob: 1}) correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) result = sess.run(accuracy, feed_dict={xs: v_xs, ys: v_ys, keep_prob: 1}) return result # 定义Weight变量,输入shape,返回变量的参数。使用了tf.truncted_normal产生随机变量来进行初始化 def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) # 定义biase变量,输入shape,返回变量的一些参数。使用tf.constant常量函数来进行初始化 def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) # 定义卷积操作。tf.

OpenCV—Python 图像去模糊(维纳滤波,约束最小二乘方滤波)

文章目录 一、维纳滤波二、约束最小二乘方滤波三、psf2otf ,circShiftcircshift(psf,K)psf2otf() 一、维纳滤波 对于运动引起的图像模糊,最简单的方法是直接做逆滤波,但是逆滤波对加性噪声特别敏感,使得恢复的图像几乎不可用。最小均方差(维纳)滤波用来去除含有噪声的模糊图像,其目标是找到未污染图像的一个估计,使它们之间的均方差最小,可以去除噪声,同时清晰化模糊图像。 y ( t ) = h ( t ) ⨂ x ( t ) + n ( t ) y(t)=h(t)\bigotimes x(t)+n(t) y(t)=h(t)⨂x(t)+n(t) 其中: ⨂ \bigotimes ⨂ 是卷积符号 x ( t ) x(t) x(t) 是在时间 t t t 刻输入的信号(未知) h ( t ) h(t) h(t) 是一个线性时间不变系统的脉冲响应(已知) n ( t ) n(t) n(t) 是加性噪声,与 x ( t ) x(t) x(t)不相关(未知) y ( t ) y(t) y(t) 是我们观察到的信号 我们的目标是找出这样的卷积函数 g ( t ) g(t) g(t),这样我们可以如下得到估计的 x ( t ) x(t) x(t) x ^ ( t ) = g ( t ) ∗ y ( t ) \hat{x}(t)=g(t)∗y(t) x^(t)=g(t)∗y(t) 这里 x ^ ( t ) \hat{x}(t) x^(t)是 x ( t ) x(t) x(t)的最小均方差估计。

Linux中信号量介绍及使用

1.初始化条件变量pthread_cond_init #include <pthread.h> int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *cattr); 返回值:函数成功返回0;任何其他返回值都表示错误 初始化一个条件变量。当参数cattr为空指针时,函数创建的是一个缺省的条件变量。否则条件变量的属性将由cattr中的属性值来决定。调用 pthread_cond_init函数时,参数cattr为空指针等价于cattr中的属性为缺省属性,只是前者不需要cattr所占用的内存开销。这个函数返回时,条件变量被存放在参数cv指向的内存中。 可以用宏PTHREAD_COND_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_cond_init函数动态分配的效果是一样的。初始化时不进行错误检查。如: pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 不能由多个线程同时初始化一个条件变量。当需要重新初始化或释放一个条件变量时,应用程序必须保证这个条件变量未被使用。 2.阻塞在条件变量上pthread_cond_wait #include <pthread.h> int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex); 返回值:函数成功返回0;任何其他返回值都表示错误 函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。 被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。 pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。 pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回。 一般一个条件表达式都是在一个互斥锁的保护下被检查。当条件表达式未被满足时,线程将仍然阻塞在这个条件变量上。当另一个线程改变了条件的值并向条件变量发出信号时,等待在这个条件变量上的一个线程或所有线程被唤醒,接着都试图再次占有相应的互斥锁。 阻塞在条件变量上的线程被唤醒以后,直到pthread_cond_wait()函数返回之前条件的值都有可能发生变化。所以函数返回以后,在锁定相应的互斥锁之前,必须重新测试条件值。最好的测试方法是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件。如: pthread_mutex_lock(); while (condition_is_false) pthread_cond_wait(); pthread_mutex_unlock(); 阻塞在同一个条件变量上的不同线程被释放的次序是不一定的。 注意:pthread_cond_wait()函数是退出点,如果在调用这个函数时,已有一个挂起的退出请求,且线程允许退出,这个线程将被终止并开始执行善后处理函数,而这时和条件变量相关的互斥锁仍将处在锁定状态。 3.解除在条件变量上的阻塞pthread_cond_signal #include <pthread.h> int pthread_cond_signal(pthread_cond_t *cv); 返回值:函数成功返回0;任何其他返回值都表示错误 函数被用来释放被阻塞在指定条件变量上的一个线程。 必须在互斥锁的保护下使用相应的条件变量。否则对条件变量的解锁有可能发生在锁定条件变量之前,从而造成死锁。 唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。 如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用。 4.阻塞直到指定时间pthread_cond_timedwait #include <pthread.h> #include <time.h> int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mp, const structtimespec * abstime); 返回值:函数成功返回0;任何其他返回值都表示错误 函数到了一定的时间,即使条件未发生也会解除阻塞。这个时间由参数abstime指定。函数返回时,相应的互斥锁往往是锁定的,即使是函数出错返回。 注意:pthread_cond_timedwait函数也是退出点。 超时时间参数是指一天中的某个时刻。使用举例:

微信扫码登录前端要做的工作

<div id="div" style="z-index: 100;"> </div> mounted(){ var obj = new WxLogin({ id:"div", //div的id appid: "wx3b976ef54d06d852", scope: "snsapi_login", //我这里重点解释一下这个url啊,编码之前的地址也就是我把二维码放的地址是 https://shujiecaishui.com/finance_system/#/ redirect_uri:"https%3a%2f%2fshujiecaishui.com%2ffinance_system%2f%23%2f", //回调地址(网上搜urlencode编码,把上面的地址编码之后写在这) state: "", //参数,可带可不带(换绑的时候可以用到,假如1是确认身份,2是换给谁) style: "", //样式 提供"black"、"white"可选,默认为黑色文字描述 href: "" //自定义样式链接,第三方可根据实际需求覆盖默认样式。 }); }, watch:{ $route(to,from){ if (this.$route.query.code){ this._ajax('weChatWeb/getUserInfo', { code:this.$route.query.code }, msg => { //成功之后的操作 localStorage.setItem('user',JSON.stringify(msg)) localStorage.setItem('uuid',JSON.stringify(msg.uuid)) console.log(msg) if(msg.flag5 == 0){ this._message(1,'登录成功') this.$router.push("/entry_company_message") }else if(msg.flag5 == 1){ this._message(1,'登录成功') this.$router.push("/home") } }) } } },

ADRC学习笔记( 一)

近期正在自学自抗扰技术,不得不感叹韩老师乃神人也!话不多说先把这两天的学习成果与大家分享,后续在做详细的介绍。欢迎大家批评指正。 (1)过渡过程 输入一个阶跃信号,其跟踪效果如图所示,可以看到毫无超调。 (2)扩张状态观测器 (3)非线性组合 (4)整个控制器结构 最后附上学习的资料,需要的小伙伴自取,simulink仿真和matalb仿真代码也会陆续分享出来,敬请关注。 自抗扰学习资料 https://download.csdn.net/download/weixin_38291293/11368082

SQLServer大批量数据库迁移方案

在项目实施过程中,有时候会遇到大批量数据库(上百个)同时迁移的问题,如果采用常规的备份还原的方式会消耗非常多的时间,对业务会造成非常大的影响,生产环境下业务很难接受这种方式,所以我们采取镜像的方式来做迁移,即提前搭建镜像,在迁移的时候进行故障转移,然后断开镜像连接,由于数据库太多,我们会通过脚本进行批量操作。 首先需要建立所有数据库的源服务器到目标数据库的镜像关系,由于数据库太多,这里我们也使用脚本进行批量备份和还原: 1. 批量备份指定的数据库和日志(备份数据库时间较长,故备份日志): Declare @total int select @total=count(name) from sys.databases where name in ( 'DB1','DB2','DB3','DB4','DB100' ) while @total<>0 begin Declare @DBname varchar(1000) Declare @sql01 varchar(2000) Declare @sql02 varchar(2000) Declare @path varchar(1000) Declare @date varchar(50) select @DBname=a.name from (select name,row_number()over(order by name) sequence from sys.databases where name in ( 'DB1','DB2','DB3','DB4','DB100' ))a where @total=a.sequence; set @date=replace(convert(varchar,getdate(),23),'-','') set @path='Y:\Backup\'+@DBname+'_'+@date+'.bak'; print @path set @sql01='BACKUP DATABASE '+quotename(@DBname,'[]')+' TO DISK='+quotename(@path,'''')+' WITH NOFORMAT,NOINIT,NAME='+quotename('Full Database Backup','''')+',SKIP,NOREWIND,NOUNLOAD,STATS = 10'

Pycharm+tensorflow+Softmax实现Mnist手写体数字识别

Pycharm+Tensorflow的配置和安装在前面的博客已经介绍过。 Minst数据集可以再这里下载:Minst数据集下载 MNIST数据集包含了6w张图片作为训练数据,1w图片作为测试数据。在MNIST数据集中,每一张图片都代表了0~9中的一个数字,图片的大小都是28×28,且数字都会出现在图片的正中间。数据集包含了四个文件: t10k-images-idx3-ubyte.gz 测试数据图片 t10k-labels-idx1-ubyte.gz 测试数据答案 train-images-idx3-ubyte.gz 训练数据图片 train-labels-idx1-ubyte.gz 训练数答案 每张图片的784个像素点组成长度为784的一维数组,作为输入特征 [0. 0. 0. ...... 0.38 0.45 0.64 .... 0. 0. 0. ] 图片的标签以一个一维数组的形式给出,每个元素表示对应分类出现的概率,比如数字“6”的标签 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. ] from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets(r'F:\手写数字识别\新建文件夹\data', one_hot=True) print("train data size:", mnist.train.num_examples) print("validation data size:", mnist.validation.num_examples) print("test data size:", mnist.test.num_examples) t_lables0 = mnist.train.labels[1] # 返回标签 t_iamge0 = mnist.train.images[1] # 返回数据 t_iamge0.resize(28, 28) # 将784个元素的标签转换为28*28 print('t_lables0: ', t_lables0) # 查看标签 结果:

ANSYS SIWave SI仿真

ANSYS SIwave: Electrothermal Analyses of a PCB - Part I https://www.youtube.com/watch?v=0-7uRuuFwiY&list=PL0lZXwHtV6OlX1zLLASBnXoW6TZ8W5FrJ ANSYS How To Videos 7,710次观看 次观看 2016年5月9日发布 Description For more info: http://bit.ly/2erY0iM This ANSYS video demonstrates how to import the board from ODB++ format into SIwave, and review the layout and schematic of the PCB. ANSYS SIwave is a specialized design platform for power integrity, signal integrity and EMI analysis of electronic packages and PCBs. /*******************************************************************************************************/ /*******************************************************************************************************/ [视频]Mentor公司极好的仿真介绍教程视频 | 吴川斌的博客

使用Spring REST Docs 编写接口文档

目录 Spring REST Docs 概述Spring REST Docs 与 Swagger 的区别框架搭建修改pom.xml编写测试代码编写Controller代码使用MockMvc编写测试代码编写index.adoc 代码片段 昨晚边试错边学习硬是搞到凌晨3点多.......生成的代码片段存放的目录target目录的结构index.html存放目录index.html接口页面展示引用曹雪芹的诗一首满纸荒唐言一把辛酸泪都言作者痴谁解其中味 持续努力中.......努力coding......... Spring REST Docs 概述 Spring REST Docs 是基于 jdk1.8 和SpringFramework 5.0.2及以上版本的RESTful 服务文档,Spring REST Docs是通过将手写xxx.adoc文档与使用spring-mvc-test-framework测试框架编写的测试代码片段相结合的方式,来最终生成HTML接口文档,记录RESTful服务接口文档,是半自动的。 Spring REST Docs 与 Swagger 的区别 1.swagger是在线文档(传说也可以生成离线的),Spring REST Docs是离线文档 2.swagger是自动生成的,不可修改文档格式样式,Spring REST Docs 是半自动的,生成 的HTML文档样式不满意可以自定义 3.最主要的区别:swagger是对业务代码中有入侵性的,Spring REST docs是不需要修改业务代码的,没有入侵性 框架搭建 基于Spring boot ,去htttps://start.spring.io,搜索并添加Spring REST Docs 依赖 修改pom.xml 当你添加好maven 依赖后,会有 <build> <!--当项目没有规定目标时的默认值,项目没有报错,不用加 --> <defaultGoal>compile</defaultGoal> <plugins> <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.3</version> <executions> <execution> <id>generate-docs</id> <phase>prepare-package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> <!

Python 中的Sympy详细介绍

Python 中的Sympy详细使用 遇到复杂计算找python绝对不让你失望,sympy是一个Python的科学计算库,用一套强大的符号计算体系完成诸如多项式求值、求极限、解方程、求积分、微分方程、级数展开、矩阵运算等等计算问题。虽然Matlab的类似科学计算能力也很强大,但是Python以其语法简单、易上手、异常丰富的三方库生态,个人认为可以更优雅地解决日常遇到的各种计算问题。安装在本博客就不细讲了! 1、表达式与表达式求值: #--------多项式求解-------- #定义变量 x=sympy.Symbol('x') fx=5*x+4 #使用evalf函数传值 y1=fx.evalf(subs={x:6}) print(y1) #多元表达式 x=sympy.Symbol('x') y=sympy.Symbol('y') fx=x*x+y*y result=fx.evalf(subs={x:3,y:4}) print(result) 2、函数方程求解: #解方程 有限解 #定义变量 x=sympy.Symbol('x') y=sympy.Symbol('y') fx=x*3+9 #可求解直接给出解向量 print(sympy.solve(fx,x)) #解方程无穷多解 #定义变量 x=sympy.Symbol('x') y=sympy.Symbol('y') fx=x*3+y**2 #得到是x与y的关系式, print(sympy.solve(fx,x,y)) #解方程组 #定义变量 x=sympy.Symbol('x') y=sympy.Symbol('y') f1=x+y-3 f2=x-y+5 sympy.solve([f1,f2],[x,y]) 3、求和 import sympy #定义变量 n=sympy.Symbol('n') f=2*n #前面参数放函数,后面放变量的变化范围 s=sympy.summation(f,(n,1,100)) print(s) 解带有求和式的方程 : #解释一下,i可以看做是循环变量,就是x自己加五次 #先定义变量,再写出方程 x=sympy.Symbol('x') i=sympy.Symbol('i') f=sympy.summation(x,(i,1,5))+10*x-15 result=sympy.solve(f,x) print(result) 4、求极限(注意,math包中sin和很多数学函数会报错,要用sympy中的,无穷大用 sympy.oo 表示) #求极限使用limit方法 #定义变量与函数 x=sympy.Symbol('x') f1=sympy.sin(x)/x f2=(1+x)**(1/x) f3=(1+1/x)**x #三个参数是 函数,变量,趋向值 lim1=sympy.

linux -ssh无法连接ubuntu的解决办法

1、安装ssh服务 sudo apt-get install openssh-server 2、修改配置文件 sudo vi /etc/ssh/sshd_config 打开后 #PermitRootLogin without-password #注释掉这行 PermitRootLogin yes #增加这行 #重启服务 /etc/init.d/ssh restart 3、远程连接报下面错误,解决方法同上 [root@salt ~]# ssh root@192.168.141.77 Permission denied (publickey,password).

ubuntu 编译zlib quazip静态库和动态库

一 环境 ubuntu14.04qt-embed4.7.0qt-x864.8.5gcc4.8.4arm-linux-gcc 4.5.1 二 编译zlib 2.1)源码下载 http://www.zlib.net/ 点击此处下载,本次下载版本为1.2.11 2.2)解压 tar -xvf zlib-1.2.11.tar.gz 2.3)配置安装目录和编译工具链 (生成arm版本) export CC=arm-linux-gcc(交叉编译工具链) ./configure --prefix=../arm (生成库和头文件安装目录) make make install 2.4)生成x86版本 export CC=gcc ./configure --prefix=../x86 make make install 三 编译quazip 3.1)源码下载 https://sourceforge.net/projects/quazip/ 版本:quazip-0.7.3.tar.gz 3.2)解压 tar -xvf quazip-0.7.3.tar.gz 3.3)配置quazip项目,源码包含pro,使用qtCreator打开 3.4)构建不依赖上面构建的zlib库的x86版本或Arm libquazip.so版本 qmake;构建,即可生成对应的库文件 如果报错缺少zlib.h 直接 sudo apt-get install zlib1g-dev libssl-dev 3.5)构建libquazip.a版本就需要依赖上面编译的zlib库了,目的编译成静态库 LIBS += -L/home/yangtq/temp/arm/lib" -lz (上面编译的lzlib库路径) INCLUDEPATH += /home/yangtq/temp/arm/include (上面编译zlib安装的include路径) CONFIG += staticlib (编译静态库) 3.6)相应目录将生成相应文件 3.7)x86版本类似只需更改上面zlib相应版本的路径即可 3.8)安装quazip生成的库文件和头文件到指定目录 修改quazip的Makefile 加上 INSTALL_ROOT = .

我的暑假翻车史一:如何生成年NDVI平均值

我用的是MOD13Q1的16d数据,数据下载参考https://blog.csdn.net/weixin_43519457/article/details/95483905,最后生成的是5.1-9.30的NDVI平均值,年平均值大同小异。 (1)MRT融合 (2)每幅影像都有一个-3000,是无效值,那这个-3000应该如何处理,我做了两种,一种是保留这个-3000,另一种是将其设为无效值并且在计算时忽略无效值,发现最后结果是有差别的。查阅资料过后我认为应该采用第二种的方法,工具用的是spatial analyst工具——局部分析——像元统计——mean,无效值用的是栅格计算器中的setnull。 明显这一切都是在ARCGIS里做的,最后导致了我的翻车,还翻了一上午。 翻车史:envi里利用band math将band<0.1和band>0.9的附成了0,但背景值也变成了0,而且死活去不掉,出现这种问题我认为是ENVI里并没有nodata,只有NAN和INF,所以我又把它弄回到arcgis重新剔除了不需要的区域 公式con(raster,0,raster,“value<0.1|value>0.9”) (3)解决方案二:首先将-3000设为NAN,计算时忽略无效值,再在ENVI波段计算应该不会出现背景值参与计算的问题,我试了再补充。

BigDecimal四舍五入后保留两位小数

测试代码如下: double dayDiff2 = 5.06135778; BigDecimal usedM = new BigDecimal(dayDiff2); //保留两位小数且四舍五入 usedM = usedM.setScale(2, BigDecimal.ROUND_HALF_UP); 测试结果如图:

Webservice wsdl 里显示的数据不全。

用cxf 开发一个webservice接口,去访问WSDL的时候会发现 总感觉里面的信息不全。试了各种方案,最后才发现 ,webservice接口和实现类要放在同一层级的包下,才能显示全的信息。 但是呢 我这边的包结构,又不允许我这么放,那就在@WebService注解里加上一个targetNamespace。(注意,接口和实现类都需要加,而且需要加一样的) 加完后再去执行。OK 没问题