K8S搭建三主三从高可用redis集群 一、创建存储卷二、创建PV三、创建configmap四、创建headless service五、创建redis集群节点六、初始化redis集群七、创建用于访问的service八、redis主从切换测试 本方案采用StatefulSet进行redis的部署。它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序。在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
在k8s上搭建Redis sentinel完全没有意义,经过测试,当master节点宕机后,sentinel选择新的节点当主节点,当原master恢复后,此时无法再次成为集群节点。因为在物理机上部署时,sentinel探测以及更改配置文件都是以IP的形式,集群复制也是以IP的形式,但是在容器中,虽然采用的StatefulSet的Headless Service来建立的主从,但是主从建立后,master、slave、sentinel记录还是解析后的IP,但是pod的IP每次重启都会改变,所有sentinel无法识别宕机后又重新启动的master节点,所以一直无法加入集群,虽然可以通过固定pod IP或者使用NodePort的方式来固定,或者通过sentinel获取当前master的IP来修改配置文件。 sentinel实现的是高可用Redis主从,检测Redis Master的状态,进行主从切换等操作,但是在k8s中,无论是dc或者ss,都会保证pod以期望的值进行运行,再加上k8s自带的活性检测,当端口不可用或者服务不可用时会自动重启pod或者pod的中的服务,所以当在k8s中建立了Redis主从同步后,相当于已经成为了高可用状态,并且sentinel进行主从切换的时间不一定有k8s重建pod的时间快,所以个人认为在k8s上搭建sentinel没有意义。
K8s集群环境
节点IPmaster192.168.1.100node1192.168.1.101node2192.168.1.102node3192.168.1.103 下载镜像经导入集群中redis:3.2.8、inem0o/redis-trib:latest
docker pull redis:3.2.8; docker save –o redis.tar redis:3.2.8
docker pull inem0o/redis-trib:latest; docker save –o redis-trib.tar inem0o/redis-trib:latest
redis.tar上传至k8s的各个node节点;redis-trib.tar上传至k8s的master节点
一、创建存储卷 创建NFS存储主要是为了给Redis提供稳定的后端存储,当Redis的Pod重启或迁移后,依然能获得原先的数据。
安装nfs软件包 ]# yum –y install nfs-utils rpcbind #任选一台节点(这里选k8s-master) 创建共享存储 ]# mkdir -p /usr/local/k8s/redis/pv{1..6} #创建共享目录 ]# cat /etc/exports #配置共享路径 /redis/pv1 192.168.1.0/24(rw,sync,no_root_squash) /redis/pv2 192.168.1.0/24(rw,sync,no_root_squash) /redis/pv3 192.168.1.0/24(rw,sync,no_root_squash) /redis/pv4 192.168.1.0/24(rw,sync,no_root_squash) /redis/pv5 192.168.1.0/24(rw,sync,no_root_squash) /redis/pv6 192.168.1.0/24(rw,sync,no_root_squash) ]# systemctl restart rpcbind;systemctl restart nfs;systemctl enable nfs 查看共享路径 exportfs –v #k8s-master上操作 其他节点安装nfs客户端,并验证 ]# yum -y install nfs-utils ]# showmount -e 192.
ENVI5.5之前的ENVI版本是不能直接打开哨兵数据的,但是办法总比困难多,看了一些文章记录的方法,自己试了一下,是可以成功使用5.5之前的版本处理哨兵数据的。本篇详细记录一下如何用ENVI5.3处理哨兵数据,怕时间长了我给忘了orz
参考:http://blog.sciencenet.cn/blog-3367669-1084798.html 步骤1:用ENVI打开下载好的数据文件里的.jp2文件,路径为 文件夹\GRANULE\...\IMG_DATA,注意:不能用classic版本打开!要不然会出问题 步骤2:将.jp2文件另存为ENVI Standard 格式,注意:不要直接存为tiff格式,一定先存为Envi Standard格式,要不然会丢失坐标信息 全部转为ENVI Standard后是这个亚子(一般用到2,3,4,8波段)
步骤3:将转换后的ENVI Standard数据波段合成 假彩色显示一下是这样的
步骤4:将上一步Layer Stacking后的文件另存为TIFF格式输出 然后就大功告成!
Flask-Babel是flask支持i18n 和 l10n的一个扩展库,基于babel, pytz和speaklater。我用的是python3
安装 pip install Flask-Babel 在flask中应用举例 from flask import Flask from flask_babel import Babel app = Flask(__name__) app.config.from_pyfile('config.py') babel = Babel(app) 配置项 配置项说明BABEL_DEFAULT_LOCALE默认语言,不设置取enBABEL_DEFAULT_TIMEZONE默认时区,不设置取utcBABEL_TRANSLATION_DIRECTORIEStranslations的目录,如果不设置,则取app的根目录 使用如下,在app的配置文件config.py中设置,translations的生成后面说
BABEL_DEFAULT_LOCALE='en' BABEL_DEFAULT_TIMEZONE='UTC' BABEL_TRANSLATION_DIRECTORIES='**/translations' LANGUAGE = ['zh', 'en', 'fr'] 通过请求中切换语言 通过select返回的语言类型来更改服务返回的数据,文档中说用refresh清缓存,这个没有用到,但是可以实现切换,这个以后再研究,文档原文如下:
If any of these methods return None the extension will automatically fall back to
what’s in the config. Furthermore for efficiency that function is called only once
and the return value then cached.
由于一个c++项目需要make的命令需要另外下载安装MinGW。
MinGW,是Minimalist GNUfor Windows的缩写。它是一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合。
MinGW安装教程如下:
1、进入MinGW的官网:mingw.org 然后在左侧侧边栏可以找到下载链接
点击ALL time 目录下的download/installer
2(在这里推荐下载到c盘根目录(没什么原因,就是尽量和该指导文档一致,在后面设置环境变量的时候也会比较方便) 3、添加 C:\MinGW\bin 到系统环境变量PATH 添加过程:
windows在这里输入你要搜索的内容,输入:环境变量
然后进入环境变量
在下面系统变量中寻找到PATH这一栏,双击path
然后出现这个编辑环境变量,在这里点击新建,粘贴:C:\MinGW\bin
然后全部点确定就添加成功!
4、打开MinGW Installation Manager【MinGW】
该部分安装请参考MinGW安装中文
下图:安装完毕,在安装管理器的主页面中,复选框会填充成绿色,我们以后可以根据它来管理 MinGW 的组件。
5、先添加后添加环境变量无差 6、打开C:\MinGW\bin文件夹,找到mingw32-make.exe MinGW 只提供了名字为 mingw32-make.exe 的执行文件,该文件和 make.exe 功能一样,为了make执行时能找到该文件,复制mingw32-make.exe一份,并将复制文件命名为make.exe
7、查看make是否安装成功: 在cmd中输入 make -v,看到以下内容表示安装成功: GNU Make 3.82.90 Built for i686-pc-mingw32 Copyright © 1988-2012
Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or
later http://gnu.org/licenses/gpl.html This is free software: you
are free to change and redistribute it.
文章目录 一、基本语法二、希腊字母三、字母的特殊修饰符号四、比较运算与集合运算五、数学分析常见符号六、矩阵七、补充 一、基本语法 公式用$...$或$$...$$$定义。区别是,前者显示为行内公式,后者显示为行间公式。
例如,输入:
我是多项式函数$f(x)=a_0+a_1x+\cdots+a_nx^n \quad n \geq 0$ 我是多项式函数$$g(x)=b_0+b_1x+\cdots+b_nx^n \quad n \geq 0$$ 显示:
我是多项式函数 f ( x ) = a 0 + a 1 x + ⋯ + a n x n n ≥ 0 f(x)=a_0+a_1x+\cdots+a_nx^n \quad n \geq 0 f(x)=a0+a1x+⋯+anxnn≥0
我是多项式函数 g ( x ) = b 0 + b 1 x + ⋯ + b n x n n ≥ 0 g(x)=b_0+b_1x+\cdots+b_nx^n \quad n \geq 0 g(x)=b0+b1x+⋯+bnxnn≥0
最近看悟空问答,很多人提问:“互联网行业女孩子做什么比较好?”,我也回复了很多这样的问题。今天发个微头条针对这个问题做一个总结。
先来讲讲互联网大概有哪些岗位,一般互联网企业都有以下岗位:1)产品岗 2)研发岗 3)UED 4)软件测试 5)运营岗 6)数据分析岗 7)系统运维岗 8)销售岗。
现在来讲讲女孩子在互联网行业从事什么岗位比较好。这个要根据自身的专业、工作经验、兴趣爱好和未来的职业规划综合去考虑。建议可以去看下招聘网站的这些岗位的招聘要求。我认为女孩子比较合适从事的岗位按我的理解分为几个层次,从上到下依次表示哪些是女孩子选择比较多的岗位。
第一、运营岗、软件测试、销售岗
互联网运营岗位是做用户运营、活动运营、平台运营等,软件测试是保证软件的正确性、完整性、安全性和质量。这两个岗位目前互联网企业女孩子相对来说比较多。另外有销售岗的互联网企业女性销售也是蛮多的。
第二、UED、产品岗
User Experience Design(用户体验设计),简称UED。产品岗就是负责产品需求设计。这两个如果是有相关专业背景的女孩子从事会比较好。UED一般大学学习用户交互设计、视觉设计等设计类的专业的去学这个会比较好。产品岗一般是要求逻辑思维好,有相关的产品设计经验。
第二、研发岗、数据分析岗、系统运维岗
一般这三个岗位男的比较多,都是要求有严谨的逻辑思考能力,要有一定的编程能力。
综上所述,就是女孩子互联网做销售、运营、测试的会比较多。但是这个也不是绝对的,还是要根据自己的实际情况去选择。
复制下面代码到一个txt文本中,将后缀名修改为bat,双击运行即可。操作方式如下图
这里我去掉了新建两个字。
注意编码问题,如果打开是乱码,请先查看cmd的属性,然后将文本保存为对应字符编码。
复制下方代码:
@echo off set /p str1= 请输入要替换的文件(文件夹)名字符串(可替换空格): set /p str2= 请输入替换后的文件(文件夹)名字符串(去除则直接回车): echo. echo 正在修改文件(夹)名中,请稍候…… for /f "delims=" %%a in ('dir /s /b ^|sort /+65535') do ( if "%%~nxa" neq "%~nx0" ( set "file=%%a" set "name=%%~na" set "extension=%%~xa" call set "name=%%name:%str1%=%str2%%%" setlocal enabledelayedexpansion ren "!file!" "!name!!extension!" 2>nul endlocal ) ) exit
文章目录 1、引言2、docker环境安装3、建立测试项目4、编写Dockerfile5、编译Dockerfile6、查看镜像7、运行容器8、测试访问9、总结 1、引言 前段时间将netcore项目部署在了centos 7的docker下,通过部署的过程中本篇文章用于介绍下在部署的时候相关步骤。
2、docker环境安装 我测试时使用的docker desktop,毕竟开发时基本上都是在windows下进行开发的,本地发布镜像运行程序开发完后可以发布到自己的私有镜像仓库中去(笔者使用的阿里的免费私有仓库)。
关于docker desktop有两种容器(linux container、windows container),运行时依赖Hyper-V虚拟机,安装的时候会自动开启Hyper-v。
运行后可以看到下图标记的虚拟机。
3、建立测试项目 直接使用dotnet-cli脚手架进行创建web项目,关于dotnet-cli的知识参见官网。
dotnet new web -n myapp 安装完成
切换到myapp目录
cd myapp 4、编写Dockerfile 创建Dockerfile文件
touch Dockerfile 注意:Dockerfile文件是没有后缀的
# 1. 设置基础镜像 FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build # 2. 指定(编译和发布)工作目录 WORKDIR /app # 3. 将.csporj文件复制到工作目录“/app”下,然后执行“dotnet restore”还原nuget包 COPY *.csproj ./ RUN dotnet restore # 4. 将所有文件辅助到工作目录"/app"下,然后使用"dotnet publish"命令发布到“/app/out”目录下 COPY . ./ RUN dotnet publish -c Release -o out # 5. 编译生成Docker镜像 # 5.
点击关注上方“五分钟学算法”,
设为“置顶或星标”,第一时间送达干货。
转自面向大象编程
本期例题:LeetCode 198. House Robber 打家劫舍(Easy)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例:
输入: [1,2,3,1] 输出: 4 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。 House Robber 问题(翻译为小偷问题、打家劫舍问题)是一道非常经典的动态规划入门题目。如果你对于动态规划还不是很了解,或者没怎么做过动态规划的题目的话,那么我非常建议你用 House Robber 这道题来入门。
动态规划可能被很多同学奉为最难理解的算法题类型,因为它的解法很灵活,变化很多。但是动态规划又经常作为笔试题的压轴,我们不得不去面对它。
其实,动态规划是一类很讲究「触类旁通」的题型。很多动态规划的解法需要你做过某一类型的例题,再做类似的题目的时候就可以想起来相应的思路。如果你做过不少题目,但是拿到新的题目依然没有思路,那说明你做过的题目不够典型。例如很多文章拿来大讲特讲的换硬币问题,实际上并不太适合用来理解动态规划的思想。
这正是我写《LeetCode 例题精讲》系列文章的意义所在。我的文章会做到两点,一是选择最有代表性的例题,二是通过该例题讲解一类问题的解题套路。从本期开始的几篇文章我会讲解动态规划问题,每篇文章用一道最典型的例题,带你学习动态规划的解题套路。
本文选择一个非常典型的动态规划问题:打家劫舍,在一步步求解这道题的过程中,讲解动态规划题目的四个基本步骤。
动态规划的解题四步骤 动态规划的的四个解题步骤是:
定义子问题
写出子问题的递推关系
确定 DP 数组的计算顺序
空间优化(可选)
不瞒你说,在 LeetCode 上我做过的几十道动态规划题目,都是用这个解题四步骤做出来的。这个解题步骤适用于任何一道动态规划题目,能够让你很快理清解题的各个要点。
步骤一:定义子问题 稍微接触过一点动态规划的朋友都知道动态规划有一个「子问题」的定义。什么是子问题?子问题是和原问题相似,但规模较小的问题。例如这道打家劫舍问题,原问题是「从全部房子中能偷到的最大金额」,将问题的规模缩小,子问题就是「从 个房子中能偷到的最大金额」,用 表示。
打家劫舍问题的子问题定义 可以看到,子问题是参数化的,我们定义的子问题中有参数 。假设一共有 个房子的话,就一共有 个子问题。动态规划实际上就是通过求这一堆子问题的解,来求出原问题的解。这要求子问题需要具备两个性质:
原问题要能由子问题表示。例如这道题中, 时实际上就是原问题。否则,解了半天子问题还是解不出原问题,那子问题岂不是白解了。
dedecms访问PC自动跳转响应手机模板,代码如下:
首页:
var mobileAgent = new Array("iphone", "ipod", "ipad", "android", "mobile", "blackberry", "webos", "incognito", "webmate", "bada", "nokia", "lg", "ucweb", "skyfire"); var browser = navigator.userAgent.toLowerCase(); var isMobile = false; for (var i = 0; i < mobileAgent.length; i++) { if (browser.indexOf(mobileAgent[i]) != -1) { isMobile = true; location.href = 'http://m.xxx.com'; break; } } -列表-
var mobileAgent = new Array("iphone", "ipod", "ipad", "android", "mobile", "blackberry", "webos", "incognito", "webmate", "bada", "nokia", "lg", "
第一次安装时运行,产生错误,主要是include头文件找不到,检查了下环境变量设置,必须设置在系统环境变量设置下,用户环境变量设置不行。
网络上和指导文档里会有环境变量设置的区别,这里贴出我的设置:
vs2005的路径要是真实的安装路劲作为环境变量。不然进不了C++编译器。vs2005安装好后必须启动下,看看能不能正常工作。最好按照环境变量设置的路径安装。
安装opnet的路径中不能有空格存在特别是系统自动放到Program Files中,这里有空格,是最大的坑。最好放到C:/OPNET下,这样路径简单不出错。
首次安装opnet运行错误,卸载后,记得要删除用户名下的op_文件,再重新安装一遍,再打开就能正常打开了。否则会自动进入上一次安装的路径,会找不到路径。
安装vim-plug 前提是已经安装好VIM,我本地安装的是VIM8.2,缺省安装路径
首先把插件克隆到本地(可以使用任何自己熟悉的工具):
我用的是git bash CLI
git clone https://github.com/junegunn/vim-plug.git
cd vim-plug/
cp plug.vim ~/vimfiles/autoload/plug.vim
这样就安装好vim-plug了。注意这个只对当前用户生效。如果要对所有用户生效,可以把plug.vim安装到vim安装目录下的autoload。
配置 启动GVIM,点菜单Edit=>startup Settings:
添加以下几行
call plug#begin( '~\vimfiles\plugin')
Plug 'junegunn/vim-easy-align'
call plug#end()
说明:call plug#begin( '~\vimfiles\plugin')指定VIM插件的安装目录。这时安装到当前用户的配置目录下,如果对全局用户可见,可以安装到VIM的安装目录下的plugin子目录下
Plug 'junegunn/vim-easy-align'是指定的一个插件示例,根据自己的需要调整需要维护管理的插件
call plug#end()就是结束声明了
别忘记存盘退出
维护 现在,在VIM命令窗口中,可以执行:PlugXXX命令维护了。
比如:
:PlugInstall
: PlugStatus
MOOC地址 http://163.lu/UAHy02
学习资料 源代码是课程提供的,加上自己的理解,写了很详细的注释小白也能看懂。还加一份DIY智慧小屋的操作手册。链接:https://pan.baidu.com/s/17_y_IPK5kff0O-nnm8tVrg
提取码:35b9
准备阶段 因为我是寒假疫情期间开始看的这个课,刚开始根据操作手册里购买清单里买了一些东西,不过大部分商品都过期了。下面是我买的一些模块。
1.控制器
ardunio驱动模块,和ardunio模块,如果自己有ardunio不需要自己买,买个驱动板就行,这个驱动板集成了EMW3080的WiFi模块,这个模块我接触的不多
复制这行话₴ny2Q1mIESB0₴转移至宝或點击链街https://m.tb.cn/h.ViSBr0K?sm=deb272 至瀏lan嘂…【智慧小屋驱动接口板,兼容arduino板接口,带WIFI模块EMW3080】
这家店现在提供定制了 ,不过不建议买全套,自己动手才有乐趣。
2.传感器
覆置这行话₤ntfD1mICCBw₤转移至氵匋宝或点几链街https://m.tb.cn/h.VRZ8rIp?sm=9674cc 至瀏…覽…噐【Risym 模块 MQ-9一氧化碳 可燃气体传感器检测报警模块】
这家店铺差不服都有。不过二氧化碳传感器不建议购买,太贵。如果有资金可以买来玩玩,不加这个模块相应的代码需要改一下
3.执行器
付致这行话₤EXfF1mIBjSV₤转移至宝或點击鏈→接https://m.tb.cn/h.VRPynOS?sm=008a03 至瀏lan嘂…【4CM导冷风扇 4010冷端散冷风扇 冷端蒸发风扇 12V微风量 40*10mm】
还有其他的空调,我都没买资金不到位 哈哈哈
平台搭建阶段 因为疫情期间发货比较慢,所以在这期间把mooc里的视频看了一遍,跟着老师把环境搭建好,跟着老师来没有什么困难。具体细节那个手册很详细,老师讲的也挺好。
收获 作为物联网工程的专业,看了这个慕课也是很有收获的。从感知层,网络层,应用层三个方面比较好的了解物联网的大体框架,对以后的学习也有帮助。物联网大致可以分成:感知层、网络层、平台层和应用层。了解了MQTT服务器,阿里云平台,Ardunio的使用,WIFI AT指令的使用,各种传感器的使用。
在课程中,我们可以直观看到物联网的分层架构,其与智慧小屋里相对应的关系为:感知层 主要是负责通过传感器设备来识别和收集信息,我们搭建了通过 Arduino 采集不同接口形式的传感数据的实例来学习感知层的构架; 网络层负责安全的把这些信息进行传输,我们通过 AT 指令,将数据通过 WiFi 传送到物联网平台端。平。台层负责数据的鉴权、接入和转发,我们以阿里云为例,讲述了接入云平台需要的鉴权方式、MQTT 协议。应用层负责结合具体的应用需求,利用 IoT Studio 这些先进的可视化工具,将建立服务编排,详细讲解了如何创立直观的 Web 显示、调度和控制的面,并可方便地编制出手机控制的 APP,通过实例让大家能快速掌握构架物联网系统原型的能力,并在云平台上对数据进行计算、 处理、挖掘,来实现智能化的物联网应用。
期间也遇到一些问题比如传感器初始化失败因为自己买的传感器参数与代码上的不一样,上云失败是因为标识符和代码中的标识符不一样等等,不过自己还是一点点解决了。
成果 自己没有定制亚克力板借用手册上的图片演示一下整屋的效果
WWW类废除,Unity提供了更加快捷的获取方式 ,如下:
IEnumerator GetAssetBundle()
{
//AB包路径
string bundlePath = Application.streamingAssetsPath + “/textrue”;
//直接使用UnityWebRequestAssetBundle获得AB包
UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(bundlePath);
yield return www.SendWebRequest();
//最终获得
AssetBundle ab= DownloadHandlerAssetBundle.GetContent(www);
}
eg:2
/// /// 加载非预制对象
/// /// 本地路径or远端地址
/// 操作且显示的对象
/// 欲加载资源的名称
/// IEnumerator LoadNoObjAsset(string URL,GameObject showObj,string assetName)
{
//直接使用UnityWebRequestAssetBundle获得AB包
using (UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(URL))
{
yield return www.SendWebRequest();
//最终获得
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
if (ab != null)
{
showObj.GetComponent().material.mainTexture = ab.LoadAsset(assetName);
//卸载资源(只卸载AB包本身)
ab.Unload(false);
}
javaWeb项目怎么在pom.xml文件里面引入本地jar包 日常开发中,有些jar包通过maven是找不到的,但是项目又需要这些包。如果是只在本地运行的话,那可以直接把jar包下载下来,然后build一下就可以了。但如果是联合开发,项目是多人共同开发的,你本地的jar包不提交或者没配置好的话,别人下载你的代码就会报错!而且发布到生产环境的时候也会运行报错的。所以,你不仅需要提交你本地的jar包,而且还要在配置文件里面配置对应的jar包地址。
比如我要用到下面的这些包:
首先,可以把这些包在项目下建一个文件夹,放在里面提交!我这里是放在web-info下面。
然后就是在pom.xml文件里面进行配置,比如我要引用smack-core-4.1.4.jar这个包,只需要在pom.xml里面加入下面的代码就行了,其它包也是一样的引入方式。之后编译一下项目就可以了。
<dependency> <groupId>smack</groupId> <artifactId>core</artifactId> <version>4.1.4</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/smack-core-4.1.4.jar</systemPath> </dependency>
成果演示 利用按钮操作可以直接控制nodemcu的led灯的亮灭,实时显示室内的温度和压强
硬件组成 一块Nodemcu开发板,bmp280模块
注意:在连接电路的时候要注意,SCL连接GPIO5,SDA连接GPIO4,利用IIC通信将数据传送给nodemcu开发板。
代码组成 这里只列举部分代码,这些代码也是借鉴别人自己改的,完整代码在
链接:https://pan.baidu.com/s/1oqfne9qCfDU2OLHqCQaZ_g
提取码:th87
#include <ESP8266WiFi.h> //安装esp8266arduino开发环境 #include <PubSubClient.h> //安装PubSubClient库 #include <ArduinoJson.h> //json V5版本 #include "aliyun_mqtt.h" //需要安装crypto库 #include <Wire.h> //#include <SPI.h> #include <Adafruit_Sensor.h> #include <Adafruit_BMP280.h> #define LED D4 #define WIFI_SSID "TP-LINK_6B29"//替换自己的WIFI #define WIFI_PASSWD "wg15385778716.."//替换自己的WIFI #define PRODUCT_KEY "a1QA2EMzdKC" //替换自己的PRODUCT_KEY #define DEVICE_NAME "esp8266" //替换自己的DEVICE_NAME #define DEVICE_SECRET "12Q7UFMSloyEPxtJoKxewT2li7UMXw4i"//替换自己的DEVICE_SECRET #define DEV_VERSION "S-TH-WIFI-v1.0-20190220" //固件版本信息 #define ALINK_BODY_FORMAT "{\"id\":\"123\",\"version\":\"1.0\",\"method\":\"%s\",\"params\":%s}" #define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post" #define ALINK_TOPIC_PROP_POSTRSP "/sys/" PRODUCT_KEY "/" DEVICE_NAME "
字节流与字符流的区别: 上图解析:
字节输出流是直接把字节数据写出到磁盘或其他设备上,而字符输出流会先把字符输出流按一定的编码规则进行编码成二进制的字节输出流再写出到磁盘或其他设备上。字节输入流每次读取一个字节数据到内存(也可以使用缓冲区一次读取多个),而字符输入流是每次读取多个字节的数据到内存中,然后对读取的字节数据按照一定的编码规则进行解码,然后形成文本数据。总的来说,系统到磁盘或者其他设备上的数据,从其他数据读取到内存中时,都是字节数据,只是java封装了一层字节到字符或者字符到字节的工序。字节流适用于任何形式的数据;而字符流只适用于文本数据,不能用字符流来传输诸如图片、语言、视频等二进制数据。但是处理文本数据字符流要比字节流方便许多。使用字符输出流时一定要记得使用flush方法或者close方法把数据强制刷出到磁盘或其他设备,因为字符流会存在缓冲区,除非缓冲区满了才会自动刷出到磁盘或者其他设备中。使用字节流的话如果带有或者使用缓冲区的话,就也要强制刷出到磁盘或者其他设备中。 总结区别: 比较项字节流字符流基类OutputStream/InputStreamWriter/Reader操作的数据单位字节字符(多个字节)缓冲区可以有也可以无有适用数据任何只适用于文本类数据
一、前言 开门见山的说,transition、transform和translate这三者经常有人搞混,先把这三者做一个简单解释:
transform是 转换,指的是改变所在元素的外观,它有很多种手段(转换函数)来改变外观,例如 位移、缩放、旋转 等,而其中的位移的函数名就叫translate,所以说,translate是transform的一部分。transition是 过渡,指的是某个CSS属性值如何平滑的进行改变,就是平常说的 动效。而transform是没有动画效果,你改变了它的值,元素的样子就唰的改变了。 二、transition 过渡 1.基本用法 好,接下来看一下transition的基本用法:
transition: [属性名] [持续时间] [速度曲线] [延迟时间]; 我们可以很方便的用这个过渡来给某一个属性加上好看的动效。例如,高度属性的值改变时,延迟 0.5 秒后以 ease 曲线进行过渡,持续2秒:
transition: height 2s ease .5s; 或者一个属性不够,想要监听所有属性。
transition: all 2s ease .5s; 注意,这里的所有属性是指能进行动画过渡的属性,有很多属性是不能进行过渡的,比如display,你不能让一个div的显示模式慢悠悠的发生过渡。
有了上面的认识时候搭配:hover等可以引起属性值变化的伪类时就可以很轻松的做出一个动画效果:
.box { width: 10px; transition: width 0.4s ease; } .box:hover { width: 50px; } 2.给多个属性指定同一个过渡 接下来深入一点,如果你想给多个属性指定同一个过渡的时候该怎么做?如下:同时监听宽度和高度进行过渡。
transition-property: width, height; transition-duration: 2s; transition-timing-function: ease; transition-delay: .5s; 但是,千万不要简写成这样!会报错的。至于为啥报错,看下一小节。
transition: height, width 2s ease .5s; 3.给多个属性指定多个过渡 咱们再深入一点,如果想指定height的过渡为2s,width的过渡为3s该怎么做呢?如下:
关于链表的TIPS** 链表中各结点在内存中可以不是连续存放的在链表结点 的数据结构中,结构体内的指针域的数据类型可以使用未定义成功的数据类型,这是C语言中唯一规定可以先使用后定义的数据结构头指针:存放一个地址(头结点地址),指向第一个结点(一定不要把头结点当做头指针,头指针只不过是储存头结点的地址而已,我在学的时候这里总出错)具体链表的结构体建立,包括链表的指针域和数据域如下: struct student { int num; float score; student *next; }; 建立单链表的一些约定
- 每个结点类型用LinkList表示
- 数据域 data(类型:通用类型标识符 ElemType)
- 指针域 next typedef struct LNode { ElemType data; struct LNode *next; }LinkList; 创建单链表 头插法
新产生的结点作为新的表头插入链表
(第一次执行②的作用是确定尾指针NULL) #include<stdio.h> LinkList*CreateListF(ElemType a[],int n) { LinkList*head,*s; int i; head = (LinkList*)malloc(sizeof(LinkList)); //创建头结点 head->next = NULL; for(i=0;i<n;i++) { s = (LinkList*)malloc(sizeof(LinkList)); //创建新结点 s->data=a[i]; s->next=head->next; //第一次执行时是为了确定尾指针NULL head->next=s; //将*s插在原开始结点之前、头结点之后 } return head; } 2.尾插法( 一句话,相当于不断开创新的结点,然后不断将新的结点的地址当做尾结点。尾结点不断后移,而新创的结点时按照创建的先后顺序而连接的。先来先到)
四、题目:方格计数
如图p1.png所示,在二维平面上有无数个1x1的小方格。
我们以某个小方格的一个顶点为圆心画一个半径为1000的圆。
你能计算出这个圆里有多少个完整的小方格吗?
注意:需要提交的是一个整数,不要填写任何多余内容
分析思路:
1.以原点为圆心,半径为1000,则x轴范围(-1000,1000),y轴范围(-1000,1000)
for(x:-1000~1000)
for(x:-1000~1000)
if(x^2+y ^2<=1000 ^2)
需要注意轴上的点是不能形成方格的(图中橘色的点)
2.可以将圆分成4等分,也就是4个卦限,选择其中一个算方格数,最后乘以4
以第一卦限为例
代码1:
public class Fanggenum { public static void main(String[] args) { int r=0; for(int i=-1000;i<=1000;i++) { for(int j=-1000;j<=1000;j++) { if(i==0||j==0) continue; if(i*i+j*j<=1000*1000) r++; } } System.out.println(r*4); } } 代码2:第一卦限
public class Fanggenum { public static void main(String[] args) { int r=0; for(int i=1;i<=1000;i++) { for(int j=1;j<=1000;j++) { if(i*i+j*j<=1000*1000) r++; } } System.out.println(r*4); } } 代码3:优化
目录
1.需求分析
2.功能实现
3.使用说明
3.1 使用前准备
3.2 布局引用
3.3 适配器生成、绑定及数据更新
4.注意事项
5.最后
1.需求分析 京东首页分类栏滑动效果图 像直播间礼物列表和电商首页类别列表,常见出现这种需求:当前页展示效果为GridView样式,同时具有ViewPager换页效果
一般采用 ViewPager + RecyclerView 组合实现,本文 HorizontalGridView 只是在此基础上进行封装,以达到能更方便快捷实现该效果的目的
2.功能实现 HorizontalGridView展示效果 设计思路:
自定义 HorizontalGridView 继承 LinearLayout ,添加 ViewPager 和 TabLayout ,根据需要展示的 总数据数目 、 每行最大展示数目 及 每页最大展示数目 生成把多个 RecyclerView ,并填充到 ViewPager 中
需要实现带 圆角 的 指示器 / 指示器栏背景
为了提高该 View 适用性,该View需要支持设定 每行最大展示数目 及 每页最大展示数目
为了提高该 View 的使用便捷性,采用 适配器模式 和 观察者模式 进行封装设计
存在问题:当 HorizontalGridView 高度设置为 wrap_content 时,会出现占满全屏的现象。该现象因为 ViewPager 高度默认占满全屏造成。解决办法如下
到目前为止, shell脚本最常见的一个用途就是处理文本文件。检查日志文件、读取配置文件、处理数据元素,shell脚本可以帮助我们将文本文件中各种数据的日常处理任务自动化。但仅靠shell脚本命令来处理文本文件的内容有点勉为其难。如果想在shell脚本中处理任何类型的数据,你得熟悉Linux中的sed和gawk工具。这两个工具能够极大简化需要进行的数据处理任务。
一.文本处理
有时候,你会发现需要自动处理文本文件,可你又不想动用全副武装的交互式文本编辑器。在这种情况下,有个能够轻松实现自动格式化、插入、修改或删除文本元素的简单命令行编辑器就方便多了。
Linux系统提供了两个常见的具备上述功能的工具。本节将会介绍Linux世界中最广泛使用的两个命令行编辑器:sed和gawk。
1.sed编辑器
sed编辑器被称作流编辑器(stream editor),和普通的交互式文本编辑器恰好相反。在交互式文本编辑器中(比如vim),你可以用键盘命令来交互式地插入、删除或替换数据中的文本。流编辑器则会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个命令文本文件中。sed编辑器会执行下列操作。
(1) 一次从输入中读取一行数据。
(2) 根据所提供的编辑器命令匹配数据。
(3) 按照命令修改流中的数据。
(4) 将新的数据输出到STDOUT。
在流编辑器将所有命令与一行数据匹配完毕后,它会读取下一行数据并重复这个过程。在流编辑器处理完流中的所有数据行后,它就会终止。
由于命令是按顺序逐行给出的,sed编辑器只需对数据流进行一遍处理就可以完成编辑操作。
这使得sed编辑器要比交互式编辑器快得多,你可以快速完成对数据的自动修改。
sed命令的格式如下。
sed options script file
选项允许你修改sed命令的行为,可以使用的选项已在表19-1中列出。
script参数指定了应用于流数据上的单个命令。如果需要用多个命令,要么使用-e选项在命令行中指定,要么使用-f选项在单独的文件中指定。有大量的命令可用来处理数据。我们将会在本章后面介绍一些sed编辑器的基本命令,然后在第21章中会看到另外一些高级命令。
1)在命令行定义编辑器命令
默认情况下,sed编辑器会将指定的命令应用到STDIN输入流上。这样你可以直接将数据通过管道输入sed编辑器处理。这里有个简单的示例。
$ echo "This is a test" | sed 's/test/big test/' This is a big test $ 这个例子在sed编辑器中使用了s命令。s命令会用斜线间指定的第二个文本字符串来替换第一个文本字符串模式。在本例中是big test替换了test。
在运行这个例子时,结果应该立即就会显示出来。这就是使用sed编辑器的强大之处。你可以同时对数据做出多处修改,而所消耗的时间却只够一些交互式编辑器启动而已。
当然,这个简单的测试只是修改了一行数据。不过就算编辑整个文件,处理速度也相差无几。
$ cat data1.txt The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
位运算符以及他们的用途 C语言中的6个位运算符位运算的实用 一.位运算符 1.& ——按位与运算 "&"是双目运算符(双目运算符就是有两个操作数),
只有当二进制对应位上都是1的时候结果才为1。
aba&b111100010000 2.| ——按位或运算 “|”是双目运算符,
只有当二进制对应位上有一个为1,结果为1
aba或b111101011000 3.^ ——按位异或运算 “^”是双目运算符,
只有当二进制对应位上二者不同时,结果为1
aba^b110101011000 4.~ ——求反运算符 “~”为单目运算符(只有一个操作数),具有右结合性(从右向左执行计算),
a~a0110 5.<< ——左移运算符 “<<”是双目运算符,将二进制位进行左移操作,
左移n位就是乘以2的n次方。
“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。
6.>> ——右移运算符 “>>”是双目运算符,将二进制位进行右移操作,
右移n位就是除以2的n次方。
“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。同时注意,>>运算符是用符号位来填充高位的。
二.位运算符的实用 位运算符是针对二进制位上的运算
1.判断奇偶数 因为在二进制中只有当最后一位二进制数为1的时候才表示奇数,而相反只有当最后一位二进制数为0的时候才表示偶数,所以利用“&”的特点,我们可以得到:
if((x&1) == 1) printf("x是奇数\n"); else if((x&1) == 0) printf("x是偶数\n"); 2.交换两个整数 在C语言中,我们可以编写一个swap函数来交换两个整数:
void sswap(int *a,int *b) { int t; t = *a; *a = *b; *b = t; } 但是其实也可以使用“^”的特性来进行两个整数的交换:
void sswap(int *a,int *b) { *a = *a ^ *b; *b = *b ^ *a; *a = *a ^ *b; } 3.
1.使用Structured Streaming读取Socket数据,把单词和单词的反转组成 json 格式写入到当前目录中的file文件夹中 package com.wzy.code.code01 import org.apache.spark.sql.streaming.Trigger import org.apache.spark.sql.{DataFrame, Dataset, SparkSession} object StructuredStreaming { def main(args: Array[String]): Unit = { //使用Structured Streaming读取Socket数据,把单词和单词的反转组成 json 格式写入到当前目录中的file文件夹中 val spark = SparkSession.builder().appName("Json").master("local[*]").getOrCreate() val sc = spark.sparkContext sc.setLogLevel("WARN") val liens: DataFrame = spark.readStream .option("host", "node01") .option("port", 9999) .format("socket") .load() import spark.implicits._ val dataDS: Dataset[String] = liens.as[String] val reverseDF = dataDS.flatMap(_.split(" ")).map({ x => (x, x.reverse) }).toDF("before", "reverse") reverseDF.show() reverseDF.writeStream .format("json") .option("path","E:\\SparkCode\\SparkSql\\Day0417Work\\file") .option("checkpointLocation","./ck1")// 必须指定 checkpoint 目录,否则报错 .
1.确定后端返回的是一个文件
2.调用导出文件的接口,并携带相应的数据以及发送请求,获取相应的值
3.在发送请求的时候,如果访问接口需要携带token值的话,一定要和后端确定token的和变量名
4.如果你的接口请求方式是一个get的请求,可以直接将token以拼接字符串的形式进行传递
话不多说,看代码
这里是你要发送的请求 this.$http.get('/business-instock/instock/return/export', { params: { 'buyerId': this.getSupplier.buyerId, 'status': this.documentFrom.documentType, 'access_token': this.token 这里是token值 } }) .then((res) => { if (res.status === 200) { 这里的地址就是你文件存放在服务器上的路径,因为是get请求,所以采用的是字符串拼接的方式,当然也要传递token的值 let excelUrl = 'http://23wz.top:9000/business-instock/instock/return/export?status=' + this.documentFrom.documentType + '&buyerId=' + this.getSupplier.buyerId + '&access_token=' + this.token 这里是页面要跳转打开的下载页面的地址,也就是在服务器的地址 window.location.href = excelUrl } else { this.$message.error('导出失败') } })
哨兵2号是高分辨率多光谱成像卫星,携带一枚多光谱成像仪(MSI),用于陆地监测,可提供植被、土壤和水覆盖、内陆水路及海岸区域等图像,还可用于紧急救援服务。分为2A和2B两颗卫星。
第一颗卫星哨兵2号A于2015年6月23日01:52 UTC以“织女星”运载火箭发射升空。
6月29日,在轨运行4天的哨兵-2A卫星,传回了第一景数据,幅宽290km,卫星第一次扫描的范围是从瑞典开始,经过中欧和地中海,到阿尔及利亚结束。
第二颗卫星哨兵2号B于2017年3月07日北京时间9时49分 UTC以“织女星”运载火箭发射升空。
欧洲航天局介绍说,“哨兵-2B”卫星与2015年6月发射的“哨兵-2A”卫星为同一组,携带高分辨率多光谱成像装置,主要用于监测土地环境,可提供有关陆地植被生长、土壤覆盖状况、内河和沿海区域环境等信息,不仅对改善农林业种植、预测粮食产量、保证粮食安全具有重要意义,还可用于监测洪水、火山喷发、山体滑坡等自然灾害,为人道主义救援提供帮助。两者同时进入运行状态后,每5天可完成一次对地球赤道地区的完整成像,而对于纬度较高的欧洲地区,这一周期仅需3天。
哨兵-2号卫星携带一枚多光谱成像仪(MSI),高度为786km,可覆盖13个光谱波段,幅宽达290千米。地面分辨率分别为10m、20m和60m、一颗卫星的重访周期为10天,两颗互补,重访周期为5天。从可见光和近红外到短波红外,具有不同的空间分辨率,在光学数据中,哨兵-2号数据是唯一一个在红边范围含有三个波段的数据,这对监测植被健康信息非常有效。
哨兵2号(sentinel-2)各个波段的意义说明:
Band1:海岸/气溶胶波段:用来监测近岸水体和大气中的气溶胶。
Band2、3、4:可见光波段
Band5、6、7:红边范围内波段对监测植被健康信息非常有效
Band8:近红外波段(宽)
Band8A:近红外波段(窄)
Band9:水蒸气波段
Band10、11、12:短波红外波段
从2015年12月3日起,哨兵2A(Sentinel2A)数据正式向全球用户提供免费下载。
————————————————
版权声明:本文为CSDN博主「遥感小白的笔记」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
更多请查看:https://blog.csdn.net/qq_41718357/article/details/83536322
当前内核4.15,是由4.14莫名其妙通过软件升级程序自动升级上来的。
打算自行编译配置升级到4.16
先到镜像地址:http://mirror.bjtu.edu.cn/kernel/linux/kernel/
或者 : http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/
下载4.16.9版本源码
编译前使用make mrproper命令清理掉编译痕迹,该指令与make clean有区别,可以理解为更彻底。
图形化配置xconfig的依赖需要 flex bison qttools5-dev 。
make xconfig到此可以使用。
然后按百度文档描述使用make dep
root@lc-NUC8i5BEH:/usr/src/linux-4.16.9# make dep
Makefile:976: "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel"
make: *** No rule to make target 'dep'。 停止。
这个还是缺依赖,按提示安装libelf-dev等。
以上所有内容均为百度过的手段,全都没用,全都不对,全是扯淡。
除了这个镜像地址是不错的
镜像地址:http://mirror.bjtu.edu.cn/kernel/linux/kernel/
或者 : http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/
最最最正确的编译内核和升级内核的说明文档就在源码目录下的README文件,
请仔细阅读,你需要的不是百度,而是有道翻译。
Linux kernel release 4.x <http://kernel.org/>
=============================================
These are the release notes for Linux version 4. Read them carefully,
as they tell you what this is all about, explain how to install the
听起来高大上的“算法”,其实一点也不难学。这次介绍的是关于运行在计算机上面的算法,正如你日常所运行的算法会影响你每天的生活一样,在计算机上运行算也会影响你的算法。我们可以利用计算机搜索信息的简单方式解决各种排序问题的方法,利用有向无环图和最短路径方法解决基本问题。
第1章 什么是算法以及为什么应该关注算法
第2章 如何描述和评估计算机算法
第3章 排序算法和查找算法
第4章 排序算法的下界和如何超越下界
第5章 有向无环图
第6章 最短路径
第7章 字符串算法
第8章 密码学基础
第9章 数据压缩
第10章 难?问题
左程云老师用3个小时讲明白了字节跳动的两道最新面试题:
1、已知一个搜索二叉树后序遍历的数组posArr, 请根据posArr,重建出整棵树 返回新建树的头节点 2、给定长度为m的字符串aim,以及一个长度为n的字符串str 问能否在str中找到一个长度为m的连续子串, 使得这个子串刚好由aim的m个字符组成,顺序无所谓, 返回任意满足条件的一个子串的起始位置,未找到返回-1
栈操作原则 使用栈操作数据,必须遵循“先入后出”的原则;
栈操作之链栈 链栈是用链表实现栈的存储结构,链表头部作为栈顶,链表尾部为栈底(单链表);
入栈 写入数据时,实际是对链表做“头插”操作,空链表时,头指针head指向null;新进数据插入链表头部,头指针head指向当前链表头部;以此类推,这种操作即为入栈(压栈);
出栈 读出数据时,实际是删除当前链表的头部(首元节点),将头指针head指向新的链表头部(原首元节点的下一节点,成为当前首元节点),以此类推,这种操作即为出栈(弹栈);
代码实现 在这段代码中,主要实现:
1.创建链表数据结构,初始化为空链表;
2.入栈操作,压入8个数据;
3.出栈操作,弹出2个数据,栈中剩余最先入栈的6个数据;
4.打印各个过程的情况;
5.在CMD中运行查看结果;
#include <stdio.h> #include <stdlib.h> #include <string.h> // 创建链表的数据结构 typedef struct list_stack { int data; struct list_stack *next; } list_stack; // 链栈入栈操作,head为当前链栈栈顶指针,val为入栈的值 list_stack *push(list_stack *head, int val) { // 创建新节点,并分配内存 list_stack *p = (list_stack *)malloc(sizeof(list_stack)); p->data = val; // 建立当前节点与头指针head之前所指节点的关系(头插) p->next = head; // 使头指针head指向当前新插入节点 head = p; printf("push elem %d\r\n", head->data); printf("current head elem is : %d\r\n"
pyecharts 图例颜色设置 目前使用的pyecharts版本1.7.1,图例暂时不支持颜色指定
图例的颜色是根据你添加图例的顺序,然后再根据全局变量colors 里面的颜色的顺序一一对应的:
self.colors = ( "#c23531 #2f4554 #61a0a8 #d48265 #749f83 #ca8622 #bda29a #6e7074 " "#546570 #c4ccd3 #f05b72 #ef5b9c #f47920 #905a3d #fab27b #2a5caa " "#444693 #726930 #b2d235 #6d8346 #ac6767 #1d953f #6950a1 #918597" ).split() 当然,如果你添加数据的时候指定了颜色,colors 会自动在顶部帮你添加对应的颜色值 ,也就是说你最后添加的是在颜色值得最顶端的
.add_yaxis("商家A", Faker.values(), linestyle_opts=opts.LineStyleOpts(color="red"),) .add_yaxis("商家B", Faker.values(), color="blue") .add_yaxis("商家C", Faker.values(), color="green") 分别设置线的颜色和系列 label 颜色的颜色
但是最后显示的时候,是不是就感觉乱套了呢?
看一下此时colors里面的情况:
是不是明白什么了呢?
添加系列线的颜色是会添加进去的,而设置linestyle_opts则不会添加, 而且添加的顺序是反过来的,最后添加的颜色是在最前面的呢!
def _append_color(self, color: Optional[str]): if color: self.colors = [color] + self.colors if self.theme == ThemeType.
用的是vs2019编译器
这是一个有关运算符重载的例题,希望大家作以参考 定义有理数类(分母不为0的分数,分子分母均为整数)Rational,实现相应操作符的重载。
(1)定义私有数据成员:分子int iUp ; 分母int iDown。
(2)定义私有成员函数:void Reduce() 和 int Gcd(int l, int r),分别用于有理数的约简和求两个整数的最大公约数。其中,在约简时需要求取分子与分母的最大公约数。
(3)定义构造函数,在构造函数体内可调用Reduce对有理数进行约简。
(4)将负号-和赋值运算符=重载为公有成员函数,分别用于求有理数的负数和赋值。
(5)将前置++、前置–、后置++、后置–重载为公有成员函数,实现有理数自增1或自减1。
(6)将+、-、*、/重载为友员函数,实现有理数的加减乘除。
(7)将<、<=、>、>=重载为友员函数,实现有理数的大小关系比较。
(8)重载流插入符<<和流提取符>>,分别用于有理数的输出和输入。其中,输出格式为“分子/分母”,若为整数,则直接输出整数。
在main函数中,根据输入的分子和分母定义两个有理数对象a和b。再定义几个有理数对象分别用于表示a和b的加、减、乘、除、前置自增a、前置自减a、后置自增a、后置自减a,并依次各个对象的结果。最后依次用<、<=、>、>=比较a和b的大小关系,并依次输出比较结果(true或false)。
输入 两个有理数a和b的的分子和分母 输出 有理数a和b的加、减、乘、除以及前置自增a、前置自减a、后置自增a、后置自减a有理数a和b的<、<=、>、>=的结果 样例输入 4 3
3 2
样例输出 a+b: 17/6
a-b: -1/6
a*b: 2
a/b: 8/9
-a: -4/3
++a: 7/3
–a: 4/3
a++: 4/3
a–: 7/3
a<b: true
a<=b: true
a>b: false
a>=b: false
下面展示此题代码
#include <iostream> using namespace std; class Rational{ friend Rational operator+(Rational& p1, Rational& p2); friend Rational operator-(Rational& p1, Rational& p2); friend Rational operator*(Rational& p1, Rational& p2); friend Rational operator/(Rational& p1, Rational& p2); friend bool operator<(Rational& p1, Rational& p2); friend bool operator<=(Rational& p1, Rational& p2); friend bool operator>(Rational& p1, Rational& p2); friend bool operator>=(Rational& p1, Rational& p2); friend ostream& operator <<(ostream& cout, Rational& p); friend istream& operator >>(istream& cin, Rational& p); private: int iUp; int iDown; //有理数约简 void Reduce() { int t = abs(Gcd(iUp, iDown)); iUp = iUp / t; iDown = iDown / t; if (iDown < 0){ iDown = -iDown; iUp = -iUp; } } int Gcd(int l, int r) { int temp = l % r; while (temp !
写在前面 本文转自涛哥的《亿级流量网站架构核心技术》,啥也不说了,向开涛大神致敬!
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流。
限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。
一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。
先有缓存这个银弹,后有限流来应对618、双十一高并发流量,在处理高并发问题上可以说是如虎添翼,不用担心瞬间流量导致系统挂掉或雪崩,最终做到有损服务而不是不服务;限流需要评估好,不可乱用,否则会正常流量出现一些奇怪的问题而导致用户抱怨。
在实际应用时也不要太纠结算法问题,因为一些限流算法实现是一样的只是描述不一样;具体使用哪种限流技术还是要根据实际场景来选择,不要一味去找最佳模式,白猫黑猫能解决问题的就是好猫。
因在实际工作中遇到过许多人来问如何进行限流,因此本文会详细介绍各种限流手段。那么接下来我们从限流算法、应用级限流、分布式限流、接入层限流来详细学习下限流技术手段。
限流算法 常见的限流算法有:令牌桶、漏桶。计数器也可以进行粗暴限流实现
令牌桶算法 令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。令牌桶算法的描述如下:
假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌;桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝;当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上;如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。 漏桶算法 漏桶作为计量工具(The Leaky Bucket Algorithm as a Meter)时,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolicing),漏桶算法的描述如下:
一个固定容量的漏桶,按照常量固定速率流出水滴;如果桶是空的,则不需流出水滴;可以以任意速率流入水滴到漏桶;如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的。 令牌桶和漏桶对比 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。 另外有时候我们还使用计数器来进行限流,主要用来限制总并发数,比如数据库连接池、线程池、秒杀的并发数;只要全局总请求数或者一定时间段的总请求数设定的阀值则进行限流,是简单粗暴的总数量限流,而不是平均速率限流。
到此基本的算法就介绍完了,接下来我们首先看看应用级限流。
单机限流 // 令牌桶算法实现 tryAcquire @Slf4j public class RateLimiterExample1 { private static RateLimiter rateLimiter = RateLimiter.create(5); public static void main(String[] args) throws Exception { for (int index = 0; index < 100; index++) { if (rateLimiter.tryAcquire(190, TimeUnit.MILLISECONDS)) { handle(index); } } } private static void handle(int i) { log.
ll -lh
Linux系列:查看子目录文件夹大小
指定单位 ls -l --block-size=g
ls -l --block-size=m
ls -l --block-size=k 扩展 man ls
(一)Webots简介 Webots是Cyberbotics公司出品的便携式机器人仿真平台,可运行在windows,Mac和Linux上,内建3D编辑器,可构建3D机器人模型。应用C++或JAVA或者MATLAB编辑机器人程序可模拟机器人的动作。也就是说,它支持多种编程语言,包括C, C++, JAVA, Python, Matlab等。这个软件封装并提供了各种API函数,然而这么好的软件他的学习资料大多都是英文的,所以在一开始学习起来有很多困难,软件官网发布了pdf格式和html格式的资料,如果在网页直接看的话,可以用一些翻译软件查询看不懂的地方或直接网页翻译,这里我贴出软件下载方式,第一个链接是官网下载,可能会下载不下来,所以又分享了网盘,和pdf格式的参考手册(英文版)。点击进入:官方网址网盘下载:Webots2019b( 提取码:53xj ) 参考手册 ( 提取码:ghe4 ) (二)软件界面 软件是支持简体中文的,打开软件后,点击工具栏Tools–>Preferences(首选项)–>Language选择简体中文,然后选择yes重启软件生效。进入软件后,可以看到软件大致分为如下及部分:
1.场景树
2.仿真界面
3.调试窗口
4.菜单栏
5.代码区 (三)新建项目 界面 我们可以点击向导–>新建项目目录–>选择合适路径–>文件命名–>命名下面选项可以默认,新建后,我们可以在场景树中看到如下
Worldinfo:主要是一些物理参数设定,包括重力等Viewpoint:里面记录了在仿真试图中观察的角度和位置后面两个都与背景有关 创建模型 首先,创建大地:
右击–>新增–>PROTO nodes–>objects–>floors–>选择地面创建球体
1.右击–>新增–>Base nodes -->Soild(添加固体节点)
2.在Soild节点下展开,点击children,新建一个shape节点,点击节点,在下面DEF中输入“sphere”
3.展开Shape,在geometry中增加sphere节点这个时候,我们可以在场景中看到一个白色的球,但是他嵌入在大地中,此时,我们可以在Soild下,修改translation值,改变小球位置这个时候小球就出现在了场景中,我们可以通过Shape节点下appearance节点,改变模型的纹理等,我们双击,增加Appearance节点 双击texture ImageTexture节点,添加纹理图片,我们可以得到 增加物理属性 在Soild节点下,双击physics节点,增加physics节点,点击增加节点后,发现小球不见了,原来是因为仿真是启动状态,增加物理属性后,小球有了密度,质量等属性,所以在重力作用下向下坠落,此时,我们点击菜单栏的停止仿真,然后在translation中将坐标重置即可。展开physics节点,下方我们主要看前两个参数,第一个density是密度,第二个mass是质量,两个我们只需要设置一个,另一个设置成-1,即可,系统会根据其中一个的设定值计算出相应的值,比如我们这里默认,就是密度1000,质量-1。在设置完成后,点击仿真,依旧会掉落到大地下方,因为还需要设置碰撞属性。 增加碰撞属性 在boundingObject节点下,双击,增加一个sphere节点,将其设置为和小球一样的大小即可。
(四)仿真效果 点击仿真,可以看到小球在重力作用下,掉落到地面上
另外,还有一些在运行仿真时移动物体的方法:
ALT+鼠标左键:给物体一个力
ALT+鼠标右键:给物体一个力矩
一、前言 记录一次服务假死的整个排查过程,服务基础为spring boot + druid + 多数据源切换,在请求过多(尤其是长事务请求)时,服务出现请求无响应的状况,之前未完结的查询也没有任何返回结果。
二、定位问题原因 问题出现时,表现如下图,后台无任何报错,sql语句戛然而止,后续的查询被中断。这时如果再次发起某个请求,后台服务处于大部分时间不能收到新的请求的状态,或者偶尔可收到请求但不会执行crud。经过一段时间后,日志输出了session校验的内容,此时我推测服务并不是真的宕机,而是处于假死状态。
druid配置如下
看了下配置文件,最大连接池数量设置为100,但重现问题的过程不需要特别多的请求,稳妥起见,将最大数量改为200,问题没有解决,初步推测不是因为请求达到了连接池上限。
CPU和内存均无异常。
期间还优化过业务逻辑,甚至将同步请求改为异步请求,都无济于事。一筹莫展之际,想起了druid自带的监控功能。打开监控页面,找到了一处令我怀疑的地方。数据源中的逻辑打开和逻辑关闭次数起初是一致的,随着查询次数增多,逻辑关闭次数小于逻辑打开次数,于是我怀疑数据库连接池出现了泄露的情况,根据URI监控中显示的情况,jdbc出错数刚好等于逻辑打开与逻辑关闭次数的差,也就是说,很有可能由于jdbc出错导致数据库连接池未正确关闭。
(图片为部分截图,下面还有个1没有截到) 按着这个思路,对测试部分代码进行了排查,无果。后来根据druid官方的文档,找到了下面这段话。
从这段话中可以看出,判断是否是泄露应该在URI监控中,点击URI进入详情页面,查看打开和关闭的数量是否相等。于是我在逻辑连接打开和逻辑连接关闭次数有巨大差异的情况下,对每一个URI都进行了核查,所有URI详情中,连接池获取连接次数都是等于连接池关闭连接次数的,理论上证明数据库连接池并无泄露。
前面图中druid的文档中还提到了removeAbandoned等三个属性用以检测数据库连接池泄露,于是我将这三个属性写在了yml里。
一通华丽丽的操作下来,服务依旧被玩坏,然后打开了druid的监控,找到了数据源页面中的ActiveConnectionStackTrace。点开,没数据???控制台也没输出日志??? 网上找了一些文章,然后大胆的怀疑是不是druid的配置没生效?再看一下druid运行时的数据源,果不其然。初始化连接大小、最小空闲连接数、最大连接数、超时时间等等等等,除了数据库指向是生效的,其他配置使用的都是缺省值。所以我即使把最大数量从100改成了200,依然是没几个请求就爆炸了。
三、解决方案 下面开始着手解决配置没生效的问题。由于项目是多数据源切换,于是找到了这个配置文件,尝试着改造了一下,将yml中的配置set到了DruidDataSource对象中。
(下图仅展示了与本文有关的内容) @Configuration public class DruidConfig { @Value("${spring.datasource.druid.initial-size}") private int initialSize; @Value("${spring.datasource.druid.min-idle}") private int minIdle; @Value("${spring.datasource.druid.max-active}") private int maxActive; @Value("${spring.datasource.druid.max-wait}") private int maxWait; @Value("${spring.datasource.druid.pool-prepared-statements}") private boolean poolPreparedStatements; @Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}") private int maxPoolPreparedStatementPerConnectionSize; @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") private int timeBetweenEvictionRunsMillis; @Value("${spring.datasource.druid.min-evictable-idle-time-millis}") private int minEvictableIdleTimeMillis; @Value("${spring.datasource.druid.test-while-idle}") private boolean testWhileIdle; @Value("${spring.datasource.druid.test-on-borrow}") private boolean testOnBorrow; @Value("${spring.datasource.druid.test-on-return}") private boolean testOnReturn; @Value("
1)按钮点击事件 onclick不跳转的情况
给button添加type=“button”,button标签的type默认属性值是submit,不设置默认行为是提交表单,例如下面两个实例,实例1点击按钮时是不会执行跳转的,实例2才会正常跳转到1.html
下面两个实例:
实例1:
<button type="submit" onclick="window.location.href='1.html'"</button> 实例2:
<button type="button" onclick="window.location.href='1.html'"</button> 2)window.location.href 不跳转的情况
以下是登录按钮的跳转事件,在ajax中window.location.href 不跳转也要看看自己的登录按钮 的type类型 要改成 type="button"
使用方法
按住alt和SysRq键的同时,再输入reisub,输入完b之后,系统自动重启。
指令说明
按住art和SysRq键时,输入的一切都会直接交给Linux内核来处理。
reisub中的每一个字母都是一个独立操作,他们分别表示:
Alt+SysRq+R 把键盘从X手中夺过来
Alt+SysRq+E 终结所有进程
Alt+SysRq+I 强制关闭所有进程
Alt+SysRq+S 同步所有挂载的文件系统
Alt+SysRq+U 重新挂载所有的文件系统为只读
Alt+SysRq+B 重启
记忆方式:busier的倒序!
服务器网络太差只能在自己的小笔记本上编译,结果笔记本死翘翘了,只好重启了,硬关机是在有点粗暴,于是使用上述方法,尽量优雅些。。。
说起IO,很多人首先想到的是磁盘中的文件,将磁盘中的文件读到内存以及内存内容写入文件。但是还有一种内存和内存之间的IO,叫类文件对象,这一篇我们就一起来学习下python中的两个类文件对象:StringIO和BytesIO。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
文章目录 内存中的IO操作环境标志位StringIOBytesIO总结 内存中的IO 首先必须要搞清一个问题,就是为什么要有内存级别的IO?
之前说的磁盘上的文件,就是将数据持久化到磁盘的一块区域,供后面重复使用。其优点就是持久化稳定不丢失,但是缺点也很明显,就是每次要使用都要从磁盘读入,相对内存而言很缓慢。
如果只是短时间的重复利用,并不希望长期持久化,而且对速度的要求比较高,这时候就可以考虑缓存。说到缓存,很多朋友就想到redis,熟悉python的朋友还会想到装饰器和闭包函数。
不过python已经原生为我们准备好了类文件对象(file-like object),这种对象在内存中创建,可以像文件一样被操作。
下面我们就来学习下两种类文件对象,StringIO和BytesIO。
操作环境 以下操作基于Python3.7。
因为python3中将StringIO和BytesIO都集成到了io模块中,导入方式和python2不同,这个要注意。
标志位 内存中的对象有一个标志位的概念,往里面写入,标志位后移到下一个空白处。而读数据的时候是从标志位开始读,所以想要读取前面的数据需要手动将标志位进行移动。在下面的操作中我们会看到。
StringIO 看名字就大概能猜到,这种类文件对象是用来存储字符串的。
新建一个StringIO对象
In [1]: from io import StringIO In [2]: s=StringIO() In [3]: type(s) Out[3]: _io.StringIO 写入一些字符串
In [6]: s.write('this\nis\na\ngreat\nworld!') Out[6]: 22 In [7]: s.read() Out[7]: '' 可以看到尝试读取的时候返回为空,这就是上面提到的标志位的原因。因为此时write以后标志位跑到了第23字节,所以再往后读取就是空值。
将标志位移动到最前面再尝试读取,就能成功了
In [11]: s.seek(0) Out[11]: 0 In [12]: s.read() Out[12]: 'this\nis\na\ngreat\nworld!' 但是获取全部值方法不受这个标志位影响
In [13]: s.read() Out[13]: '' In [14]: s.getvalue() Out[14]: 'this\nis\na\ngreat\nworld!' 除了一次读取全部内容,还可以\n为分界读取单行
In [15]: s.
hive数据倾斜 目录
1、什么是数据倾斜?
2、Hadoop 框架的特性
3、主要表现
4、容易数据倾斜情况
5、产生数据倾斜的原因
6、业务场景
(1)空值产生的数据倾斜
(2)不同数据类型关联产生数据倾斜
(3)大小表关联查询产生数据倾斜
1、什么是数据倾斜? 由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点
2、Hadoop 框架的特性 A、不怕数据大,怕数据倾斜
B、Jobs 数比较多的作业运行效率相对比较低,如子查询比较多
C、 sum,count,max,min 等聚集函数,通常不会有数据倾斜问题
3、主要表现 任务进度长时间维持在 99%或者 100%的附近,查看任务监控页面,发现只有少量 reduce 子任务未完成,因为其处理的数据量和其他的 reduce 差异过大。 单一 reduce 处理的记录数和平均记录数相差太大,通常达到好几倍之多,最长时间远大 于平均时长。
4、容易数据倾斜情况 A、group by 不和聚集函数搭配使用的时候
B、count(distinct),在数据量大的情况下,容易数据倾斜,因为 count(distinct)是按 group by 字段分组,按 distinct 字段排序
C、 小表关联超大表 join
5、产生数据倾斜的原因 A:key 分布不均匀
B:业务数据本身的特性
C:建表考虑不周全
D:某些 HQL 语句本身就存在数据倾斜
6、业务场景 (1)空值产生的数据倾斜 场景说明
在日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的 user_id 相关联,就会碰到数据倾斜的问题。
解决方案 解决方案 1:user_id 为空的不参与关联
python 格式化字符串报错:TypeError: not all arguments converted during string formatting 最近在将数据写入数据库的时候,sql报错,TypeError: not all arguments converted during string formatting,
字面意思为:所有参数在格式化时未能格式化。首先,我们要明确,格式化字符串是为了将变量按照定义的格式添加到原有字符串从而生成一个新的字符串,也就是说,有这么几个条件,第一,一个变量,第二,原字符串,第三,原字符串加变量生成新的字符串。代码演示:
a=('a','b') print(type(a)) print('woxiang %s' % a) #运行结果: #print('woxiang %s' % a) #TypeError: not all arguments converted during string formatting #首先定义了一个变量a,变量a的格式为<class 'tuple'> #原字符串‘woxiang’后想添加变量生成一个新的字符串,很显然,元组是有两个元素, #占位符%s只占了一个位置,另一个元素无处安放了,因此报错。那么,这个问题怎么解决呢? #显然,有多少个元素,就放几个占位符%s即可,中间以,隔开即可 a=('a','b') print(type(a)) print('woxiang %s%s' % a): #运行结果: <class 'tuple'> woxiang ab #那,如果是三个或者四个元素的元组呢?很显然,有几个元素,放几个占位符 #也就是n个元素放n个占位符 b=('aa','cc','dd') print('woxiang %s,%s,%s' %b) #运行结果: #woxiang aa,cc,dd 会不会有第二种方法呢?当然有,这种方法是将整个元组作为一个整体格式化,和上面的方法,将每个元素单独取出格式化是不一样的哦,也就是说效果是完全不一样的,切记。
下面依然是代码演示:
a=('a','b',1,'c') print(type(a)) print('woxiang %s' % (a,)) #运行结果: #<class 'tuple'> #woxiang ('a', 'b', 1, 'c') #切记,变量名用括号包裹,一定要加一个逗号,这样Python解释器才会将元组整体作为一个元素格式化 列表类型第一种方法是无效的,只能使用第二种方法,望牢记!!!!!!!!!!!!!
说明:python本地时间与UTC时间转换,程序中常用于日志或生成文件命名,待补充完善。
参考小例
# -*- coding: utf-8 -*- import time import datetime class TimeShift: def __init__(self): pass def get_utctime(self) -> datetime.datetime: utc_time = datetime.datetime.utcfromtimestamp(time.time()) return utc_time def utctime_format(self) -> str: ''' 作用:datetime.datetime格式化成字符串,可用于文件命名 :return: str ''' utc_fmt = self.get_utctime().strftime("%Y%m%d%H%M%S") return utc_fmt def get_localtime(self): local_time = datetime.datetime.now() return local_time def localtime_format(self): local_fmt = self.get_localtime().strftime("%Y%m%d%H%M%S") return local_fmt def utc2local(self, utc_st) -> datetime.datetime: ''' 作用:将utc时间转换成本地时间 :return: 返回本地时间 ''' now_stamp = time.time() local_time = datetime.datetime.fromtimestamp(now_stamp) utc_time = datetime.
在bash中,使用“ =~ ”进行字符包含的判断。
看例子:
需求:判断路径中是否含zip压缩文件,若有则将其解压。
该需求需判断ls中的文件是否包含".zip"关键字
首先使用
FILES=$(ls) 绑定当前路径下的文件名,存为list到FILES中
再使用for循环遍历 FILES
for FILE in $FILES do if [[ $FILE =~ ".zip" ]] then ZIP_FILE=$FILE echo "zip file name: $ZIP_FILE" unzip $ZIP_FILE fi done 完整代码:
#!/bin/bash FILES=$(ls) for FILE in $FILES do if [[ $FILE =~ ".zip" ]] then ZIP_FILE=$FILE echo "zip file name: $ZIP_FILE" unzip $ZIP_FILE fi done
系统:MacOS Mojave 10.14.5
因为需要访问内网,刚上手Mac又没有安装这个软件,然后在EasyConnect官网下载安装后,发现竟然连不上,软件提示:”客户端版本与服务器不匹配,请更新“。接下来就开始了填坑之路,如标题所示。下面就说说我遇到的坑吧,如下:
1.mac安装EasyConnect,输入Ip后提示:“客户端版本与服务器不匹配,请更新 然后会出来这么个页面,下载安装包:EasyConnectPlugin插件。
下载完后,安装的过程中那么就可能会出现标题2的坑
2.mac安装EasyConnectPlugin失败 虽然安装失败了,但是依然会在应用程序下出现一个图标,还不支持双击打开。那么我都没有安装成功,何来网友说的在Safari中设置插件呢?何来的降级Safari 12 呢?而且降级Safar是有风险的。仔细一想,标题1中提示的是客户端和服务器不匹配,虽然我也不知道服务端是什么版本,但是应该有个版本能够保持一致的吧,然后依旧一波度娘。最终在深信服社区找到了一个可用的版本:"提供临时的支持Mac OS10.15的EC客户端安装包,点击下载 密码: 0uni"。
下载安装后,就可以登录了
登录成功后,会打开Safari自动跳转弹出如下页面,不用管他,也不需要下载,此时其实已经连上内网了。ps:我安装的是EasyConnect_for mac 10.15(M6.3-M7.5R1)
可以看到登录信息,那么应该连上了:
总结:说了这么多,这也不是什么技术文,其实就是找到了一个可用的软件而已😂😂,然后记录了一下自己填的坑,这可是困扰了两个星期了,差点就要装双系统了,重装开发环境了,害~ 好在现在解决了,如果哪位伙伴有最终的解决办法了,欢迎留言哈,不胜感激!
使用协程要引入的命名空间:using System.Collections;
unity 之协程返回值
yield return null; // 下一帧再执行后续代码 yield return 6;//(任意数字) 下一帧再执行后续代码 yield break; //直接结束该协程的后续操作 yield return asyncOperation;//等异步操作结束后再执行后续代码 yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码 yield return WWW();//等待WWW操作完成后再执行后续代码 yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行 yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响); yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响); yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码 yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候....如:yield return new WaitUntil(() => frame >= 10); yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候.... 如:yield return new WaitWhile(() => frame < 10);
转自:https://blog.csdn.net/chrisnotfound/article/details/80111559
1. TCP Keepalive的起源 TCP协议中有长连接和短连接之分。
短连接环境下,数据交互完毕后,主动释放连接;
长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了TCP的Keepalive(保活探测)机制。
2. TCP Keepalive工作原理 当一个 TCP 连接建立之后,启用 TCP Keepalive 的一端便会启动一个计时器,当这个计时器数值到达 0 之后(也就是经过tcp_keep-alive_time时间后),一个 TCP 探测包便会被发出。这个 TCP 探测包是一个纯 ACK 包(规范建议,不应该包含任何数据,但也可以包含1个无意义的字节,比如0x0。),其 Seq号 与上一个包是重复的,所以其实探测包报文不在窗口控制范围内。
如果一个给定的连接在两小时内(默认时长)没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一:
客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。
客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送10个这样的探测 ,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
客户机正常运行,但是服务器不可达,这种情况与2类似,TCP能发现的就是没有收到探测的响应。
对于linux内核来说,应用程序若想使用TCP Keepalive,需要设置SO_KEEPALIVE套接字选项才能生效。
三个重要的参数
tcp_keepalive_time,在TCP保活打开的情况下,最后一次数据交换到TCP发送第一个保活探测包的间隔,即允许的持续空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2h)。
tcp_keepalive_probes 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)
tcp_keepalive_intvl,在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为75s。
其他编程语言有相应的设置方法,这里只谈linux内核参数的配置。例如C语言中的setsockopt()函数,java的Netty服务器框架中也提供了相关接口。
3. TCP Keepalive作用 ① 探测连接的对方是否存活
在应用交互的过程中,可能存在以下几种情况:
(1)客户端或服务器意外断电,死机,崩溃,重启。
(2)中间网络已经中断,而客户端与服务器并不知道。
利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP连接。
② 防止中间设备因超时删除连接相关的连接表
中间设备如防火墙等,会为经过它的数据报文建立相关的连接信息表,并为其设置一个超时时间的定时器,如果超出预定时间,某连接无任何报文交互的话,
中间设备会将该连接信息从表中删除,在删除后,再有应用报文过来时,中间设备将丢弃该报文,从而导致应用出现异常。
4. 使用方法 结构体使用
struct tcp_keepalive { ULONG onoff ;//是否开启 keepalive ULONG keepalivetime ; //多长时间(ms)没有数据就开始心跳包 ULONG keepaliveinterval ; // 每隔多长时间(ms)发送一个心跳包, }; 这个结构体设置了空闲检测时间,及检测时重复发送的间隔时间。此外, tcp_keepalive 这个结构体中没有对重试次数这个参数的设置,这个参数可以通过注册表来设置,具体位置为:
引言 在现代电子系统中,到处都可以看到数字信号处理( DSP )的应用,从MP3播放器、数码相机到手机。DSP设计人员的工具箱的支柱之一是有限脉冲响应( FIR )滤波器。FIR滤波器越长(有大量的抽头),滤波器的响应越好。然而这里有折衷的情况,由于大量的抽头增加了对逻辑的需求、增加了计算的复杂性,增加了功耗,以及可能引起饱和/溢出。
多相技术可以用于实现滤波器,拥有与传统FIR滤波器可比的结果,而且使用了较少的逻辑、需要较少的计算资源、更低的功耗,并减少了可能的饱和/溢出。可用如今新型的小规模、中档的FPGA,如LATTICEECP3 来实现这些滤波器。
基本概念 进入DSP世界可能会有些令人生畏,因此,让我们首先介绍一些简单的概念。对于数字系统,如音频,视频和无线领域,形成信号的结果是与采样率相关的。举例来说,以48 kHz(即每秒48000个样本)对专业音频信号进行采样。相比之下,消费者的CD播放机则使用44.1 kHz的采样率。
多速率系统 多速率系统使用多个采样速率。在某些情况下,运行于某个速率的系统的一部分需要一个原来以另外某个速率采样的信号(转换专业音频到消费者的CD音频就是一个例子)。在这种情况下,原始信号的速率必须根据需要增加或减少。
或者针对特定的用途,也可能以比实际需要更高的速率对原来的数据进行了采样。因此,降低采样率,然后运行所得到的数据就可以大幅度降低数据吞吐量的要求,降低对存储器的要求,提高处理效率并降低功耗。
向下采样和抽取 让我们首先考虑降低采样率的问题。假设我们有一个信号,原来以我们称之为fHz的某一频率进行采样,如如图1所示。
图1 用f Hz采样率对原始信号采样
现在假设我们要降低采样率至原来频率的1/4。达到此目的一个方法来就是简单地扔掉每四个原始采样中的三个,如图2所示。
图2 用1/4 f Hz采样率得到新的信号
在数字信号处理中, “混叠现象”是指采样时造成不同的连续信号彼此难以区分的情况,它们互相“混叠”。 混叠现象也称为失真,或赝品,即源于采样重构的信号不同于原来的连续信号。
如果我们丢弃了如上文所讨论的一些样本,由此得到的信号会含有混叠现象的赝品。作为一个简单的例子,考虑一个音频信号,可能含有人耳听不见的高频分量的乐曲。如果我们以过低的速率对这个信号采样(当我们丢弃一些样本时,实际上是我们正在做的事情),然后用数字模拟转换器重构这个乐曲,我们可以听到欠采样高频分量的低频混叠。
为了避免这种情况,常见的做法是在丢弃不想要的样本之前,用低通滤波器去除不要的高频,如图3所示。
图3 在丢弃任何样本前对这个信号进行滤波
一般而言, “向下采样”只是指丢弃样本的处理而不执行滤波的操作。相比之下, “抽取”指的是降低采样率的整个过程,即执行滤波操作,然后丢弃样本。实际上, “向下采样” 、“下变频”和“抽取”往往交替使用。
“抽取因子”是指输入采样率与输出采样率之比。通常用字母M来表示。在上面的例子中,输入速率是输出速率的4倍,所以M=4。
向上采样和内插 现在考虑的情况是,我们希望提高采样率。这样做的原因是为了使系统的另一部分与信号运行在更高的采样速率。假设我们从一个信号开始,即原来以我们称为fHz的某个频率进行采样的信号,如图4所示。
图4 采样率为f Hz的原始信号
现在假设我们要增加采样率为原来频率的4倍。我们开始在原始样本之间插入零值样本,以提高采样率,如图5所示。
图5 用零值样本对原始信号进行扩充
但现在有一个问题,因为新的零值样品添加了不要的频谱分量至信号。为了解决此问题,我们对这个新的信号进行了滤波,除去了不想要的分量,产生了更合适的采样值,如图6所示。
图6采样率为4倍 fHz的最终信号
从技术上讲, “ 向上采样 ”只是指插入零值样本的过程。相比之下, “内插”指的是增加采样率的整个过程,即插入零值样本,然后进行滤波操作1。实际上, “向上采样 ”、“向上转换”和“内插”往往交替使用。
“内插因子”指的是输出采样率对输入采样率的比例。这通常用字母L来表示。在上面的例子中,输出速率4倍于输入速率,因此,L = 4 。这个过程的图形说明参见图7。
图7 插入零值样本后对这个信号进行滤波
重采样 前面的讨论中,应该指出的是,抽取和内插因子可以假设为只有整数值。也就是说,我们只可以抽取或内插整数因子,而不是分数因子。例如,如果进行抽取,我们只能丢弃整数的样本(2个中的1个、3个中的1个、3个中的2个、3个4个中的3个,等等)。
假设我们要修改信号的采样率,以便在两个子系统之间进行接口。如果子系统的采样率的比率是一个整数值,那么我们只需要执行抽取或内插。但是,如果采样率的比率是一个分数值,那么我们需要进行抽取和内插的组合,这样的过程称之为重采样。
例如,如果用2.5因子进行重采样,首先我们用插值因子为5 ,然后用抽取因子2产生输出对输入采样率为5/2 = 2.5的采样率,如图8所示。
CSS3 filter滤镜详解 文章目录 CSS3 filter滤镜详解1、filter:blur(px) 高斯模糊2、filter:brightness(%) 亮度调节3、filter:contrast(%) 对比度调节4、 filter:drop-shadow(offset-x offset-y blur spread color) 阴影设置5、filter:grayscale(%) 灰度6、filter:hue-rotate(deg) 应用色旋转7、filter:invert(%) 反转图像输入8、filter:opacity(%) 透明度9、filter:saturate(%) 饱和度10、filter:sepia(%) 褐色转化 因为filter是css3新属性,所以我们在介绍它之前先简单看一下它的兼容性(图片来源于菜鸟教程): 那么我们开始介绍一下filter的几个属性: 1、filter:blur(px) 高斯模糊 这个属性很简单是给图像设置高斯模糊,括号中数值越大那么图像越模糊。
写个例子:
<style type="text/css"> img{ height: 100px; width: 200px; } div{ filter: blur(3px); } </style> <body> <div> <img src="1.jpg" > <span> 这是一张图片 <p>p标签</p> </span> </div> </body> 就如效果图上所显示的,这个属性会影响到这个标签下面所有的子代元素。
2、filter:brightness(%) 亮度调节 说简单点这个属性用于调节图像亮度,值位于0和1之间,默认是1(正常亮度),数值越小亮度越低,到达0时为黑色。
例:
div{ color: red; filter: brightness(50%); } 效果图如下:
html和上面相同,这里不做复制,看效果图就很容易理解,图像很明显变暗,字体颜色设置的红色,这里变成黑红,说明对子代同样有效。
3、filter:contrast(%) 对比度调节 这个属性调节的是图像当中的对比度,取值同brightness,为了我们看的更清晰,我换了一张图片,请看案例:
<style type="text/css"> img{ height: 100px; width: 200px; } img:nth-of-type(2){ filter: contrast(50%) ; } div{ color: red; filter: brightness(50%); } </style> <body> <p>原图</p> <img src="
看到一篇很不错的教程,mark一下
Fiddler抓包教程
Postman的test本质上是JavaScript代码,通过我们编写测试代码,每一个tests返回True,或是False。
每一个tests实际上就是一个测试用例。官方文档给出了很多验证方式,我们通过实例来进行学习
接口返回结果为json
{
"status": 301,
"message": "购买商品库存不足",
"lists": [11]
}
1. 检查response的body中是否包含字符串
tests["测试点"] = responseBody.has("需要查找的字符串");
例:
tests["status code"] = responseBody.has("301");
tests["status是否存在"] = responseBody.has("status");
tests["lists是否存在"] = responseBody.has("lists");
tests["lists值为11"] = responseBody.has("11");
注:
当json中value为integer时,需要查找的值可以不带双引号tests["xxx"]xxx代表的是你测试点的名字,可以是中文tests["xxx"]xxx在一个脚本中如果出现多次,那么只执行第一个,所以尽量不要重复当value等于中文字符串时,这个方法貌似就不怎么好用了,但是我们有别的方法去验证,往下看,如果有读者知道怎么解决这个问题,也可以联系我,教教我 2. 检查Response Body是否等于字符串
tests["测试点"] = responseBody === "Response Body返回的内容";
这个可以用在接口返回内容为纯字符串时,直接检查整个返回结果的正确性,
例子:
接口返回:哈哈
tests["返回为哈哈"] = responseBody === "哈哈";
tests["返回为哈哈"] = responseBody === "哈";
第二个会返回False,必须完全匹配
3. 检查相应时间
tests["Response time 小于200毫秒"] = responseTime > 200;
tests["Response time 大于200毫秒"] = responseTime < 200;
cmd powershell