z-950 / blog Goto Github PK
View Code? Open in Web Editor NEWThis is a blog for myself
This is a blog for myself
在服务中,禁用以下三个服务:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc
,右侧找到Start
键,右键点击修改,将数值2
改为4
。找到FailureActions
键,右键点击修改,修改该键的二进制数据,分别将0010
和0018
行的左起第5个数值选中,由原来的01
改为00
。在C:\Windows\System32
中找到WaaSMedicAgent.exe
将其重命名为WaaSMedicAgent-fuck.exe
,然后在桌面创建一个.txt文件,修改名字为WaaSMedicAgent.exe
,并复制进C:\Windows\System32
。
重命名时提示无权限:右键该文件,点击属性->安全->高级,在“所有者”一行点击更改,然后点击高级,再选择立刻查找。在搜索结果里选择符合本机用户的对象,点击所有确定。 然后再次右键该文件,点击属性->安全->编辑,选择刚才添加的用户,勾选完全控制,点击所有确定即可。
逆向操作:恢复服务,恢复注册表,恢复文件。然后重启。
常见的hexo建站,使用两个branch,分别放原始md文件和生成的文件。常用vscode进行文章编写。
在只使用一个branch放生成的文件时会发送。因为大部分教程实际上没有提及使用两个branch。
对于喜欢折腾的人来说不算什么,起码比自己搭建后端甚至写博客界面简单多了。但相对于现成的issue,还是比较麻烦。
如果文章中有更新,只能手动写明,甚至更改文章日期,没有其他信息。issue虽然也只保留了edited的时间戳,但勉强能用。
首先是整理自己所学的技术和知识。此前由于觉得博客毕竟是给人看的,总不能写的太随意。现在想来,大抵只有自己会看,所以无需顾虑太多,该写清楚的还是需要写清楚。
此前记录笔记都是通过网页收藏。近来复习时发现有的文章失效了,大致也算理解了csdn那种抄来抄去的情况了。
对于自己的技术总结,也基本上是在闲聊时完成。聊天式的口述总归不严谨,还是写文章来的通达,并且还能回顾与改正。
自带标签与按标签搜索、无需考虑配置问题、无需考虑存储问题、文章管理更为方便。
我见过有人说写md文档直接存进repo里的,实际上这种做法和hexo类似,并且由于没有别人提供的样式,具有的功能反而更少一些(文件没有办法添加标签,自然也没有按标签搜索)。
附上其他更常见的方式:利用GitHub写博客的几种方式
这本是一道笔试题。最后差两个判断没有修改。
其中比较少见的是,使用new Date(2020, 1, 0).getDate()
可以获取2020年1月的总天数
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
table.calendar {
font-size: 14px;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
table.calendar th {
background: #f5f5f5;
color: #999;
}
table.calendar th,
table.calendar td {
border: 1px solid #e1e1e1;
padding: 0;
height: 32px;
line-height: 32px;
text-align: center;
}
table.calendar td.current {
background: #1890ff;
color: #fff;
}
table.calendar th.pre,
table.calendar th.next {
color: #1890ff;
cursor: pointer;
}
table.calendar th.date {
color: #000;
}
</style>
</head>
<body>
<button type="button" onclick="calendar(document.getElementById('jsContainer'), 2020, 1)">button</button>
<div id="jsContainer">
</div>
<script>
function calendar(container, year, month) {
this.year = year;
this.month = month;
this.html = html;
this.el = document.createElement("table"); //TODO: 创建分页组件根节点
if (!el) return;
this.el.className = 'calendar';
this.el.innerHTML = this.html();
container.appendChild(this.el);
this.el.addEventListener('click', event => {
var el = event.target;
var isPre = el.classList.contains('pre');
var isNext = el.classList.contains('next');
if (!isPre && !isNext) return;
if (isPre) {
//TODO: 更新this.month和this.year
if (this.month !== 1) {
this.month--
} else {
this.year--
this.month = 12
}
} else {
//TODO: 更新this.month和this.year
if (this.month !== 12) {
this.month++
} else {
this.year++
this.month = 1
}
}
this.el.innerHTML = this.html();
});
function html() {
var date = new Date();
var year = +this.year || 0;
var month = (+this.month || 0) - 1;
var day = date.getDate();
//TODO: 生成组件的内部html字符串
let days = new Date(year, month + 1, 0).getDate()
let startAt = new Date(year, month).getDay() - 1
if (startAt === -1) startAt = 6
const lines = Math.ceil((days + startAt) / 7)
let endAt = (days + startAt) % 7
if (endAt === 0) endAt = 7
const couldBeCurrent = month === date.getMonth() && year === date.getFullYear()
let i = 1
const getDateTd = () => {
const current = i
++i
if (couldBeCurrent && current == day) {
return `<td class="current">${current}</td>`
} else {
return `<td>${current}</td>`
}
}
const getDateTdList = (n) => {
let tdList = ''
while (n--) {
tdList += getDateTd()
}
return tdList
}
let html = `<thead>
<tr><th class="pre"><</th><th colspan="5" class="date">${this.year}.${this.month < 10 ? '0' : ''}${this.month}</th><th class="next">></th></tr>
<tr><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th><th>日</th></tr>
</thead>
<tbody>`
html += `<tr>${'<td></td>'.repeat(startAt)}${getDateTdList(7 - startAt)}</tr>`
let count = lines - 2
while (count--) {
html += `<tr>${getDateTdList(7)}</tr>`
}
html += `<tr>${getDateTdList(endAt)}${'<td></td>'.repeat(7 - endAt)}</tr>`
html += '</tbody>'
return html;
}
}
</script>
</body>
</html>
环境:Windows 10 64bit,16G
使用js增加内存占用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 30vh;
}
</style>
</head>
<body>
<div>div1</div>
<div>div2</div>
<div>div3</div>
<div>div4</div>
<div>div5</div>
<div>div6</div>
<div>div7</div>
<div>div8</div>
<div>div9</div>
<div>div10</div>
<script>
const arr = []
const n = 1 * 1000 * 1000
let count = 0
function increace() {
setTimeout(() => {
arr.push(new Array(n).fill("x"))
count += n
increace()
}, 100);
}
window.onload = () => {
debugger
increace()
}
</script>
</body>
</html>
window.performance.memory.jsHeapSizeLimit
查看最大内存环境:openJDK 13、Kotlin 1.3.72、Vert.x 3.9.x
遇到需要重写Launcher进行固定配置的情况,参考官方例子。
由于个人主要使用Kotlin进行编写,所以重写Launcher类自然也会用Kotlin。下面介绍遇到的问题。
对于Kotlin,main可以单独定义,也可以使用伴生对象在类里面定义。
Launcher类是启动类,所以Vert.x的Launcher类中定义了main方法。重写时,也需要定义自己main方法。
自己的Launcher写在MyLauncher.kt中。
class MyLauncher : io.vertx.core.Launcher() {
companion object {
@JvmStatic
fun main(args: Array<String>) {
MyLauncher().dispatch(args)
}
}
}
上面的写法会报错:Accidental override: The following declarations have the same JVM signature (main([Ljava/lang/String;)V)
经过搜索,了解到可以通过@JvmName
注解改变重写的方法的名字来解决该错误,但此为main函数,不可修改名字,故无解。
fun main(args: Array<String>) {
MyLauncher().dispatch(args)
}
class MyLauncher : io.vertx.core.Launcher() {}
不会报错。来看一下能否运行。
配置build.gradle的mainClass为自己的Launcher:vertx { launcher = "com.example.starter.MyLauncher" }
此处使用了
io.vertx.vertx-plugin
这个插件。只用java
插件时,应该配置mainClassName
字段
编译并运行。报错:java.lang.ClassNotFoundException: com.example.starter.Launcher
看一下build/classes里面有什么:
+-- com.example.starter
+-- MyLauncherKt.class
+-- MyLauncher.class
具体进去看一下,发现main方法放到了MyLauncher.class里。修改一下mainClass的位置:vertx { launcher = "com.example.starter.MyLauncherKt" }
。再次编译运行,没有报错。
答案是不可行,标题已经说明了。
至于为什么不可行,还是出于方便角度考虑的。
对于Vert.x,一个jar里还得包括mainVerticle用于启动,mainVerticle中才是应用的启动逻辑。
mainVerticle可以在build.gradle里设置,放到manifest里。这样我们可以直接运行jar,否则还得传入mainVerticle参数。
首先说明,传入mainVerticle的麻烦之处(对于微服务架构):
手动配置参数是容易忘记的,毫无意义,不如将其自动化方便。
在dispatch
所在的类io.vertx.core.impl.launcher.VertxCommandLauncher
中,有如下函数:
private String getFromManifest(String key) {
try {
Enumeration<URL> resources = RunCommand.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
while (resources.hasMoreElements()) {
try (InputStream stream = resources.nextElement().openStream()) {
Manifest manifest = new Manifest(stream);
Attributes attributes = manifest.getMainAttributes();
String mainClass = attributes.getValue("Main-Class");
if (main.getClass().getName().equals(mainClass)) {
String value = attributes.getValue(key);
if (value != null) {
return value;
}
}
}
}
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
}
return null;
}
其中attributes.getValue("Main-Class")
获取到的是build.gradle中配置的mainClass,也就是MyLauncherKt
。
而main.getClass().getName()
获取到的则是运行时的Launcher的class,也就是MyLauncher
。
所以,单独定义main函数,并且配置正确的情况下,Vert.x的Launcher是无法获取到manifest中的mainVerticle的。
目前找不到此做法。
如果使用@file:JvmName("MyLauncher")
强制输出为MyLauncher.class,则会报错:Duplicate JVM class name 'com/example/starter/MyLauncher' generated from: package-fragment com.example.starter, MyLauncher
修改为@file:JvmName("Launcher")
可以解决此错误,但Launcher.class里只有main函数,并且MyLauncher类依然在MyLauncher.class里,相当于没用。
综上,解决办法是,使用Java重写Launcher和其中的main函数。
看到有资料说main函数无法被重写(override),实践了一下,其实是可以的。
signal协议是一种棘轮式前向保密协议,适用于同步和异步消息传递环境。
signal协议的js实现:libsignal-protocol-javascript
此为例子。该js库的实现不完整,缺少了原始版本的部分方法,见issue
一旦建立会话,就没有必要解除会话。
引入dist/libsignal-protocol.js
const KeyHelper = libsignal.KeyHelper;
const registrationId = KeyHelper.generateRegistrationId();
// Store registrationId somewhere durable and safe.
// store需要自行实现,可参考最后的例子
// 可以储存在浏览器的localStorage或者indexDB。如果浏览器不安全则毫无办法
// 为2.建立会话中同一个store
KeyHelper.generateIdentityKeyPair().then(function(identityKeyPair) {
// keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer }
// keyPair格式
// Store identityKeyPair somewhere durable and safe.
});
KeyHelper.generatePreKey(keyId).then(function(preKey) {
store.storePreKey(preKey.keyId, preKey.keyPair);
});
KeyHelper.generateSignedPreKey(identityKeyPair, keyId).then(function(signedPreKey) {
store.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
});
// Register preKeys and signedPreKey with the server
// 向服务器注册,服务器有signal相关接口.
// 应为2.建立会话
// store需要自行实现,可参考最后的
const store = new MySignalProtocolStore(); // 储存identity, prekeys, signed prekeys, and session state
const address = new libsignal.SignalProtocolAddress(recipientId, deviceId); // 目标地址
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
const sessionBuilder = new libsignal.SessionBuilder(store, address);
// Process a prekey fetched from the server. Returns a promise that resolves
// once a session is created and saved in the store, or rejects if the
// identityKey differs from a previously seen identity for this address.
// 将载入时生成的id和key传入以建立链接
const promise = sessionBuilder.processPreKey({
registrationId: <Number>,
identityKey: <ArrayBuffer>,
signedPreKey: {
keyId : <Number>,
publicKey : <ArrayBuffer>,
signature : <ArrayBuffer>
},
preKey: {
keyId : <Number>,
publicKey : <ArrayBuffer>
}
});
promise.then(function onsuccess() {
// encrypt messages
});
promise.catch(function onerror(error) {
// handle identity key conflict
});
const plaintext = "Hello world"; // 要发送的原信息
const sessionCipher = new libsignal.SessionCipher(store, address); // 加密解密接口
sessionCipher.encrypt(plaintext).then(function(ciphertext) {
// ciphertext -> { type: <Number>, body: <string> }
handle(ciphertext.type, ciphertext.body);
});
// 新建会话解密PreKeyWhisperMessage
const sessionCipher = new SessionCipher(store, address);
// Decrypt a PreKeyWhisperMessage by first establishing a new session.
// Returns a promise that resolves when the message is decrypted or
// rejects if the identityKey differs from a previously seen identity for this
// address.
sessionCipher.decryptPreKeyWhisperMessage(ciphertext).then(function(plaintext) {
// handle plaintext ArrayBuffer
}).catch(function(error) {
// handle identity key conflict
});
// 使用现有会话解密WhisperMessage
// Decrypt a normal message using an existing session
const sessionCipher = new SessionCipher(store, address);
sessionCipher.decryptWhisperMessage(ciphertext).then(function(plaintext) {
// handle plaintext ArrayBuffer
});
stroe示例,储存在memory中。但规范要求持久化(durable)储存。
此示例来自该js库的test
function SignalProtocolStore() {
this.store = {};
}
SignalProtocolStore.prototype = {
Direction: {
SENDING: 1,
RECEIVING: 2,
},
getIdentityKeyPair: function() {
return Promise.resolve(this.get('identityKey'));
},
getLocalRegistrationId: function() {
return Promise.resolve(this.get('registrationId'));
},
put: function(key, value) {
if (key === undefined || value === undefined || key === null || value === null)
throw new Error("Tried to store undefined/null");
this.store[key] = value;
},
get: function(key, defaultValue) {
if (key === null || key === undefined)
throw new Error("Tried to get value for undefined/null key");
if (key in this.store) {
return this.store[key];
} else {
return defaultValue;
}
},
remove: function(key) {
if (key === null || key === undefined)
throw new Error("Tried to remove value for undefined/null key");
delete this.store[key];
},
isTrustedIdentity: function(identifier, identityKey, direction) {
if (identifier === null || identifier === undefined) {
throw new Error("tried to check identity key for undefined/null key");
}
if (!(identityKey instanceof ArrayBuffer)) {
throw new Error("Expected identityKey to be an ArrayBuffer");
}
var trusted = this.get('identityKey' + identifier);
if (trusted === undefined) {
return Promise.resolve(true);
}
return Promise.resolve(util.toString(identityKey) === util.toString(trusted));
},
loadIdentityKey: function(identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get identity key for undefined/null key");
return Promise.resolve(this.get('identityKey' + identifier));
},
saveIdentity: function(identifier, identityKey) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to put identity key for undefined/null key");
var address = new libsignal.SignalProtocolAddress.fromString(identifier);
var existing = this.get('identityKey' + address.getName());
this.put('identityKey' + address.getName(), identityKey)
if (existing && util.toString(identityKey) !== util.toString(existing)) {
return Promise.resolve(true);
} else {
return Promise.resolve(false);
}
},
/* Returns a prekeypair object or undefined */
loadPreKey: function(keyId) {
var res = this.get('25519KeypreKey' + keyId);
if (res !== undefined) {
res = { pubKey: res.pubKey, privKey: res.privKey };
}
return Promise.resolve(res);
},
storePreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeypreKey' + keyId, keyPair));
},
removePreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeypreKey' + keyId));
},
/* Returns a signed keypair object or undefined */
// 25519.Curve25519是一个椭圆曲线,在加密中使用
loadSignedPreKey: function(keyId) {
var res = this.get('25519KeysignedKey' + keyId);
if (res !== undefined) {
res = { pubKey: res.pubKey, privKey: res.privKey };
}
return Promise.resolve(res);
},
storeSignedPreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeysignedKey' + keyId, keyPair));
},
removeSignedPreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeysignedKey' + keyId));
},
loadSession: function(identifier) {
return Promise.resolve(this.get('session' + identifier));
},
storeSession: function(identifier, record) {
return Promise.resolve(this.put('session' + identifier, record));
},
removeSession: function(identifier) {
return Promise.resolve(this.remove('session' + identifier));
},
removeAllSessions: function(identifier) {
for (var id in this.store) {
if (id.startsWith('session' + identifier)) {
delete this.store[id];
}
}
return Promise.resolve();
}
};
由于是web api,主要考虑使用http、websocket的交互方式,也即http和websocket的api设计风格。
调用api,实际上就是信息传递。这里信息传递包括了请求和响应。
简单起见,先只考虑使用http协议调用api。
最初接触到前后端交互并自己进行开发,很理所当然的按最“简单”的方式设计api:所有请求数据都使用json并塞进http的body里,同时所有返回数据同样使用json塞进body里。
此处的“简单”并非真的简单,自行解析和处理数据,实际上不比利用http字段并使用相关函数处理简单。
后面了解到更多关于http的知识,并且了解到RESTful api规范,自然而然会使用RESTful风格的api。
迁移确实有成本,但下文会给出迁移成本低的理由。
前面提到了调用api实际上是信息传递。我们来看一下纯post和RESTful有哪里不一样。
首先可以肯定的是,经过设计,纯post能表达的信息量与RESTful是一样甚至更多的。
具体他们的区别:纯post是语义的聚合,RESTful是语义的分散。聚合,指的是大部分的信息都集中在http的body中。分散,指的是信息分散在http的各个字段中。
也即他们的区别是,对http使用方式的不同。纯post往往需要将http已有的字段重新定义一次,对http的利用率是较低的。纯post与RESTful风格的api比较,它们对path的设计也不同:纯post往往会把method放入path;而对于RESTful来说,method就是http的method。
也就是说,如果不了解http,那么设计纯post api会再设计出类似http的一套东西。那么为什么不学习http并直接转向RESTful风格呢?自行设计需要踩坑,效率通常比学习现有的东西低。即便是需求复杂,http的字段无法满足,http和RESTful的知识也能为设计提供思路。
JSON-RPC实际上就是比较完整的纯post信息交换设计。实际需要时可供参考。
由于websocket在正式通信过程中仅有消息内容,与http丰富的字段不同。协议本身限制了通信过程中只能使用类似http的纯post的方式。(socket.io有命名空间和事件实际上也是对消息内容的设计)
所以JSON-RPC同样适合websocket。
虽然RPC主要是在服务间进行,但浏览器也可以当作一个服务(渲染服务),并且通常只作为调用者。
RPC面向方法,RESTful面向资源,但无论怎么样,调用时都需要知道相关的地址(RPC的注册中心,RESTful的资源解析)和对应的参数,对于调用者来说,两者区别不大。
上面是经典的误解。RPC调用强调的是 函数调用,如何实现网络通信是函数调用的底层,也就是说,纯post(JSON-RPC)和RESTful都能作为RPC的网络交互设计风格。具体如何选择,看需求、接口的复杂程度。
这两者其实不应该相提并论,具体区别可以参考:如何理解RPC和REST (注:RESTful是REST使用http的一种实现)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.