0%

引言

在国家放开管控之后,我一波浩浩荡荡的小阳人大军中,我在圣诞节有幸加入,虽然网上已经有很多体验了,但记录下个人的感染过程,还是很有必要的。

新冠发病7日典型症状过程


首先放一张来自网上的新冠发病7日典型症状过程和对应的一些药物治疗。

多了解一点,心里有底一点,万一自己患病很严重用的上呢。

个人新冠历程

DAY1 2022-12-25

当天本人都没啥感觉,也不知道自己已经感染了,只是晚上的时候感觉到喉咙发干,很想喝水。

DAY2 2022-12-26

一觉醒来,喉咙干的厉害,起床后就感觉头有点痛,之后就一直头痛,但属于可以忍受的范围。
从中午开始,就开始发烧了。

  • 上午到中午12点,大概38度左右
  • 中午12点到晚上6点,在38度到39度之间反复横跳,平均大概38.5,这个阶段头痛的就比较厉害了,但属于还能硬抗的阶段,还有要带娃,也只能硬抗。身体的酸痛程度的话,膝盖,背部有轻微酸痛的感觉,有些许没有力气的症状
  • 晚上6点到10点,体温进一步升高,大概在38.5到39度之间横跳了,人的反应有那么点小木呆了,熬到老婆下班回来就休息了。
  • 当天晚上睡眠不怎么好,基本1,2个小时就会醒,高烧也退不下来,基本在38.5到39.5之间横跳,但基本处于38.8这一档,如果温度再升高,就需要动用退烧药了

总的来说,和新冠7天中的第二天症状基本符合。

DAY3 2022-12-27

早上醒来8点,测体温直接下到37.4,以为今天不会发烧了。
但9点过后又烧起来,基本全天维持38度的样子。
头痛的话只是轻微头痛了。
然后乏力这一点,大概中度这个样子。
喉咙干的话,基本没有这个症状了,也没有出现网上说的吞刀片这个场景。
咳嗽的话,有很轻微的咳嗽,全天不超过10声,但有痰。

总的来说,感觉处于新冠流程第四天到第五天的样子。

DAY4 2022-12-28

早上醒来,温度37度,已经不发烧了
不咳嗽,不酸痛,整体精神可以,基本已经恢复正常了。
唯一有点异常的就是感觉胸里有点东西,其他啥都没感觉了。

DAY4 2022-12-28

早上醒来,温度37度,已经不发烧了
不咳嗽,不酸痛,整体精神可以,基本已经恢复正常了。
唯一有点异常的就是感觉胸里有点东西,其他啥都没感觉了。

DAY5 2022-12-29

今天体温全天正常
除了一点鼻塞外其他基本无异常了
顺便测了下抗原,还是阳性。

DAY6 2022-12-30

基本正常

DAY7 2022-12-31

全天开始流鼻涕,开始咳嗽

DAY8 2023-01-01

咳嗽开始严重

DAY8 2023-01-01

没啥症状,over

作用差别

指标体系是结果和过程,更多的作用是分析,
标签体系维度集合,更多是用于圈选,
画像是标签的集合

所谓标签系统,就是利用企业自由数据、第三方数据、采购数据等等,对自有的用户进行打标签的处理。标签系统既可以自主生成标签,也可以对标签进行有序管理维护。

所谓画像系统,就是利用标签,进行的可视化分析及各类应用的落地。简单的,可以是一些统计分析;复杂的,可以是人群的各维度的透视。

数据库

MPP 数据库

MPP (Massively Parallel Processing),即大规模并行处理。简单来说,MPP是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果(与Hadoop相似)。

MPP数据库是一款 Shared Nothing架构的分布式并行结构化数据库集群,具备高性能、高可用、高扩展特性,可以为超大规模数据管理提供高性价比的通用计算平台,并广泛地用于支撑各类数据仓库系统、BI 系统和决策支持系统

常见mpp数据库有

  • GreenPlum
  • Vertica
  • Sybase IQ
  • TD Aster Data
  • HybirdDB 阿里云的,基于greenplum开源改造

数仓应用

DSS 决策支持系统

决策支持系统(Decision Support System)是一个基于计算机用于支持业务或组织决策活动的信息系统。 DSS服务于组织管理、运营和规划管理层(通常是中级和高级管理层),并帮助人们对可能快速变化并且不容易预测结果的问题做出决策。决策支持系统可以全计算机化、人力驱动或二者结合。

ad-hoc query 即席查询

即席查询是用户根据自己的需求,灵活的选择查询条件进行查询。

系统根据用户的选择生成相应的统计报表。普通查应用查询是定制开发的,即席查询是用户自定义查询条件

理解:快速的执行自定义SQL(可能无法提前运算和预测)

业务术语

UJM (User Journey Map)

用户访问路径太朴素,试试英文的UJM

前言

不知做数仓的各位在工作中是否碰到以下几个现象

  • 业务方问,这个报表中的xx指标和另一个报表中的数据为什么对不上?一查,原来是表报02种的指标不含包了某个条件。
  • 新接了一个需求,是一个从来没有碰到过的需求,于是坑此坑系的去梳理了半天,然后一次偶然的机会,同事告诉你有现成的,某某以前做过。
  • 业务方拿着一个表的某个字段过来问你,这个字段是什么意思?
  • 某业务方开发的脚本,速度奇慢,于是让你帮忙优化,你一看脚本,好家伙,代码惨不忍睹,各种模型表中已有指标全部重新计算。
  • 使用其他同事开发的模型,各种命名全靠猜。
  • 已上线的任务,某天业务方反馈数据重复了,某某字段为空了等等等。

如果你有过以上大多数精力,恭喜你,你的数仓建设尚未体系化,处于百花齐放的各自开发阶段。

海拍客的现状

本人刚来海拍客的时候,上述描述的情况不断在发生,几个比较严重的原因和现象个人总结如下。

1. 需求烟囱化

因海拍客采用的是

前言

KMP(Knuth-Morris-Pratt)算法是一种字符串匹配算法,可以在 O(n+m) 的时间复杂度内实现两个字符串的匹配。

字符串匹配问题

所谓字符串匹配,是这样一种问题:“字符串 P 是否为字符串 S 的子串?如果是,它出现在 S 的哪些位置?” 其中 S 称为主串;P 称为模式串。下面的图片展示了一个例子。

  主串是莎翁那句著名的 “to be or not to be”,这里删去了空格。“no” 这个模式串的匹配结果是“出现了一次,从$S[6]$开始”;“ob”这个模式串的匹配结果是“出现了两次,分别从$S[1]$、$S[10]$开始”。按惯例,主串和模式串都以0开始编号。  字符串匹配是一个非常频繁的任务。例如,今有一份名单,你急切地想知道自己在不在名单上;又如,假设你拿到了一份文献,你希望快速地找到某个关键字(keyword)所在的章节……凡此种种,不胜枚举。  我们先从最朴素的Brute-Force算法开始讲起。

Brute-Force

顾名思义,Brute-Force是一个纯暴力算法。说句题外话,我怀疑,“暴力”一词在算法领域表示“穷举、极低效率的实现”,可能就是源于这个英文词。  

首先,我们应该如何实现两个字符串 A,B 的比较?所谓字符串比较,就是问“两个字符串是否相等”。最朴素的思想,就是从前往后逐字符比较,一旦遇到不相同的字符,就返回False;如果两个字符串都结束了,仍然没有出现不对应的字符,则返回True。实现如下:

  既然我们可以知道“两个字符串是否相等”,那么最朴素的字符串匹配算法 Brute-Force 就呼之欲出了——

  • 枚举 i = 0, 1, 2 … , len(S)-len(P)
  • 将 S[i : i+len(P)] 与 P 作比较。如果一致,则找到了一个匹配。

  现在我们来模拟 Brute-Force 算法,对主串 “AAAAAABC” 和模式串 “AAAB” 做匹配:

  这是一个清晰明了的算法,实现也极其简单。下面给出Python和C++的实现:

  我们成功实现了 Brute-Force 算法。现在,我们需要对它的时间复杂度做一点讨论。按照惯例,记 $n = |S|$ 为串 S 的长度,$m = |P|$ 为串 P 的长度。
  考虑“字符串比较”这个小任务的复杂度。最坏情况发生在:两个字符串唯一的差别在最后一个字符。这种情况下,字符串比较必须走完整个字符串,才能给出结果,因此复杂度是 $O(len)$ 的。
    由此,不难想到 Brute-Force 算法所面对的最坏情况:主串形如“AAAAAAAAAAA…B”,而模式串形如“AAAAA…B”。每次字符串比较都需要付出 $|P|$ 次字符比较的代价,总共需要比较 $|S| - |P| + 1$次,因此总时间复杂度是 $O(|P|⋅(|S|−|P|+1))O(|P|\cdot (|S| - |P| + 1) )O(|P|\cdot (|S| - |P| + 1) )$ . 考虑到主串一般比模式串长很多,故 Brute-Force 的复杂度是 $O(|P|⋅|S|)O(|P| \cdot |S|)O(|P| \cdot |S|)$ ,也就是 $O(nm)的$。这太慢了!

Brute-Force的改进思路

经过刚刚的分析,您已经看到,Brute-Force 慢得像爬一样。它最坏的情况如下图所示:

  我们很难降低字符串比较的复杂度(因为比较两个字符串,真的只能逐个比较字符)。因此,我们考虑降低比较的趟数。如果比较的趟数能降到足够低,那么总的复杂度也将会下降很多。  要优化一个算法,首先要回答的问题是“我手上有什么信息?” 我们手上的信息是否足够、是否有效,决定了我们能把算法优化到何种程度。请记住:尽可能利用残余的信息,是KMP算法的思想所在。
  在 Brute-Force 中,如果从 S[i] 开始的那一趟比较失败了,算法会直接开始尝试从 S[i+1] 开始比较。这种行为,属于典型的“没有从之前的错误中学到东西”。我们应当注意到,一次失败的匹配,会给我们提供宝贵的信息——如果 S[i : i+len(P)] 与 P 的匹配是在第 r 个位置失败的,那么从 S[i] 开始的 (r-1) 个连续字符,一定与 P 的前 (r-1) 个字符一模一样!

  需要实现的任务是“字符串匹配”,而每一次失败都会给我们换来一些信息——能告诉我们,主串的某一个子串等于模式串的某一个前缀。但是这又有什么用呢?

跳过不可能成功的字符串比较

  有些趟字符串比较是有可能会成功的;有些则毫无可能。我们刚刚提到过,优化 Brute-Force 的路线是“尽量减少比较的趟数”,而如果我们跳过那些绝不可能成功的字符串比较,则可以希望复杂度降低到能接受的范围。  那么,哪些字符串比较是不可能成功的?来看一个例子。已知信息如下:

  • 模式串 P = “abcabd”.
  • 和主串从S[0]开始匹配时,在 P[5] 处失配。

      首先,利用上一节的结论。既然是在 $P[5]$ 失配的,那么说明 $S[0:5]$ 等于 $P[0:5]$,即”abcab”. 现在我们来考虑:从 $S[1]$、$S[2]$、$S[3]$ 开始的匹配尝试,有没有可能成功?
      从 $S[1]$ 开始肯定没办法成功,因为 $S[1] = P[1]$ = ‘b’,和 $P[0]$ 并不相等。从 $S[2]$ 开始也是没戏的,因为 $S[2] = P[2]$ = ‘c’,并不等于$P[0]$. 但是从 $S[3]$ 开始是有可能成功的——至少按照已知的信息,我们推不出矛盾。

      带着“跳过不可能成功的尝试”的思想,我们来看next数组。

next数组

  next数组是对于模式串而言的。P 的 next 数组定义为:$next[i]$ 表示 $P[0]$ ~ $P[i]$ 这一个子串,使得 前k个字符恰等于后k个字符 的最大的k. 特别地,k不能取i+1(因为这个子串一共才 i+1 个字符,自己肯定与自己相等,就没有意义了)。

  上图给出了一个例子。P=”abcabd”时,$next[4]$=2,这是因为$P[0]$ ~ $P[4]$ 这个子串是”abcab”,前两个字符与后两个字符相等,因此$next[4]$取2. 而$next[5]=0$,是因为”abcabd”找不到前缀与后缀相同,因此只能取0.

  如果把模式串视为一把标尺,在主串上移动,那么 Brute-Force 就是每次失配之后只右移一位;改进算法则是每次失配之后,移很多位,跳过那些不可能匹配成功的位置。但是该如何确定要移多少位呢?

  在 $S[0]$ 尝试匹配,失配于 $S[3] <=> P[3]$ 之后,我们直接把模式串往右移了两位,让 $S[3]$ 对准 $P[1]$. 接着继续匹配,失配于 $S[8] <=> P[6]$, 接下来我们把 P 往右平移了三位,把 $S[8]$ 对准 $P[3]$. 此后继续匹配直到成功。
  我们应该如何移动这把标尺?很明显,如图中蓝色箭头所示,旧的后缀要与新的前缀一致(如果不一致,那就肯定没法匹配上了)!

  回忆next数组的性质:$P[0]$ 到 $P[i]$ 这一段子串中,前$next[i]$个字符与后$next[i]$个字符一模一样。既然如此,如果失配在 $P[r]$, 那么$P[0]$~$P[r-1]$这一段里面,前$next[r-1]$个字符恰好和后$next[r-1]$个字符相等——也就是说,我们可以拿长度为 $next[r-1]$ 的那一段前缀,来顶替当前后缀的位置,让匹配继续下去!
  您可以验证一下上面的匹配例子:P[3]失配后,把P[next[3-1]]也就是P[1]对准了主串刚刚失配的那一位;P[6]失配后,把P[next[6-1]]也就是P[3]对准了主串刚刚失配的那一位。

  如上图所示,绿色部分是成功匹配,失配于红色部分。深绿色手绘线条标出了相等的前缀和后缀,其长度为next[右端]. 由于手绘线条部分的字符是一样的,所以直接把前面那条移到后面那条的位置。因此说,next数组为我们如何移动标尺提供了依据。接下来,我们实现这个优化的算法。

利用next数组进行匹配

  了解了利用next数组加速字符串匹配的原理,我们接下来代码实现之。分为两个部分:建立next数组、利用next数组进行匹配。  首先是建立next数组。我们暂且用最朴素的做法,以后再回来优化:

  如上图代码所示,直接根据next数组的定义来建立next数组。不难发现它的复杂度是 $O(m2)O(m^2)O(m^2)$ 的。
  接下来,实现利用next数组加速字符串匹配。代码如下:

  如何分析这个字符串匹配的复杂度呢?乍一看,pos值可能不停地变成next[pos-1],代价会很高;但我们使用摊还分析,显然pos值一共顶多自增len(S)次,因此pos值减少的次数不会高于len(S)次。由此,复杂度是可以接受的,不难分析出整个匹配算法的时间复杂度:O(n+m).

快速求next数组

  终于来到了我们最后一个问题——如何快速构建next数组。
  首先说一句:快速构建next数组,是KMP算法的精髓所在,核心思想是“P自己与自己做匹配”。
  为什么这样说呢?回顾next数组的完整定义:

  • 定义 “k-前缀” 为一个字符串的前 k 个字符; “k-后缀” 为一个字符串的后 k 个字符。k 必须小于字符串长度。
  • next[x] 定义为: P[0]P[x] 这一段字符串,使得k-前缀恰等于k-后缀的最大的k.
      这个定义中,不知不觉地就包含了一个匹配——前缀和后缀相等。接下来,我们考虑采用递推的方式求出next数组。如果next[0], next[1], … next[x-1]均已知,那么如何求出 next[x] 呢?
      来分情况讨论。首先,已经知道了 next[x-1](以下记为now),如果 P[x] 与 P[now] 一样,那最长相等前后缀的长度就可以扩展一位,很明显 next[x] = now + 1. 图示如下。

      刚刚解决了 P[x] = P[now] 的情况。那如果 P[x] 与 P[now] 不一样,又该怎么办?

      如图。长度为 now 的子串 A 和子串 B 是 P[0]
    P[x-1] 中最长的公共前后缀。可惜 A 右边的字符和 B 右边的那个字符不相等,next[x]不能改成 now+1 了。因此,我们应该缩短这个now,把它改成小一点的值,再来试试 P[x] 是否等于 P[now].
      now该缩小到多少呢?显然,我们不想让now缩小太多。因此我们决定,在保持“P[0]P[x-1]的now-前缀仍然等于now-后缀”的前提下,让这个新的now尽可能大一点。 P[0]P[x-1] 的公共前后缀,前缀一定落在串A里面、后缀一定落在串B里面。换句话讲:接下来now应该改成:使得 A的k-前缀等于B的k-后缀 的最大的k.
      您应该已经注意到了一个非常强的性质——串A和串B是相同的!B的后缀等于A的后缀!因此,使得A的k-前缀等于B的k-后缀的最大的k,其实就是串A的最长公共前后缀的长度 —— next[now-1]!

      来看上面的例子。当P[now]与P[x]不相等的时候,我们需要缩小now——把now变成next[now-1],直到P[now]=P[x]为止。P[now]=P[x]时,就可以直接向右扩展了。  代码实现如下:

      应用摊还分析,不难证明构建next数组的时间复杂度是O(m)的。至此,我们以O(n+m)的时间复杂度,实现了构建next数组、利用next数组进行字符串匹配。
      以上就是KMP算法。它于1977年被提出,全称 Knuth–Morris–Pratt 算法。让我们记住前辈们的名字:Donald Knuth(K), James H. Morris(M), Vaughan Pratt(P).

模板代码

  最后附上洛谷P3375 【模板】KMP字符串匹配 的Python和Java版代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Kmp {

//next[i]表示长度为n的多少前缀等于后缀
private int[] next;

private String pattern;

public Kmp(String pattern){
this.pattern=pattern;
buildNext(pattern);
}

private void buildNext(String pattern){
next=new int[pattern.length()];
next[0]=0;
int pos=0;
int i=1;
while (i<pattern.length()){
if(pattern.charAt(i)==pattern.charAt(pos)){
pos++;
next[i]=pos;
i++;
}else if(pos!=0){
pos=next[pos-1];
}else{
next[i]=pos;
i++;
}
}
}

public int search(String word){
int index=-1;
int j=0;
for(int i=0;i<word.length();i++){
while (j<pattern.length()){
if(word.charAt(i)==pattern.charAt(j)){
j++;
i++;
}else if(j==0){//字符不一样时
break;
}else{
j=next[j-1];
}
}
if(j==pattern.length()){
return i-pattern.length();
}
}
return index;
}
}

KMP算法在jdk中的源码应用

学完了这么牛逼的算法,将复杂度从$O(m*n)$降低到了$O(m+n)$,那么来看一下jdk源码String.indexOf和contains是使用kmp算法吗?

1
2
3
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}

contains直接调用indexOf方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}

char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);

for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}

/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);

if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}

indexOf方法采用的是非常朴素的Brute-Force暴力算法。

所以,为什么jdk没有采用kmp算法?

这就不清楚了,一种说法是短字符串的比较,开辟空间消耗的资源远远比计算的大,kmp更适用的场景是,固定的pattern,不断去比较新的字符串,复用next数组。

本文转自 如何更好地理解和掌握 KMP 算法?

Brute Force 算法

用于模式匹配算法

人话就是暴力算法,两层循环。

Brute Force 算法就是穷举 P 相对于 T 产生的所有可能性。

问题

很多人在各种平台开通会员时,经常会选择自动续费,连续包月,毕竟便宜啊。
但是设置后,过个个把月,就不怎么使用功能了,然而钱还是每月哗哗哗的扣款,心疼啊。
本文就描述微信2022新版如何取消自动扣款设置。

怎么取消自动扣款?

大家上网搜的基本都是旧版方案,比如这篇微信怎么样关闭自动扣款,旧版微信在【我的】界面确实有【支付这个选项】,然而在最新的2022版本中已经没有了。

没有了不要慌,这就来说新版怎么取消。

2022微信新版的操作界面在【我的-服务-钱包-支付设置-自动续费中】

1.进入微信,点击【我的】按钮

2.进入【我的】页面,点击【服务】按钮

3.进入【服务】页面,点击【钱包】按钮

4.进入【钱包】页面,点击【支付设置】按钮

5.进入【支付设置】页面,点击【自动续费】按钮

6.进入【自动续费】页面

该页面展示的是你自动续费的选项,选中你要取消的服务,进入详情页

6.进入【详情页】页面,点击【关闭扣费服务】按钮

前言

之前写过一篇ngrok+nginx实现windows远程桌面连接实现了在公司远程连接家里的电脑,最近发现另一个开源网络穿透frp似乎更优秀,其在git的star达到了61.4k,本篇就是用frp搭建网络穿透。

frp 是什么?


官方的解释为

frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports TCP and UDP, as well as HTTP and HTTPS protocols, where requests can be forwarded to internal services by domain name.
frp also has a P2P connect mode.

frp 是一种快速反向代理,可帮助您将 NAT 或防火墙后面的本地服务器暴露给 Internet。 目前,它支持 TCP 和 UDP,以及 HTTP 和 HTTPS 协议,可以通过域名将请求转发到内部服务。
frp 还有一个 P2P 连接模式。

简单来说,你个人本地web服务,通过与frp的服务建立隧道连接,然后别人就可以通过访问frp服务来访问你的本地服务了。

frp的git地址: https://github.com/fatedier/frp

frp官方文档: https://gofrp.org/docs/overview/

官方文档罗列了相关概念和操作,要是有能力尽量阅读官方文档。

frp 的下载

frp可以在release页面进行下载,当前的最新版本为0.045,客户端和服务端为同一个,只不过配置和启动命令不同,大家可以根据自己的操作系统进行下载,要是linux不知道下载那个那个文件,可以查看linux cpu 指令集架构 RISC / CISC | arm | amd | X86/i386 | aarch64这篇文章了解cpu对应的指令集架构下载对应的软件包。

frp 服务端

frp 服务端配置


frp的服务端配置文件为frp文件夹下的frps.ini文件,具体完整的配置可以参考frps_full.ini文件.

本人的一个配置为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 模块名
[common]
# 对frp客户端暴露的端口
bind_port = 7000
# 对外暴露的接口
vhost_http_port = 7099
# 服务端统计仪表盘端口
dashboard_port = 7091
# 仪表盘登录用户名
dashboard_user = admin
# 仪表盘登录密码
dashboard_pwd = admin
# 子域名,这里采用子域名模式
subdomain_host = frp.xxx.com

# 和客户端token链接
authentication_method=token
# 认证链接
token=test_token

frp 服务端启动

标准的启动命令为

1
/frp_path/frps -c /frp_path/frps.ini

frp 的部署

只使用命令,当我们推出linux shell后,服务就暂停了,这不是我们想要的结果,部署有两种方式,使用systemd及后天运行脚本。

systemd
systemd为官方推荐的部署方式,参考官方文档 使用 systemd

后台运行
我们也可以使用linux后台运行命令自己部署

1
nohup /frp_path/frps -c /frp_path/frps.ini &

frp 客户端

frp 客户端配置


frp的客户端配置文件为frp文件夹下的frpc.ini文件,具体完整的配置可以参考frpc_full.ini文件.

本人的一个配置为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# frpc.ini
# 对应上文服务器配置的模块名
[common]
server_addr = frp.xxx.com
server_port = 7000

# windows 远程桌面端口
[windows]
# 协议需要是tcp
type = tcp
# 本地的远程服务端口,默认为3389,处于安全考虑,建议修改远程默认端口
local_port = 3389
# frp server暴露连接的端口,远程桌面连接时需要输入端口,因此通过端口暴露,这里也建议修改端口
remote_port = 3389
# 和服务端认证链接
token=test_token

# http web 接口服务
[httpweb01]
# 协议
type = http
# 本地服务端口
local_port = 7102
# 子域名,由于我们服务器的域名subdomain_host配置为frp.xxx.com,因此这里暴露的域名为 web1.frp.xxx.com
subdomain = web1
# http basic auth 基础认证的用户名和密码,不然别人只要知道了你的url就可以访问了
http_user = easytrader
http_pwd = easytrader123
# 和服务端认证链接
token=test_token

# web02
[httpweb02]
type = http
local_port = 7103
subdomain = web2
http_user = easytrader
http_pwd = easytrader123
# 和服务端认证链接
token=test_token


frp 客户端启动

标准的启动命令为

1
./frpc -c ./frpc.ini

frp 的部署

linux 下推荐使用 systemd
windows下推荐自己编写.bat文件,然后配置为开机启动项,windows的开机启动配置可以参考win10 开机启动,无需登录

域名配置

登录你的域名管理,配置frp.xxx.com*.frp.xxx.com指向你的frp server地址

nginx 配置

一个简单的nginx配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name *.frp.xxx.com;

location / {
proxy_pass http://127.0.0.1:7000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

本质上就是当后续使用服务的时候,如访问者访问http://web1.frp.xxx.com 时,将请求转交给frp server处理。

frp 的使用情况

登录 http://server ip:7091 进行访问,端口为之前服务端的dashboard_port配置。

服务的使用

远程桌面连接

通过以下地址 frp.xxx.com:3389 进行连接

http接口或页面

如果是页面,直接访问 web1.frp.xxx.com ,输入用户名密码后即可进行访问

如果是程序调用,需要加上 Authorization 的header,具体可以参考文章 JAVA Http Basic auth

自问自答

frp的客户端和服务端通信安全如何保障?

参考身份认证 ,目前 frpc 和 frps 之间支持两种身份验证方式,token 和 oidc,默认为 token。

我暴露的服务如何保障安全

http相关的可以通过设置 BasicAuth 鉴权实现简单的认证。

复杂可以通过安全地暴露内网服务,这样服务只能被部署了visitor frp client的机器访问,缺点是需要部署frp cli。

完结

至此,使用frp搭建的http穿透及远程桌面连接已顺利实现。

前言

有时候我们希望一些常用程序在开机后就自动运行。
比如笔者就配置了一个远程登录结合frp网络穿透实现windows远程连接,那么就需要frp程序在开机时就运行。
那么如何配置呢?

开机启动配置

上网搜索开机启动,有非常多的答案,但大多方案都有一个弊端,需要登录后才会启动。
比如知乎的这篇Windows10 开机自启动设置 中给的【任务选项->启动】配置及【运行窗口->shell:startup】启动项选项两个方案,都是需要用户登录后才能生效的。

那么有没有不需要启动就能登录的?

有的,可以采用设置定时任务的方式实现。

win10 实现设置定时任务方法

1、在Windows10桌面,右键点击此电脑图标,在弹出菜单中选择“管理”菜单项。

2、然后在打开的计算机管理窗口中,找到“任务计划程序”菜单项。

3、右键,点击创建基本任务
这里创建任务由两种,一种为向导模式的【创建基本任务】,一种为直接模式【创建任务】,我们选择向导模式。

4、进入创建基本任务界面,填入名称和描述,本人的为frp启动,填完后点击下一步。

5、进入触发器选择界面,这里我选择的是计算机启动时,如果你有不同需求,按照自己需求选择。

6、进入操作选择界面,这里我们选择启动程序,因为我们的目标是运行程序。

7、点击下一步,点击界面上的浏览按钮,选择我们期望运行的程序,这里本人选择的是自己编写的start_frp.bat脚本。

8、最后点击完成完成配置。

9、回到之前的【管理->系统工具->任务执行计划->任务执行计划程序库】,此时就能看到自己创建的定时任务了。

此时大家可以重启电脑进行验证。

至此,配置完成