万用表测量二极管的那一档常被用来测量电路板上的两点之间是否导通,听见滴的一下,就认为是通的。如果测的是电容,那么正常情况下回在表笔刚接上去时滴一下,这是电容在充电;电容充满电后就不会响了。 遇到的一个电路板,直流电源和地之间正常情况下用万用表的这一档量是不会响的,但是其中的一块用万用表的这一档测量电源和地之间会一直响,电路也无法正常工作。这种情况一般都会认为电源和地短路了,那么问题来了,电源对地短路电压一定会被拉低到0V吗?其实不一定,这块电路板的电源此时电压仍然是正常的5V,看起来很奇怪,但是仔细分析一下就知道原因了。直流电源部分是采用的LM2596由24V降压到5V,设计的输出能力是3A。讲故障板的LM2596的输出与后面的负载电路断开后测量负载电路的输入电阻,阻值为26.5欧,计算可以得到电源输出的电流是5伏/26.5欧=0.19安。这个电流是远远小于3A的,因此电源电压不会被拉低。
所有,当遇到电路故障时,测量电源电压发现电压是正确的,不一定代表着没有发生短路。
helm作为k8s的包管理工具,让我们很方便的在k8s上安装部署软件
helm实际上分为几个部分:
1).helm(client)
helm可以把安装chart请求发送给tiller
2).tiller(server)
相当于helm server,部署在集群内部,接收部署请求,调用apiserver完成部署
3).repo
相当于rpm包的yum源,里面存放着一系列包,可以使远程的,也可以是本地目录
4).chart
相当于rpm包,包含服务的一系列配置信息,可以通过远程获取,也可以本地创建
在安装helm之前需要确保本地有安装以及配置好的kubectl,包含kubeconfig文件
1.下载helm
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.6.2-darwin-amd64.tar.gz
2.初始化tiller
helm init
#init将获取本地默认的kubeconfig文件,然后在k8s上面部署 deploy/tiller-deploy
想要测试新功能使用最新版本,只需要后面加上--canary-image参数
除了通过helm init之外tiller也可以使用out-of-cluster的部署方式
./tiller #需要配置kubeconfig
3.更新本地
helm repo update #从repo里面更新chart
4.安装服务
例如安装prometheus
helm install stable/prometheus --name=prometheus
5.查看安装的服务
helm ls
6.删除已经安装的服务
helm delete prometheus
7.更新tiller
export TILLER_TAG=v2.0.0-beta.1 kubectl --namespace=kube-system set image deployments/tiller-deploy tiller=gcr.io/kubernetes-helm/tiller:$TILLER_TAG
deployment "tiller-deploy" image updated
TILLER_TAG=canary 将设置为最新image
具体helm命令的用法请参考官方文档,包含组件的概念上面也已经做了介绍,命令多用help看一下就OK,链接https://docs.helm.sh/helm/helm/
学习的重点就是自己创建想要的chart 内容比较多,不在赘述
添加 repo helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com/
查找helm search incubator
通过Xshell访问和连接linux
Xshell是一款强大的安全终端模拟软件,Xshell模拟了远程主机的操作,其实质就是通过访问和连接到远程主机,在本地实现对远程主机的操作。
一、在电脑桌面或开始-所有程序中找到“Xshell”,这里以Xshell 5为例子,如下图找到Xshell 5的程序图标,双击打开这个程序。
二、Xshell 5打开后如下图所示,会出现一个界面框,这个界面框类似于DOS的界面,需要操控远程的主机,都是通过这个界面进行操作。
三、新建连接
1、点击文件——>新建;或者使用快捷键Alt+N打开新建会话窗口。
端口默认是22,不用修改
2、输入名称、ip后点击确定会弹出一个新的会话窗口
3、点击连接,输入用户名和密码(用你想要连接的linux主机的用户名和密码),勾选记住用户名和密码方便下次连接。
Xshell连接linux服务器就是以上步骤啦,最近刚用这个,总结一下~
最近加入新公司,从公司gitlib上下载了一个工程进行入手。
该项目用到了很多公司自己的依赖,由于我刚来所以很多依赖需要配置maven的setting.xml和工程pom文件下载,在这里为了省事直接拷贝了部门老大的setting文件以及pom。
然后maven build--clean install,进行编译
在编译项目的过程中发现很多公司私有依赖无法下载,原因是无法连接到服务器,请求被拒绝。报错如下
这个问题困扰了我一天多,经过检测各种配置,上网查询各种资料发现最终原因:
访问私服仓库下载依赖需要在maven setting.xml中配置代理以便开启私服访问权限。
这<active>标签内容表示是否启用代理,由于部门老大本地仓库依赖已经齐全,不需要登录私服下载了,他就把代理关了,我直接拷贝他的文件,导致不能开启代理访问私服。
这是细节上的失误
然后下方配置好远程仓库地址等:
配置完成之后保存,再pom文件中配置需要的jar包,这样如果远程仓库里有你需要的依赖,编辑器便会自动下载。
butterknife在github上给出了library中使用的方法 project.build文件添加
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1' } } 主model 添加 apply plugin: 'android-apt' dependencies { //butterknife compile 'com.jakewharton:butterknife:8.8.1' apt 'com.jakewharton:butterknife-compiler:8.8.1' } library添加 apply plugin: 'com.jakewharton.butterknife' apply plugin: 'android-apt' dependencies {
//butterknife compile 'com.jakewharton:butterknife:8.8.1' apt 'com.jakewharton:butterknife-compiler:8.8.1'} 使用R2替代R class ExampleActivity extends Activity { @BindView(R2.id.user) EditText username; @BindView(R2.id.pass) EditText password; ... } 官方及能够收罗到的就是上面这些
那么遇到的是什么坑呢?
新项目中根据功能不同需要分成6个lib, 其中一个主lib(app),一个基础lib(BaseLibrary),
四个功能lib(这里分别叫libraryOne .libraryTwo .libraryThree .libraryFour ),主lib依赖的四个功能lib,四个功能lib都依赖了BaseLibrary 我先在BaseLibrary的build文件添加了
apply plugin: 'com.jakewharton.butterknife' apply plugin: 'android-apt' dependencies { //butterknife compile 'com.
Qt的mouseMoveEvent的一些问题: 在Qt中要捕捉鼠标移动事件需要重写MouseMoveEvent,但是MouseMoveEvent为了不太耗资源在默认状态下是要鼠标按下才能捕捉到。要想鼠标不按下时的移动也能捕捉到,需要setMouseTracking(true)。
bool mouseTracking 这个属性保存的是窗口部件跟踪鼠标是否生效。
如果鼠标跟踪失效(默认),当鼠标被移动的时候只有在至少一个鼠标按键被按下时,这个窗口部件才会接收鼠标移动事件。
如果鼠标跟踪生效,如果没有按键被按下,这个窗口部件也会接收鼠标移动事件。
QWidget中使用是没有问题的,但是,对于QMainWindow即使使用了setMouseTracking(true)依然无法捕捉到鼠标没有按下的移动,只有在鼠标按下是才能捕捉。
解决办法:要先把QMainWindow的CentrolWidget使用setMouseTracking(true)开启移动监视。然后在把QMainWindow的setMouseTracking(true)开启监视。之后就一切正常了。
原因:CentrolWIdget是QMainWindow的子类,你如果在子类上响应鼠标事件,只会触发子类的mouseMoveEvent,根据C++继承和重载的原理,所以子类也要setMouseTracking(true); 所以如果你想响应鼠标事件的控件被某个父控件包含,则该控件及其父控件或容器也需要setMouseTracking(true);
ui->centralWidget->setMouseTracking(true);
setMouseTracking(true); //这是激活整个窗体的鼠标追踪 ui->pBtnMenu->setMouseTracking(true); //进入某个按钮时,鼠标追踪属性失效,因此我们也需要激活该按钮的鼠标追踪功能 ui->pBtnTest->setMouseTracking(true); //然后再实现mouseMoveEvent()事件
void MainWindow::mouseMoveEvent(QMouseEvent *e) { qDebug()<<"mouse move "; e->accept(); if(enterBtn(e->pos(),ui->pBtnMenu)) //Qlab_context->setText("这是第一个按钮"); qDebug()<<"menu"; if(enterBtn(e->pos(),ui->pBtnTest)) qDebug()<<"test"; //Qlab_context->setText("这是第二个按钮"); } //这里我使用另一个函数来完成判断鼠标是否在一个按钮区域内,如果在区域内只返回真,否则返回假 bool MainWindow::enterBtn(QPoint pp, QPushButton *btn) { int height = btn->height(); int width = btn->width(); QPoint btnMinPos = btn->pos(); QPoint btnMaxPos = btn->pos(); btnMaxPos.setX(btn->pos().x()+width); btnMaxPos.setY(btn->pos().y()+height); if(pp.x() >= btnMinPos.x() && pp.y() >= btnMinPos.y() && pp.x() <= btnMaxPos.x() && pp.
本文讲的是Kubernetes 部署失败的 10 个最普遍原因(Part 2)【编者的话】本文作者通过和客户联合开发,从实践中总结了 Kubernetes 部署失败的 10 大普遍原因,本文阐述了剩下的 5 大原因。作者在实践中,尽量把繁琐工作自动化,授人以鱼的同时也授人以渔,小编觉得这是本文最有价值的地方。 【深圳站|3天烧脑式Kubernetes训练营】培训内容包括:Kubernetes概述、架构、日志和监控,部署、自动驾驶、服务发现、网络方案等核心机制分析,进阶篇——Kubernetes调度工作原理、资源管理及源码分析等。 本周早些时候我写了 Kubernetes 部署失败的 前5个 最普遍原因。本文是剩下的几个,包括几个特别令人沮丧的。 6. 资源配额 和资源限额类似,Kubernetes 也允许管理员给每个 namespace 设置 资源配额 。这些配额可以在 Pods,Deployments,PersistentVolumes,CPU,内存等资源上设置软性或者硬性限制。 让我们看看超出资源配额后会发生什么。以下是我们的 deployment 例子: # test-quota.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: gateway-quota spec: template: spec: containers: - name: test-container image: nginx 我们可用 kubectl create -f test-quota.yaml 创建,然后观察我们的 Pods: $ kubectl get pods NAME READY STATUS RESTARTS AGE gateway-quota-551394438-pix5d 1/1 Running 0 16s 看起来很好,现在让我们扩展到 3 个副本: kubectl scale deploy/gateway-quota --replicas=3 ,然后再次观察 Pods: $ kubectl get pods NAME READY STATUS RESTARTS AGE gateway-quota-551394438-pix5d 1/1 Running 0 9m 啊,我们的pod去哪了?让我们观察一下 deployment: $ kubectl describe deploy/gateway-quota Name: gateway-quota Namespace: fail CreationTimestamp: Sat, 11 Feb 2017 16:33:16 -0500 Labels: app=gateway Selector: app=gateway Replicas: 1 updated | 3 total | 1 available | 2 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 1 max unavailable, 1 max surge OldReplicaSets: NewReplicaSet: gateway-quota-551394438 (1/3 replicas created) Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 9m 9m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set gateway-quota-551394438 to 1 5m 5m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set gateway-quota-551394438 to 3 在最后一行,我们可以看到 ReplicaSet 被告知扩展到 3 。我们用 describe 来观察一下这个 ReplicaSet 以了解更多信息: kubectl describe replicaset gateway-quota-551394438 Name: gateway-quota-551394438 Namespace: fail Image(s): nginx Selector: app=gateway,pod-template-hash=551394438 Labels: app=gateway pod-template-hash=551394438 Replicas: 1 current / 3 desired Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed No volumes.
# coding=utf-8 import math import sys from Crypto.PublicKey import RSA keypair = RSA.generate(1024) keypair.p = 275127860351348928173285174381581152299 keypair.q = 319576316814478949870590164193048041239 keypair.e = 65537 keypair.n = keypair.p * keypair.q Qn = long((keypair.p-1) * (keypair.q-1)) i = 1 while (True): x = (Qn * i ) + 1 if (x % keypair.e == 0): keypair.d = x / keypair.e break i += 1 private = open('private.pem','w') private.write(keypair.exportKey()) private.close() openssl rsautl -decrypt -in flag.
首先介绍一下lzop(以下内容摘自lzop官网原文):
lzop is a file compressor which is very similar to gzip. lzop uses the LZO data compression library for compression services, and its main advantages over gzip are much higher compression and decompression speed (at the cost of some compression ratio).
翻译过来就是:
lzop是一款与gzip很类似的文件压缩工具,它使用lzo压缩库来提供服务,与gzip工具相比,它的最大优势就是极快的压缩速度和解压速度(在相同的压缩比例的前提下)。
看到这里,很多人可能会问,与gzip相比,速度快,那到底能快多少呢?笔者专门做了一个测试,使用lzop工具与gzip工具,分别对同一个文件进行压缩,测试压缩的时间、压缩后的文件大小,结果如下:
leon@Ubuntu: time lzop test.data real 0m7.429s user 0m5.260s sys 0m1.100s 485M test.data.lzo leon@Ubuntu: time gzip test.data real 1m9.639s user 1m1.615s sys 0m0.881s 293M test.data.gz 可以看出,在速度上,lzop确实比gzip厉害了不少。难怪linux内核编译完成后,都要使用这个工具来进行压缩。
解决方案:修改项目属性 右击项目 --> "属性”
1. “C/C++” --> "常规” -->”调试信息格式” 设置为 “C7 兼容(/Z7)”
2. “C/C++” --> "代码生成” -->”启用字符串池” 设置为 “是(/GF)”
3. “链接器” --> "调试” -->”生成调试信息” 设置为 “是(/DEBUG)” 最后就可以编译通过了。
编写数据库连接池 编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password) 实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。
Collection保证将自己返回到LinkedList中是此处编程的难点
数据库连接池:第一次访问比较慢,后面就好了
class B extends Thread {//继承线程
public void run(){
while(true){
// 把用完的Connection 在加入到连接池中
for(Connection c : MyConnPool.t){ if(c.isClose()){
// 状态进行改变
MyConnPool.list.add(c);
}
}
int size = MyConnPool.list.size(); if(size < 20){
int x = 50 - size;
for(int i = 0 ; i < x ; i++){
MyConnPool.list.add(DBUtil.getConnection());
}
}
if(size > 150){
int y = size - 120;
代码或者内容有任何问题,请留言,不胜感激 Snipaste Snipaste=Snip + Paste =截图 + 贴图。
开发了三年的截图工具,但不只是截图 它将 截图 与 贴图 整合在了一起,你可以将刚刚的截图随意放置在桌面上而不需要打开图片浏览器
Snipaste 的功能有点多,首先这是一款截图工具,能够进行各种简单、高级的截图,并可以编辑截图。其次可以将截图、文字、图片、网页、代码等剪贴板内的东西以图片的形式贴在屏幕上。
我们还是从作者眼花缭乱的演示动画中来看看具体怎么玩吧:
有人问博主这些酷炫gif怎么录制的,这里有工具推荐:【录教程必备】推荐几款屏幕录制工具(可录制GIF)
截图 贴图,即是使图片成为一个窗口并置顶显示:
贴图窗口可以旋转、缩放、半透明、鼠标穿透:
以上功能也许并不新奇,但Snipaste还有很多特别的地方。
精确控制截图范围:
截图记录回放:
是的,不只是回放全屏的截图,之前截图中的画图过程也能重现。
取色(不只是显示取色框,能复制当前像素点的 RGB 值):
取色之后,还能把颜色贴出来(同时可得到各种颜色格式的转换):
普通的纯文本,可以转换成图片:
HTML 格式的文本,也可以:
配合支持 HTML 格式复制的代码编辑器食用更佳:
图片编辑功能不止在截图时可用,贴图窗口亦可:
有马克笔、马赛克、高斯模糊、橡皮擦功能:
文字可旋转:
当然,截图和贴图还可以结合得更紧密。
截图后复制到剪贴板里的图片,贴出来可以就在原来截图的位置:
也可以一步到位:
还有炫酷一点的,在贴图窗口播放 GIF :
不喜欢默认的蓝色界面也没关系,主题色任你选,通知栏图标也随你定制:
换个截图遮罩的颜色:
软件下载地址:
https://www.snipaste.com/
1.贪婪算法的基本思想
求解一个最优化问题包含一系列的步骤,每一步都有一组选择,做出在当前看来最好的选择,希望通过做出局部优化选择来达到全局优化选择,这就是贪婪算法的基本思想。
贪婪算法能否产生优化解具有两个条件:Greedy选择性和优化子结构。
Greedy选择性:若一个优化问题的全局优化解可以通过局部优化地选择得到,则该问题具有Greedy选择性。
优化子结构:若一个优化问题的优化解包含他的子问题的优化解,则称其具有优化子结构。
2.求解最大相容集合
2.1 问题定义
输入:S = { 1, 2, ... , n }, F = { [si, fi] }, n ≥ i ≥ 1;
输出:S的最大相容集合;
题目大意就是已知n个活动的开始和结束时间,求出在时间不冲突的情况下,最多可举行活动的数量。
2.2 算法分析
为了选择最多相容的活动,每次选择fi最小的活动,这样我们就能选择更多的活动。该问题是否可以使用贪婪算法需要我们讨论此问题是否满足Greedy选择性和优化子结构,分析如下图所示。
理论依据有了,就可以大胆编写代码来实现了。
3.代码实现
import numpy as np # 需要安排的活动集合 activity_set = np.array([[2,3],[4,5],[6,8],[2,5],[3,6],[5,6]]) # 活动数目 num = len(activity_set) # 定义一个集合存放最终结果 result_set = set() # 将活动按照结束时间递增排序 activity_set = activity_set[activity_set[:,1].argsort()] # 把第一个活动放入结果集合 result_set.add((activity_set[0][0],activity_set[0][1])) # 从第二个活动开始遍历,若活动的开始时间在前一个活动结束时间之后,那此活动相容, #并且该活动也变成衡量其他活动是否相容的对象 j = 0 i = 1 while i < num: if activity_set[i][0] >= activity_set[j][1]: result_set.
本教程相关资源包下载地址
PLSQL安装 第①步
第②步
第③步:若要汉化的话,不要安装到Program Files (x86)
第④步
第⑤步
第⑥步
PLSQL破解 第一步:双击资源包中的【PLSQL Developer10.0.3.1701_keygen.exe】
第二步:打开破解PLSQL的相关密钥,只需对应填入即可完成破解。
PLSQL汉化 第一步
双击资源包中的【PLSQL Developer10.0.3.1701_CHS.exe】,打开自动匹配到了安装目录,如下图所示
第二步
第三步
第四步
第五步:登录之后可以看见汉化成功
最近在编写一个项目的代码时,需要在宏定义中连接多个字符串,具体来说就是,先定义一个软件版本号,然后再定义一个硬件版本号, 然后再将他们拼合起来生成一个综合版本号。这些动作我都希望在宏定义中直接完成,提供代码的可读性和可移植性。
类似于下面这样的:
#define SOFTWARE_VERSION "Software:V1.00" #define HARDWARE_VERSION "Hardware:V1.00" #define SYSTEM_VERSION ???? 如上,为了把SOFTWARE_VERSION和HARDWARE_VERSION连接起来,一般的程序猿应该都了解,其实办法很简单,就是使用“#”和“##”这两个特殊的宏转义字符。下面就对他们进行一下简单的介绍:
1、#:字符串 “#”的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量,通过替换后在其左右各加上一个双引号。比如下面代码中的宏:
#define WARN_IF(EXP) \ do { \ if (EXP) { \ fprintf(stderr, "Warning: " #EXP "/n"); \ } \ } while(0); 那么实际使用中会出现下面所示的替换过程:
WARN_IF (divider == 0); 被替换为
do { if (divider == 0) { fprintf(stderr, "Warning" "divider == 0" "/n"); } } while(0); 2、##:连接两个参数
“##”被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那么下面的代码就非常实用:
struct command { char * name; void (*function) (void); }; #define COMMAND(NAME) {NAME, NAME##_command} // 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了: struct command commands[] = { COMMAND(quit), COMMAND(help), .
检测鼠标单击 要想在OpenGL中处理鼠标事件非常的方便,GLUT已经为我们的注册好了函数,只要我们提供一个方法。使用函数glutMouseFunc,就可以帮我们注册我们的函数,这样当发生鼠标事件时就会自动调用我们的方法。
函数的原型是:
void glutMouseFunc(void(*func)(int button,int state,int x,int y)); 参数: func:处理鼠标click事件的函数的函数名。 从上面可以看到,处理鼠标单击事件的函数,一定有4个参数。第一个参数表明哪个鼠标键被按下或松开,这个变量可以是下面的三个值中的一个:
GLUT_LEFT_BUTTON
GLUT_MIDDLE_BUTTON
GLUT_RIGHT_BUTTON
第二个参数表明,函数被调用发生时,鼠标的状态,也就是是被按下,或松开,可能取值如下:
GLUT_DOWN
GLUT_UP
当函数被调用时,state的值是GLUT_DOWN,那么程序可能会假定将会有个GLUT_UP事件,甚至鼠标移动到窗口外面,也如此。然而,如果程序调用glutMouseFunc传递NULL作为参数,那么GLUT将不会改变鼠标的状态。
剩下的两个参数(x,y)提供了鼠标当前的窗口坐标(以左上角为原点)。
检测动作 GLUT提供鼠标动作检测能力。有两种GLUT处理的motion:active motion和passive motion。Active motion是指鼠标移动并且有一个鼠标键被按下。Passive motion是指当鼠标移动时,并有没鼠标键按下。如果一个程序正在追踪鼠标,那么鼠标移动期间,每一帧将产生一个结果。
和以前一样,你必须注册将处理鼠标事件的函数(定义函数)。GLUT让我们可以指定两个不同的函数,一个追踪passive motion,另一个追踪active motion。
它们的函数原型如下:
void glutMotionFunc(void(*func)(int x,int y)); void glutPassiveMotionFunc(void (*func)(int x,int y)); 参数: Func:处理各自类型motion的函数名。 处理motion的参数函数的参数(x,y)是鼠标在窗口的坐标。以左上角为原点。
检测鼠标进入或离开窗口 GLUT还能检测鼠标鼠标离开,进入窗口区域。一个回调函数可以被定义去处理这两个事件。GLUT里,调用这个函数的是glutEntryFunc,函数原型如下:
void glutEntryFunc(void(*func)(int state)); 参数: Func:处理这些事件的函数名。 上面函数的参数中,state有两个值表明是离开还是进入窗口:
GLUT_LEFT
GLUT_ENTERED
应用 现在想实现的功能就是使用鼠标拖动,来转动场景中的物体,当鼠标左键按下,并且上下左右滑动时,场景中的物体相应的会随之上下左右旋转,当鼠标右键按下,并且上下滑动时,场景中的物体相应的会随之移入或是移出屏幕。
首先,定义几个全局变量:
bool mouseLeftDown; bool mouseRightDown; float mouseX, mouseY; float cameraDistance; float cameraAngleX; float cameraAngleY; 其中,mouseLeftDown和 mouseRightDown变量标志鼠标左右键按下与否,mouseX和mouseY变量标志鼠标滑动时,前一刻的鼠标所在位置,cameraDistance这个变量根据鼠标右键按下并上下滑动来控制场景中物体移入屏幕的远近。cameraAngleX和cameraAngleY变量根据鼠标左键按下并滑动来控制场景中物体的旋转。
分析需要的服务 1.DHCP服务 原因:全自动安装的需要从网络获取所有需要的所以需要搭建一个DHCP服务 2.tftp 原因:dhcp可以指向引导文件位置 默认是 tftp服务 配置选项:filename: 指明引导文件名称 next-server:提供引导文件的服务器IP地址 3.httpd 原因:安装包源位置;也可以使用ftp 4.ftp 原因:Kickstart位置;也可以使用httpd 分析安装时的流程 1.dhcp配置IP --->指向PXE引导文件所在 2.PXE进行引导: 4.需要vmlinux和initrd等文件启动内核; 3.Kickstart安装策略文件 4.安装包文件 一、相关程序包的安装和开启 1.yum 安装程序包 yum -y install dhcp tftp-server httpd vsftpd 2.开启相应服务 #设为开机启动 centos7: systemctl enable dhcpd.service tftp.socket httpd.service vsftpd.service centos6: chkconfig dhcpd tftp httpd vsftpd on 开启相应服务 #dhcp服务不能正常开启;因为配置文件是空的 centos7: systemctl start dhcpd.service tftp.socket httpd.service vsftpd.service centos6: service dhcpd start service httpd start service xinet start service vsftpd start 3.
SDK包现在支付的官网上就有,具体使用如下:
正式使用之前,需要给对方的数据有:应用编号、应用秘钥、公钥这三个,有专门的人进行生成,具体规则请询问现在支付的工作人员
//正式接口
String reqUrl = "https://bc-pay.ipaynow.cn/gateway"; //测试接口
String reqUrl = "http://bc-test.ipaynow.cn/gateway";
//私钥,不能给工作人员
String key = "";
//商户号,现在支付的工作人员会给
String merchantId = "";
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
String date = df.format(new Date());// 获取当前系统时间
IpayNowClient ipayNowClient = new DefaultIpayNowClient(reqUrl, key,merchantId);
AgentPayReqDto agentPayReqDto = new AgentPayReqDto();
agentPayReqDto.setMhtOrderAmt();//接收Long型的代付款,单位:分
agentPayReqDto.setAppId("");//应用编号,现在支付的工作人员会给
agentPayReqDto.setAgentPayMemo("");//用途,可以随便填
agentPayReqDto.setMhtReqTime(date);//请求时间
agentPayReqDto.setMhtOrderNo();//请求订单号,具体规则可以自己定,我的是年月日时分秒+随机6位整数
agentPayReqDto.setAccType("0");//0对私 1对公
agentPayReqDto.setPayeeName();//开卡人姓名
agentPayReqDto.setPayeeCardNo();//卡号
agentPayReqDto.setPayeeCardUnionNo("");//入账账户联行号,没有可不填
agentPayReqDto.setNotifyUrl();//商户通知地址,具体用法后面会将具体说明
TransRespDto respBaseDto = ipayNowClient.agentPay(agentPayReqDto);//发送请求报文并得到返回报文
私钥公钥的生成方法:
ECKey key = new ECKey();
System.out.println("PrivKey\t: " + Hex.toHexString(key.getPrivKeyBytes()));
在学习 Java反射 的过程中,注意到Class类 有 以下四个方法:
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException public Method[] getMethods() throws SecurityException public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException public Method[] getDeclaredMethods() throws SecurityException 通过查阅资料学习了各个方法的作用、及相互之间的区别,在此进行整理记录,如有错误之处,还请指正。
getMethod public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException 返回一个 Method 对象,它反映当前 Class 对象所表示的类或接口中指定name的公共(public)成员方法(包括父类继承的、接口实现的、父接口继承的)。 name 参数用于指定所反映的方法的名称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。
BUT:如果 name 是 “< init >;” 或 “< clinit >”,则将引发 NoSuchMethodException。(< init >和< clinit >的区别)
下载地址:https://github.com/App-vNext/Polly
该库实现了七种恢复策略。
重试策略(Retry) 重试策略针对的前置条件是短暂的故障延迟且在短暂的延迟之后能够自我纠正。允许我们做的是能够自动配置重试机制。
断路器(Circuit-breaker) 断路器策略针对的前置条件是当系统繁忙时,快速响应失败总比让用户一直等待更好。保护系统故障免受过载,Polly可以帮其恢复。
超时(Timeout) 超时策略针对的前置条件是超过一定的等待时间,想要得到成功的结果是不可能的,保证调用者不必等待超时。
隔板隔离(Bulkhead Isolation) 隔板隔离针对的前置条件是当进程出现故障时,多个失败一直在主机中对资源(例如线程/ CPU)一直占用。下游系统故障也可能导致上游失败。这两个风险都将造成严重的后果。都说一粒老鼠子屎搅浑一锅粥,而Polly则将受管制的操作限制在固定的资源池中,免其他资源受其影响。
缓存(Cache) 缓存策略针对的前置条件是数据不会很频繁的进行更新,为了避免系统过载,首次加载数据时将响应数据进行缓存,如果缓存中存在则直接从缓存中读取。
回退(Fallback) 操作仍然会失败,也就是说当发生这样的事情时我们打算做什么。也就是说定义失败返回操作。
策略包装(PolicyWrap) 策略包装针对的前置条件是不同的故障需要不同的策略,也就意味着弹性灵活使用组合。
原文地址:http://www.cnblogs.com/CreateMyself/p/7589397.html
学习Spring等框架时,接触到反射这一机制,查阅资料发现Java的反射机制对于各大主流Java框架有着举足轻重的地位。在此特地记录自己对于reflect相关知识的些许理解。如有错误之处,还请指正。
1、简单理解什么是反射 反射机制可以让程序在运行时 加载、探知、使用一个在编译期完全未知的类,通过反射可以让我们在只知道类名的情况下,获取一个Java类的内部成员变量、成员方法。并对其成员进行操作(方法调用等)。换句话说,程序可以加载一个运行时才得知名称的Java类,获悉其完整构造(但不包括方法(method)的完整定义),并生成其对象实例、或对其fields(属性)设值、或调用其methods。
Java能够实现反射机制,ClassLoader、Class、Method、Field 、Constructor等 这几个类是至关重要的。
2、ClassLoader类 类加载器(ClassLoader的实现)是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“.calss文件”。 当一个Java类被加载,或当加载器(classloader)的defineClass()被JVM调用,JVM 便实例化一个Class 对象。这个Class对象是被加载类的字节码对象(内存中一个类的字节码只有一份,这个Class对象也就只有一个)。 但是,数组的 Class 对象并不是由类加载器创建的,而是由 JRE(Java 运行时)根据需要自动创建。 每个 Class 对象都包含了对于定义它的 ClassLoader 的引用。通过调用Class 对象的 getClassLoader()方法,可以获取该ClassLoader对象。
3、Class 类 Class类是一个特殊的类,他继承自Object,并且没有public的构造函数 。 Class 类的对象(字节码对象)表示正在运行的 Java 应用程序中的类和接口。(枚举是一种类,注解是一种接口)。不止如此,JVM还预先提供了9种Class对象,分别是: byte、short、int、long、float、double、boolean、char、void (8种基本数据类型和关键字void)的Class对象。
3.1、获取Class对象
3.1.1、获取类的Class对象,有三种方式: Student stu = new Student(); Class c1 = Student.class; Class c2 = stu.getClass(); Class c3 = Class.forName("com.company.demo.Student"); //注意:同一个类 在JVM中只有一份字节码,所以c1、c2、c3 指向同一个Class对象 c1方式: 每个类都有一个class属性,该属性包含了指向该类Class对象的引用c2方式:通过类的实例,来获取类的Class对象c3方式:Class.forName(String className)方法,参数类名必须是完整的类名(包名+类名)。该方式下:当指定类的字节码已经加载到JVM中时,获取到字节码,并返回该类的Class对象;否则 加载指定的类,并返回字节码对象。 3.1.2、对于基本数据类型、void 的 Class对象的获取方式:
byte.class short.class int.class long.class float.class double.
String patten = "^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,}$"; String password1 = "fukang"; // false String password2 = "Fukanggggg"; // false String password3 = "fukang123"; // false String password4 = "Fukang123"; // true String password5 = "##Fuk%%"; // false String password6 = "###fukang%%"; // false String password7 = "66FFFFFFFFFF";// false System.out.println(password1.matches(patten)); System.out.println(password2.matches(patten)); System.out.println(password3.matches(patten)); System.out.println(password4.matches(patten)); System.out.println(password5.matches(patten)); System.out.println(password6.matches(patten)); System.out.println(password7.matches(patten));
docker pull gitlab/gitlab-ce 该镜像是英文的需要自己去汉化,这里介绍的是已经汉化好的docker pull beginor/gitlab-ce:你喜欢的版本
启动脚本
docker run \ --detach \ --publish 8443:443 \ --publish 8080:80 \ --name gitlab \ --restart unless-stopped \ --volume /mnt/sda1/gitlab/etc:/etc/gitlab \ --volume /mnt/sda1/gitlab/log:/var/log/gitlab \ --volume /mnt/sda1/gitlab/data:/var/opt/gitlab \ beginor/gitlab-ce:9.5.5-ce.0 需要提前准备volume 以下目录可以自定义 /在升级的时候建议备份一下
mkdir -p /mnt/sda1/gitlab/etc mkdir -p /mnt/sda1/gitlab/log mkdir -p /mnt/sda1/gitlab/data 大版本升级(例如从 8.7.x 升级到 8.8.x)用上面的操作有可能会出现错误, 如果出现错误可以尝试登录到容器内部, 可以用 docker exec , 也可以用 ssh , 依次执行下面的命令:
gitlab-ctl reconfigure gitlab-ctl restart
例如:http://localhost:8080/goods/BookServlet?method=findByCategory&cid=100&pageNow=3 request.getRequestURI(); 得到/goods/BookServlet request.getQueryString();得到method=findByCategory&cid=100&pageNow=3 request.getServletPath();得到BookServlet request.getServerName();得到服务器地址 request.getRequestURL();得到http://localhost:8080/goods/BookServlet request.getHeader("referer");得到http://localhost:8080/goods/CategoryServlet;jsessionid=6E526D759A3C0ACA1B2478F345C7E303?method=findAll
转发(服务器端跳转)
1.<jsp:forward>
2.request.getRequestDispatcher("main.jsp").forward(request,response);
重定向(客户端跳转):
<%
response.sendRedirect("main.jsp");
%>
区别:
两者都实现了页面跳转,需要传递数据的时候用转发,否则用重定向
1.转发是在服务器端完成,因此称为服务器端跳转
重定向是在客户端完成,因此称为客户端跳转
2.转发后,地址栏不会改变
重定向后,地址栏会变
3,转发只能在同一个web项目范围内进行
重定向可以在不同的web项目内进行
背景: 求解 z=σ(z) 的梯度 由于 sigmoid(x)=11+e−x 在python中利用numpy模块实现:
# GRADED FUNCTION: sigmoid import numpy as np # this means you can access numpy functions by writing np.function() instead of numpy.function() def sigmoid(x): """ Compute the sigmoid of x Arguments: x -- A scalar or numpy array of any size Return: s -- sigmoid(x) """ ### START CODE HERE ### (≈ 1 line of code) s = None s = 1/(1+np.exp(-x)) ### END CODE HERE ### return s 求对应的导数 sigmoid_derivative(x)=σ′(x)=σ(x)(1−σ(x))(1) 那这个是怎么推导的呢? σ(x)=11+e−x 另临时变量 t=1+e−x ,通过复合函数的求导法则,所以 σ′(x)=(t−1)′⋅t′=−t−2⋅(−e−x)=1(1+e−x)2⋅e−x=11+e−x(e−x1+e−x)=11+e−x(1+e−x−11+e−x)=11+e−x(1−11+e−x)=σ(x)⋅(1−σ(x)) 得证! python实现 def sigmoid_derivative(x): "
// // Created by lizhiduo on 17-9-23. // #include "com_example_lizhiduo_jni_JNI.h" JNIEXPORT jstring JNICALL Java_com_example_lizhiduo_jni_JNI_sayHello (JNIEnv * env, jclass obj){ return env->NewStringUTF("hello from jni"); } JNIEXPORT void JNICALL Java_com_example_lizhiduo_jni_JNI_callBackHelloFromJava (JNIEnv * env, jclass obj){ //1 得到字节码 jclass jclass1 = env->FindClass( "com/example/lizhiduo/jni/JNI"); //jclass jclass1 = env->GetObjectClass( obj); //2 得到方法 方法签名 ()V jmethodID jmethodID1 = env->GetMethodID( jclass1, "sayHelloFromJava", "()V"); //3 实例化对象 jobject jobject1 = env->AllocObject( jclass1); //4 调用方法 env->CallVoidMethod( jobject1, jmethodID1); } JNIEXPORT jint JNICALL Java_com_example_lizhiduo_jni_JNI_callBackAdd (JNIEnv *env, jclass obj, jint a, jint b){ //1 得到字节码 jclass jclass1 = env->FindClass( "
前几天在Vue项目中使用到了echarts,所以写个简单的小Demo
想要实现的效果 数据动态生成(实时改变) 时间点1 时间点2 代码 文件(Echarts.vue) <template> <div class="echarts"> <i-echarts :option="options"></i-echarts> </div> </template> <script> import IEcharts from 'vue-echarts-v3/src/full.vue' export default { name: 'table', components: { IEcharts }, data() { return { options: { title: { text: 'Timesky' }, tooltip: { trigger: 'item' }, legend: { data: ['销量'] }, xAxis: { name: '商品', nameLocation: 'start', nameGap: '50', boundaryGap: true, data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'] }, // yAxis: {}, 纵轴自适应 yAxis: { //纵轴标尺固定 type: 'value', scale: true, name: '销量', max: 100, min: 0, boundaryGap: [0.
用eclipse的时候把项目加进Servers启动服务器,不会报错,也可以成功启动,但是打开Tomcat服务器webapps目录找不到加进去的项目,这就可能是eclipse配置问题了:
方法:
重新配置下tomcat服务器:
在eclipse中的server页面,双击tomcat服务,会看到如图所示的配置页面:
可以看到红圈中选择的是 Use workspace metadata(does not modify Tomcat installion)
如果该tomcat中部署了项目的话,这红圈中的选项会灰掉不能修改,要修改必须得先把tomcat中的部署的服务都移除。
如图:
通过右键单击tomcat服务器选择 Add and Remove,在弹出的对话框中移除已部署的项目。移除完确定后,将看到上面的选项面板部分可编辑了。
选择Use tomcat installation(Task control of Tomcat installation) 即选择tomcat的安装目录来作为项目的发布目录。
然后,下来四行,看到"Deploy Path"了没?它后面的值默认是"wtpwebapps",把它改成"webapps",也就是tomcat
中发布项目所在的文件夹名字。 修改后关掉该页面,保存配置。这样就将项目部署到了tomcat安装目录下的webapp 重启tomcat服务器,访问http://localhost:8080则能正常访问了,自己部署的项目也能正常访问了。
一、SSL/TLS SSL又被称之为安全的套接字层(Secure Socket Layer),一种安全的加密协议,是嵌套在应用层与传输层之间的一个半层。用户在浏览器中需要加密的时候输入https,则数据通过该半层进行加密。不需要加密的时候则输入http,数据不经过该半层。
TLS又被称之为传输层安全协议(Transport Layer Security),其前身是SSL。IETF(互联网工程任务小组)将SSL标准化,进行改进,在加密过程中使用了更加强大复杂的算法,修复了SSL的很多漏洞,并命名为TLS。
二、OpenSSL OpenSSL是基于SSL/TLS加密协议的一种开源实现,实现了基本的加密功能。
OpenSSL由三个组件组成:
openssl:多用途的命令行工具libcrypto:公共加密库libssl:库,实现了ssl及tls openssl的子命令有很多,这里简要的说明一下openssl的用法
对称加密:
# openssl enc -e -des3 -a -salt -in ipset.sh -out ipset enc:表示加密 -e:加密算法 -des3:des3加密算法 -a:基于文本进行编码 -salt:加盐 -in:加密的文件 -put:输出的文件 # openssl enc -d -des3 -a -salt -in ipest -out ipset.sh -d:表示解密 使用openssl –help查看加密算法
单向加密:
使用单向加密用来加密文件的特征码,加密算法有:
md5sum, sha1sum, sha224sum, sha256sum, sha512sum, openssl dgst
# openssl dgst -md5 ipset -md5:表示使用md5算法进行加密 使用openssl给密码加密:
# openssl passwd -1 -salt 123456 -1:表示使用md5进行加密 -salt 123456:表示加盐,如果改变盐的值则会对加过造成巨大改变 生成随机数:
‘utf-8’ codec can’t decode byte 0xff in position 0: invalid start byte 觉得有用的话,欢迎一起讨论相互学习~ 今天使用语句
image_raw_data_jpg = tf.gfile.FastGFile('../test_images/test_1.jpg', 'r').read() 读取图片文件的时候遇到了以下问题:
'utf-8' codec can't decode byte 0xff in position 0: invalid start byte 原因: 0x92 即 10010010,UTF8 中编码一个字符的第一个字节(start byte)只可能是 0xxxxxxx、110xxxxx、1110xxx、11110xxx……而后面的字节只可能是 10xxxxxx。也就是说 0x92 只能作为后面的字节,却出现在了第一个字节的位置。
出现这种问题绝大部分情况是因为文件不是 UTF8 编码的(例如,可能是 GBK 编码的),而系统默认采用 UTF8 解码。解决方法是改为对应的解码方式。
极少数情况是因为文件损坏了或者和一部分非 UTF8 编码混在一起,可以修复文件或采用 replace 等方式解码。
解决方案 将’r’改为’rb’的形式,即:
image_raw_data_jpg = tf.gfile.FastGFile('../test_images/test_1.jpg', 'rb').read() 参考文献: https://segmentfault.com/q/1010000004268196
最近在学习,dubbo。看了dubbo的xml配置后,表示好麻烦,服务端和消费端都要写一遍:类似下面的配置:(我用的spring 4.1.3.RELEASE 和dubbo2.5.3)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 服务提供方 --> <!-- 第一:服务提供方启名称 计算机要用名称 --> <dubbo:application name="service-product"/> <!-- 第二:到注册中心注册地址 连接zookeeper --> <!-- <dubbo:registry address="192.168.200.128:2181,192.168.200.129:2181,192.168.200.130:2181" protocol="zookeeper"/> --> <!--<dubbo:registry address="192.168.72.130:2181" protocol="zookeeper"/>--> <!--开发时优化,让消费者直接连接服务提供方。 生产环境要放开这段用上面的--> <dubbo:registry address="N/A"/> <!-- 第三:自定义端口号 默认端口是20880--> <dubbo:protocol host="127.0.0.1" port="20880"/> <!-- 第四:指定暴露的接口 --> <dubbo:service interface="cn.mytest.core.service.TestTbService" ref="testTbService"/> </beans> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 服务消息方 --> <!-- 第一:服务消费方启名称 计算机要用名称 --> <dubbo:application name="mytest-console"/> <!-- 第二:到注册中心注册地址 连接zookeeper --> <!
字符串可能是任何程序语言中都会出现的对象,java中创建并初始化一个String对象,最常见的方式有两种: String str=new String(“XXX”); String str=”XXX”; 二者看似相同,其实有很大的差别。 前者是java中标准的对象创建方式,其创建的对象将直接放置到堆中,每调用一次就会创建一个新的对象;后者则会在栈中创建一个对象引用变量str,然后 查看字符串池中是否存在”XXX”,如果没有,则将”XXX”存放字符串池,并令引用变量str指向它;如果已经有”XXX”,则直接令str指向它。这样充分利用 了栈的数据共享优点,当然也可能是一个陷阱,对象很有可能没有创建,只不过指向一个先前已经创建的对象;而new()方法则能保证每次都创建一个新的对 象。 下述代码展示了二者的不同: Java代码 1. public class Main { 2. 3. /** *//** 4. * @param args the command line arguments 5. */ 6. public static void main(String[] args) { 7. String strA = “abc”; 8. String strB = “abc”; 9. String strAA = new String(“abc”); 10. String strBB = new String(“abc”); 11. System.out.println(strA == strB); 12. System.out.println(strAA == strBB); 13.
NO.1 单一职责原则(Single Responsibility Principle) 1. 定义:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。 2. 单一职责是实现高内聚,低耦合的指导方针
NO.2 开闭原则(Open-Closed Principle) 1. 定义:一个软件实体应该对扩展开放,对修改关闭。即一个模块在不修改源代码的前提下被扩展。 2. 开闭原则是可复用设计的第一块基石,是最重要的面向对象设计原则。
NO.3 里氏代换原则(Liskov Substitution Principle) 1. 定义:所有能够引用基类的地方必须能透明的使用其子类对象 2. 里氏代换是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象替换父类对象 3. 里氏代换注意问题: ● 子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法 ● 尽量把父类设计为抽象类或接口,在声明实例时,以抽象层进行声明,运行时由子类负责实例化 ● 在编译阶段,java编译器会检查程序是否符合里氏代换原则,这只是一个与实现无关,纯语法意义上的检查
NO.4 依赖倒转原则(Dependence Inversion Principle) 1. 定义:对接口编程,而非对实现编程 2. 代码要依赖于抽象层,而不要依赖于具体的实现层,针对接口、抽象类进行编程,而不要针对具体的实现类编程,将具体类放在配置文件中 3. 类之间的耦合关系: ● 零耦合关系:两个类之间没有任何耦合关系 ● 具体耦合关系:两个具体类之间,由一个类对另一个具体类实例的直接引用 ● 抽象耦合关系:两个类之间发生耦合关系,但至少有一个类为抽象的
NO.5 接口隔离原则(Interface Segregation Principle) 1. 定义:客户端不应依赖那些他不需要的接口,即:一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。 2. 使用多个专门的接口,而不使用单一的总接口
NO.6 合成复用原则(Composite Reuse Principle) 1. 定义:尽量使用对象组合。而不是继承来达到复用目的 2. 通过继承来实现复用称为“白箱”复用 通过组合/聚合关系复用称为“黑箱”复用
NO.7 迪米特法则(Low of Demeter) 1. 多种定义: ● 不要和“陌生人”说话 ● 只与你的直接朋友通信 ● 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位 2.
Qt的Xml操作QDomDocument Qt对于Xml的支持是很好的,一些我们需要的操作应有尽有,下面简单介绍一下怎样使用。主要有以下几点使用:
写xml到文件读xml添加节点到xml删除xml中某节点信息修改xml中某节点信息 准备工作 .pro加入QT += xml需要include QDomDocument QTextStream QFile三个头文件 WriteXml 直接上代码
void writeXml() { QDomDocument doc; QDomProcessingInstruction xmlInstruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\""); QDomComment comment = doc.createComment(QString::fromLocal8Bit("离开是为了更好的归来")); QDomProcessingInstruction styleInstruction = doc.createProcessingInstruction("xml-stylesheet", "type=\"text/css\" href=\"style.css\""); doc.appendChild(xmlInstruction); // 开始文档(XML 声明) doc.appendChild(comment); // 注释 doc.appendChild(styleInstruction); // 处理指令 // 根元素 <Blogs> QDomElement root = doc.createElement("Books"); root.setAttribute("Version", "1.0"); // 属性 doc.appendChild(root); // 元素 <Blog> QDomElement child = doc.createElement("Book"); root.appendChild(child); // 元素 <作者>、<时间>、<个人说明> QDomElement author = doc.
两种方式 执行linux命令,将执行命令返回的值,赋给shell中的变量第一种 data=`command` 第二种 data=$(command) 其他 开头 必须以 #!/bin/sh 开头#![Args],Args表示执行该文件的程序chmod +x filename,使可通过 ./filename 执行 注释 # 注释内容 变量 所有变量有字符串组成不需要声明赋值 var1="hello" 打印 echo "A is" echo $a 花括号{} num = 2 echo "this is ${num}nd" Linux系列 Linux下autotools的使用线程系统调用阻塞是否导致进程阻塞的问题Ubuntu查看线程Linux打开当前路径图形化窗口valgrind小例Linux文件锁flockWindows远程桌面连接ubuntuLinux升级HeaderLinux下动态库so查找与函数列表查找项目中包含某关键字的文件Linux清屏命令Ubuntu unity7 stop/killedLinux复制远程文件或目录Ubuntu 使用telnet
刚打开电脑的时候还是正常的,当打开浏览器连接网络的时候,屏幕突然有一半变了颜色,并且在一直抖动,起初还以为是浏览器的问题,结果关闭浏览器之后,也还是会有这样的问题,重启了几遍之后也依然是没有作用。
后来了解到,发生这种问题的原因可能是:1)显卡坏了 2)屏线坏了
如果不是专业人士,还是送去专业维修地点维修比较妥当。
原文地址:http://www.cppblog.com/xunbu7/archive/2012/12/03/195930.html
strText.GetLength(); 返回的是字符个数(不包括结尾的空字符), 要视乎你编译是UNICODE还是MBCS(多字符集)。CString 本身就是TCHAR的封装,所以你定义了UNICODE那么他就占两个字节,否则就是一个字节。 我通常使用下面代码来取得字符串的字节数:
int nBytes = (csSomeCString.GetLength() + 1) * sizeof(TCHAR);
注意:这字节数的长度还包括文件结束符.
关于getlength()获取字符串的长度问题 下面几种是不一样的
只是一些实验,每个人都可以自己做。尽管简单,但是不做实验还真是无法知道,从而实际应用的时候因此出错也说不定哦。现把我的实验结果罗列在此,仅供其他网友参考:
在MBCS设置下:
1. 以'\0'为结尾。故_T("ab\0cd\n")长度为2,_T("abcd\n")长度为5。
2. 一个英文字母占1字节,一个其他文字占2字节,故_T("abcd汉字")长度为8。_T("セβΔ尒ab汉字cd")长度为16。
UNICODE设置下:
1. 每个字符占2字节,但是GetLength返回的是纯字符数,故_T("セβΔ尒ab汉字cd")长度为10。
2. '\0'仍然是结尾,所以_T("セβ\0Δ尒ab汉字cd")的长度为2。
另外W2A是把字符串从UNICODE转化到MBCS而不是ASCII,下面的代码可以证明:
CString str=_T("セβΔ尒ab汉字cd");
USES_CONVERSION;
char *pValueTemp=W2A((LPCTSTR)str);
nLength=strlen(pValueTemp);
此时的nLength为16,说明pValueTemp为MBCS串。
摘要:这篇文章介绍了如何使用Python3把PDF文件转换为word文档并替换word文档中的文字内容,这里替换的规则是中英转换或者其它自定义规则。
作者博客地址:http://www.yooongchun.cn/
pdf转换为word pdf文件由于很难编辑,因而想要通过编程修改内容有些困难【不是不可能,只是代价比较大,特别是要保留原pdf格式的话】,故而这里介绍一个替换pdf文件文字内容的间接解决方案,即:把pdf文件转换为word然后再编程替换word的文字内容,当然替换完成后想把word转为pdf就简单的多了
本文这里转换pdf文件介绍四种方法:
使用第三方工具使用pdfminer3k 解析pdf然后再把内容写入到word中使用第三方SDK使用LibreOffice 开发者库 这里简单说明下几种方法的优劣:
使用第三方工具的话,有这样两种情况: Online版本,大多可以免费使用,但是转换麻烦,不能编程操作,需要手动实现
软件版本,大多可以批量转换,但是大多转换有限制,需付费才能无限制使用,且不能编程处理
如果上面的情况符合你的实际需要,那再好不过!当然,不能符合的话,接着往下看
使用pdfminer3k解析pdf文件 对于纯文本内容且没有着严格的格式要求的话,你可以使用这个工具进行转换,详细转换请参考这篇文章:使用Python抽取PDF文件内容,包括文本、图像、线条等对象 使用第三方SDK 这个是一个比较好的选择,然而,SDK大多收费!!!这些SDK一般提供完善的开发者API接口,你可以方便而又简单的调用,楼主这里介绍一个叫做easyConvertPDF,他们提供一个pdf2word.py 的API,使用十分简单,转换效果页令人满意,只是,License太贵了,买不起,当然你可以下载试用
使用LibreOffice 开发者库 这个是Micro Office 支持的 Office 开发者库,里面可以实现PDF转换为Word,只是楼主在使用时一致没测试成功,下面给出一段使用代码,你需要先安装LibreOffice 才行
import os import subprocess for top, dirs, files in os.walk('/my/pdf/folder'): for filename in files: if filename.endswith('.pdf'): abspath = os.path.join(top, filename) subprocess.call('lowriter --invisible --convert-to doc "{}"' .format(abspath), shell=True) 从上面的四种方法中选择一种适合你的方法!
替换word文字内容 替换word使用的是win32com 的包,使用这个包你需要先安装pywin32
pip install pywin32 安装完成后可能会出现如下错误
ImportError: DLL load failed: win32api, sys, os 这时你可以通过如下方式解决
113th U.S. Congressional Districts20 years of the english premier football league20000 points in random motion2012 NFL Conference Champs2012-2013 NBA Salary Breakdown25 great circles2D Matrix Decomposition300 Outings3D bar chart with D3.js and x3dom3D scatter plot using d3 and x3dom401k Fees Vary Widely for Similar Companies512 Paths to the White House7th Grade Graphs with D39-Patch Quilt GeneratorA Bar ChartA Bar Chart, Part 1A Bar Chart, Part 2A Chicago Divided by KillingsA Christmas CarolA CoffeeScript console for d3.
1.入门级《MySql必知必会》 书中从介绍简单的数据检索开始,逐步深入一些复杂的内容,包括联结的使用、子查询、正则表达式和基于全文本的搜索、存储过程、游标、触发器、表约束,等等。其实入门阶段,主要把建库,建表,添加字段,修改字段,删除字段,约束,联表,子查询等等,其他的,如 存储过程、游标、触发器等等了解一下就好了 2.进阶篇《大话数据结构》 如果有时间就好好看看这本书,如果没时间,就把第6章,第8章好好看看,为什么要看数据结构?原因很简单,mysql索引是用B+树实现的,也就是多路查找树。所以学习mysql必须要了解一下简单的数据结构,如果有时间可以深入学习,如果没时间,就把树这块好好看看就行了 3.进阶篇《高性能MySql》 圣经中的圣经,经典中的经典,要一定的基础,这本书我就不过多介绍了,必看书籍。 4.进阶篇《MySQL技术内幕InnoDB存储引擎(第2版)》 现在mysql的默认存储引擎是InnoDB,足以说明它的重要性,所以这本书也是经典书籍,这本书也是需要基础的,不适合新手,而且需要一定数据结构和算法基础,但是如果你老老实实看过前面三本书了,相信这本书也不会有太大问题。 这四本书如果你老老实实看完,多实践,多运用,相信你的mysql方面的知识应付日常开发就足够了,至于想向更高方向发展的,本人也在学习中,希望大家一起来对学习mysql提出更好的建议。
var user ={ username : $("#username").val(), password : $("#password").val() } $.ajax({ type:"post", url:"http://localhost:8080/GD/LoginAction_execute.action", data:user, dataType:"text", success:function (data) { alert("登录成功"); } })
先上代码
#coding=utf-8 import re import urllib.request def getHtml(url): page = urllib.request.urlopen(url) html = page.read() html = html.decode('utf-8') return html def getItem(html): reg = re.compile(r'.*?<span class="title">(.*?)</span>.*?<p class="">.*?(\d+).*?</p>.*?<span class="rating_num" property="v:average">(.*?)</span>.*?<span>(\d+)人评价',re.S) items = re.findall(reg,html) global index for index,item in enumerate(items,index+1): print (index,item) if __name__=='__main__': index = 0 for i in range(0,226,25): url = "https://movie.douban.com/top250?start=" url += str(i) + "&filter=" html = getHtml(url) getItem(html) print ("\nOK!All OVER!") #关于正则表达式的一些说明
#<span class="title">(.*?)</span> 获取电影名字
#<p class="">.*?(\d+) 获取电影上映年份
随着MySQL地位爆炸式的提升, MySQL DBA的市场缺口日益剧增,优秀的MySQL DBA正在成为各个互联网公司抢手的人才。然而,需求来了,问题也来了,如何快人一步提高自身技能,让自己在日益激烈的技术挑战中不断增值、脱颖而出? 今天,我们将锁定MySQL数据库,通过韩锋、虢国飞、杨奇龙、杨建荣、张青林、李辉、贺春暘、李季鹏等8位MySQL专家的经验之谈,为想从事MySQL DBA的同学们答疑解惑,指明一条修炼的路子。
目录大纲 认真读完本文,你可以了解到:
一、选择篇
1、MySQL的流行原因及从业前景
2、MySQL DBA的薪资缘何普遍高于Oracle DBA
3、个人如何完成Oracle向MySQL的转型?
二、学习篇
1、从初级到高级的MySQL DBA学习路线及学习资料推荐
2、源码是否为高级MySQL DBA的必备技能?
3、MySQL不同分支的选择讨论
4、MySQL认证考试的含金量
三、趋势篇
1、随着功能的不断完善,MySQL还会是“小而美”的代表吗?
2、MySQL目前还缺乏哪方面的能力,是否足够成熟?
3、如何看待数据库中间层与数据库的关系?
4、传统运维逐步萎缩下,DBA应选择什么方向发展?
四、未来篇
1、数据库云化会对传统DBA带来什么样的冲击?
2、如何看待分布式数据库的未来前景?
3、越来越多的DBA正在或将从事DevOps类工作,你的看法是?
选择篇 Q1:你认为MySQL流行的主要原因是什么?从长远角度来看,从业前景如何?
1、流行原因
虢国飞:综合来看,MySQL的流行是偶然中的必然。原因主要有以下三点: 免费:传统数据库昂贵的license费用是很多企业一块心病,MySQL社区版已经能满足大部分用户的需求,而且是免费的,自然在企业内部推广时不会遇到阻力(老板们肯定支持);
开源:开源的好处是只要你想,你可以深入到代码级别来了解数据库的运作原理(甚至可以改造它),这个远胜于传统黑盒数据库只是简单输出一些log或者白皮书来让人猜测运作情况,开源能让DBA能知其然并知其所以然,而且开源社区还有丰富的工具,给DBA在维护上面提供了强大的支持。 DBA能在社区快速找到自己需求的东西,组合成自己的工具箱,所以社区工具的丰富也弥补了产品本身的缺陷;
简便:这一点使得MySQL部署简单,新手学习门槛低、上手快(当然要真正掌握好还是需要大量学习的),不要花费太大的精力就能快速搭建起一套高可用的集群来,而不像Oracle数据库那样,有很多配套东西需要了解,没有几年的学习很难掌握,对于新手来讲都会觉得挺难的,所以MySQL能在DBA群体(甚至包括开发人员)中快速地普及。
2、就业前景
李辉:目前来看,MySQL DBA的日子还是很滋润的,在可预见的3-5年内,MySQL在互联网行业的地位仍无法取代,对DBA的需求量仍然很大,特别是中高级DBA。但从长远角度来看,从业人员也要多涉猎其它关系型数据库以及NoSQL、Devops、云计算等技术,现代社会唯一不变的就是变化,不断学习、拥抱变化才能保证自己不被未来社会淘汰。
Q2:如何看待当前MySQL DBA的薪资普遍高于Oracle DBA的现象?如果想从事DBA,选哪个数据库更好?
1、MySQL DBA高工资的原因
杨建荣:因为是和Oracle来对比,可以分为几个方面来看:
首先物以稀为贵,MySQL DBA相对Oracle DBA来说要少一些,随着现在互联网行业的发展,这个需求还是会持续增长;
第二还是物以稀为贵,任何工种,高级职位都是稀缺的,如果你能够成为行业内的20%的人,无论你是从事哪种数据库,都会混得还不错。
第三还是物以稀为贵,什么事物的发展都有一个成熟度曲线,水涨船高之后,留在你手里的是真技能还是花拳绣腿,数据库的功能会越来越丰富,你的技能也要升级,不升级的话,用哪个数据库你都会很吃力。
杨奇龙:对于薪资高这点,我觉得要从两方面看:
MySQL DBA和Oracle DBA 的雇主行业不一样,MySQL DBA 多属于互联网等新兴企业,大多数是资本聚集,而Oracle DBA 则从事于传统或者部分大型企事业单位。两种企业的薪资平均水平本就有差距。
从技能要求上,MySQL在功能上比Oracle “弱”或者说不完善,需要DBA具有更高的技术掌控能力做HA、备份、恢复、校验等。Oracle 则提供了比较完备的功能。
2、从事DBA岗位,选MySQL还是Oracle?
杨建荣:有句话说,选择的利剑属于在于能够挥舞它的人,根据工作需要,自己的喜好去选择就可以,如果只是本着钱途是不推荐的。如果非要纠结到底是哪一个,其实也可以换个角度,如果两个都学,是不是这个问题就不是问题了。
杨奇龙:我的想法比较实际。行行出状元,不管是MySQL DBA 还是Oracle DBA都有比较成功的人士,建议是看各自的兴趣和爱好,以及自己想进的公司的招聘需求。MySQL 和Oracle 并不矛盾,我认识的相当多的人,两种数据库都能维护得非常好,比如大牛楼方鑫,Oracle 工具开发以及MySQL 内核开发都有非常出色的成果。
借助Spring框架的帮助,业务模块被合理的纵向分割,彼此之间互相独立。
而将这些纵向柱形的业务模块共同的,重复的部分横向切分放大,就称之为切面
在AOP中,描述切面的术语有通知(advice),切点(pointcut),连接点(join point)。
通知(advice):
它用于描述切面的目标 即切面必须要完成的工作。通知定义了切面是什么以及何时使用。
SpringAOP可以应用5种类型的通知:
1.前置通知(Before):在目标方法被调用之前调用通知功能。
2.后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。
3.返回通知(After-returning):在目标方法成功执行之后调用通知。
4.异常通知(After-throwing):在目标方法抛出异常后调用通知。
5.环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
连接点(join point)
是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
切点(pointcut)
一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点范围。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。稍后会介绍切入点表达式。另外,有些AOP框架是允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。
前言: 为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。
github:my github
注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
本文设计的文件包含:
(1)hardware_spi.c:硬件 SPI 驱动实现
(2)drvsfspi.c:软件模拟 SPI 实现代码
(3)drvexflash.c:SPI FLASH 操作部分代码
(4)hal_spi.c:SPI 软件、硬件方式封装统一接口
(5)头文件:
hardware_spi.h :硬件 SPI 相关
drvsfspi.h :软件模拟 SPI 相关
drvexflash.hSPI FLASH 相关
hal_spi.h:软件、硬件 SPI 接口封装
1. 硬件连接 W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。
W25Q64 的**最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。**操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。
W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)聚类算法,它是一种基于高密度连通区域的、基于密度的聚类算法,能够将具有足够高密度的区域划分为簇,并在具有噪声的数据中发现任意形状的簇。
DBSCAN算法的思想其实很简单,粗俗点说就是一圈套一圈套一圈套一圈………………然而,要搞清楚它,首先要弄清楚几个基本概念。
基本概念 1、对象o是一个核心对象。对象o的 ε -领域是与o为中心,以 ε 为半径的空间。 2、MinPts指定稠密区域的密度阀值。如果一个对象的 ε -区域至少包含MinPts个对象,则该对象是核心对象。 3、直接密度可达:如果对象p在核心对象q的 ε -领域内,则p是从q直接密度可达的。 4、密度可达:如果存在一个对象链p1,p2,……,pn,使得p1=q,pn=p,并且对于pi属于D,pi+1是从pi关于 ε 和MinPts直接密度可达的。 5、密度相连:如果存在对象q ε D,使得p1和p2都是从q关于MinPts密度可达的,则称p1和p2是关于 ε 和MinPts密度相连的。
来个图说明一下: 由上图可看出m,p,o.r 都是核心对象,因为他们的内都只是包含3个对象。
1.对象q是从m直接密度可达的。对象m从p直接密度可达的。 2.对象q是从p(间接)密度可达的,因为q从m直接密度可达,m从p直接密度可达。 3.r和s是从o密度可达的,而o是从r密度可达的,所有o,r和s都是密度相连的。
算法执行流程 如果,仔细研究这个算法,相信你会对我之前说的是一圈套一圈套一圈套一圈(实际上是密度可达的含义),会有所体会,哈哈^_^
OK,看一下伪代码吧。 到这里,Jiawei书上关于DBSCAN的部分就讲解完了,然而看过西瓜书的同学可能会发现,这里的做法与西瓜书上,会有些许不同 实际上,对比Jiawei和周志华老师的算法,你会发现差别并不大。只是在起始的时候会有一些区别,而算法推进策略是完全一样的。
OK,至此,DBSCAN算法结束了。 下面给几点需要注意的。 1、大家在看到上面的那个图的时候,容易过分具象化簇了。为什么这么说呢?因为一般的讲义都是把簇画成圆形,并且给出了核心点和半径。这样便于大家理解算法的思想。但实际上,大家要注意。DBSCAN是可以发现任意形状的簇的,因为它是基于密度的呀。因此这个簇基本上都不是圆形了,而且这个半径实际上是一个阀值。判断一个点是否在它的半径内,实际上还是通过计算距离来判断的。 2、DBSCAN算法需要选择一种距离度量,对于待聚类的数据集中,任意两个点之间的距离,反映了点之间的密度,说明了点与点是否能够聚到同一类中。由于DBSCAN算法对高维数据定义密度很困难,所以对于二维空间中的点,可以使用欧几里德距离来进行度量。 3、DBSCAN算法需要用户输入半径和阀值。前面也提到过,要求用户输入参数基本上都是不靠谱的,这里DBSCAN算法经过反复的实验,给出了一般的标准化参数,即半径=4,阀值=4。 具体,参数值是如何确定的,DBSCAN也是采用了一般的处理方法。而具体是什么,因为该处理方法是比较通用的,所以以后再说吧。
参考文章 DBSCAN聚类原理 数据挖掘(概念与技术)-第三版-Jiawei Han-P307
SSL证书的问题,可以忽略证书继续重新执行
git config --global http.sslVerify false
Intel(R) Ethernet Connection I218 V
Intel(R) Ethernet Connection I219 V
该网卡在安装windows Server系列系统(如windows server2012 、windows server2016等)时无法安装驱动,
官方不提供该网卡的windows server版驱动(原因不明,可能和商业销售模式有关)。
解决思路:
Intel(R) Ethernet Connection I218-LM是有windows server驱动的。两种网卡应该是通用的驱动,只是官方不给装。
解决方法:(以Windows Server 2016为例)
1、从官网下载PROWinx64驱动包。 https://downloadcenter.intel.com/zh-cn/product/71307/-I218-LM
2、改系统签名:
打开CMD分别执行:
bcdedit -set loadoptions DISABLE_INTEGRITY_CHECKS
bcdedit -set TESTSIGNING ON
重启电脑。。。
重启后系统桌面右下角:
3、手动安装驱动
打开设备管理器,找到未安装的网卡,右击,点击“更新驱动”
如上图点击手动
选择下载下的驱动包对应文件:PROWinx64\PRO1000\Winx64\NDIS64\e1d64x64.inf
选择“Intel(R) Ethernet Connection I218-LM”
点击下一步安装即可。
安装过程windows可能会弹出是否确定安装的警告,选择确定安装就行了。
安装完成后如下图,即可正常使用了。
4、最后,恢复系统签名: 打开CMD,分别执行:
bcdedit -set loadoptions ENABLE_INTEGRITY_CHECKS
bcdedit -set TESTSIGNING OFF
执行后重启电脑。
存储级内存(SCM)带来的存储革命 随着数据量的大幅增长以及各种数据分析应用的出现,存储市场保持着快速增长势头。然而,这些应用极大地消耗着硬件性能。随着性能需求的不断提升,即使是闪存也显得力不从心了。因此,我们需要新的技术和新的架构来应对这个新的挑战。
性能需求的快速增加,存储系统需要变得更快、更加密集、更加便宜,同时还要更加绿色。内存中处理(毫无疑问会极大的消耗内存)也带来了不断增加的内存配置以及动态随机访问内存(DRAM)的成本和能耗之间的平衡问题。
这些发展的结合形成了一个近乎完美的风暴,那就是存储级内存(SCM)。SCM已经成为了一个新的流行词,代表着下一代的非易失性内存(NVM)。
将下一代持久性存储应用到自己产品中的先行者将获取每年十亿计的收入,而且还会随着时间推移不断增加。SCM技术在目前的生态系统中有多个切入点,每一个切入点都能带来高价值。这一轮的变革大致顺序可以用以下三个场景来说明:
作为下一代的闪存替代技术,将彻底改变外部存储。
NVM到DRAM的扩展将带来更大规模的内存中处理(in-memory processing)。
持久性内存能彻底改变存储架构。
SCM+MLC/TLC NAND混合SSD探究
在SCM+MLC/TLC NAND混合SSD系统中,利用SCM低延迟的优势来提高SSD整体性能,但由于SCM价格较高,则利用TLC NAND低成本的优势来降低SSD整体价格,而MLC NAND则在SCM和TLC NAND充当中间介质。 SCM存储中没有类似NAND中的erase/program,而是通过set/reset来改变介质的状态达到存储的效果。如下图,在SCM中具有更短的set/reser时间,基本在ns级别。
与MLC NAND相比,TLC NAND具有更长的延迟。同时,TLC NAND 出错几率也大很多。如下图,
(1) SCM+MLC/TLC NAND混合SSD的架构长什么样子呢?如下图:
Host传进来的数据要写入SCM?MLC NAND?TLC NAND?怎么判断呢?主要看data management的“慧眼”。
如上图,来自Host的Cold/Sequential数据会直接写入MLC NAND,而Hot或者Random数据会写入SCM,当SCM满时,在挑选出相对cold的数据写入MLC NAND。最后已经写入MLC NAND的数据再将Frozen的数据趁MLC NAND做GC时搬入TLC NAND。 SCM与MLC NAND之间数据判断采用的是CDE算法:
MLC NAND与TLC NAND之间数据判断采用的是RR-FDCA算法:
(2) SCM+MLC/TLC NAND混合SSD效果如何呢?
针对Hot workloads情况下,SCM对混合SSD的整体提升效果很明显。
针对Cold/Random workloads情况下,SCM对混合SSD的性能也有一定的提升。不过,此时混合SSD的性能已经看到有收到NAND flash的制约。
针对Cold/Sequential workloads情况下,SCM的作用基本没有了,混合SSD的性能基本与Only NAND-based SSD的性能差别不大。
写在最后
实现SCM+MLC/TLC NAND混合SSD,需要在不同workloads与cost之间做出平衡。随着QLC的问世,相信QLC凭借更低的价格优势,会助力混合SSD更加多样化。
欲了解更多研究细节,请点击"阅读原文"下载原版英文文献,谢谢!
精彩推荐:
企业级SSD发展趋势 2D NAND和3D NAND横向对比 第二代3D TLC NAND原厂级深度评测
为QLC保驾护航 | 慧荣科技亮出最新LDPC技术
SSD固态硬盘接口种类多,你了解多少? 写放大机制与影响因素详解