练习
花括号展开2
如果你熟悉 Shell 编程,那么一定了解过花括号展开,它可以用来生成任意字符串。
花括号展开的表达式可以看作一个由 花括号、逗号 和 小写英文字母 组成的字符串,定义下面几条语法规则:
如果只给出单一的元素 x,那么表达式表示的字符串就只有 "x"。R(x) = {x}
例如,表达式 "a" 表示字符串 "a"。
而表达式 "w" 就表示字符串 "w"。
当两个或多个表达式并列,以逗号分隔,我们取这些表达式中元素的并集。R({e_1,e_2,...}) = R(e_1) ∪ R(e_2) ∪ ...
例如,表达式 "{a,b,c}" 表示字符串 "a","b","c"。
而表达式 "{{a,b},{b,c}}" 也可以表示字符串 "a","b","c"。
要是两个或多个表达式相接,中间没有隔开时,我们从这些表达式中各取一个元素依次连接形成字符串。R(e_1 + e_2) = {a + b for (a, b) in R(e_1) × R(e_2)}
例如,表达式 "{a,b}{c,d}" 表示字符串 "ac","ad","bc","bd"。
表达式之间允许嵌套,单一元素与表达式的连接也是允许的。
例如,表达式 "a{b,c,d}" 表示字符串 "ab","ac","ad"。
例如,表达式 "a{b,c}{d,e}f{g,h}" 可以表示字符串 "abdfg", "abdfh", "abefg", "abefh", "acdfg", "acdfh", "acefg", "acefh"。
给出表示基于给定语法规则的表达式 expression,返回它所表示的所有字符串组成的有序列表。
假如你希望以「集合」的概念了解此题,也可以通过点击 “显示英文描述” 获取详情。
示例 1:
输入:expression = "{a,b}{c,{d,e}}"
输出:["ac","ad","ae","bc","bd","be"]
示例 2:
输入:expression = "{{a,z},a{b,c},{ab,z}}"
输出:["a","ab","ac","z"]
解释:输出中 不应 出现重复的组合结果。
提示:
1 <= expression.length <= 60
expression[i] 由 '{','}',',' 或小写英文字母组成
给出的表达式 expression 用以表示一组基于题目描述中语法构造的字符串
代码
class Solution {
private TreeSet<String> s = new TreeSet<>();//去重 compareTo()方法
public List<String> braceExpansionII(String expression) {
dfs(expression);//递归
return new ArrayList<>(s);//s返回新的数组 按照添加的先后顺序排列
}
private void dfs(String exp) {
//如果j找不到 证明exp是单一元素(不是花括号)
int j = exp.indexOf('}');//从左找右边的花括号的指针
if (j == -1) {//如果j没有找到} 会返回下标-1
//没有花括号了,添加到数组里
s.add(exp);
return;
}
int i = j;//当j有值时 需要找左边的花括号
while (exp.charAt(i) != '{') {//一直找到字符串为{的下标
--i;//减到下标在{的位置
}
String a = exp.substring(0, i);//就是左括号左边的前缀
String c = exp.substring(j + 1);//右括号右边的后缀
for (String b : exp.substring(i + 1, j).split(",")) {//exp.substring[i+1:j] 为 exp 中花括号内的部分
//在上面的范围里 以,分割线 去遍历每个符合要求的数组
dfs(a + b + c);//就是前缀加上遍历的中间内容加上后缀,去除了花括号
//以分割的数组组成新的字符串,再次递归
}
}
}
//以下面的式子举例
//输入:expression = "{a,b}{c,{d,e}}"
//输出:["ac","ad","ae","bc","bd","be"]
// 第一次递归 遍历出 "a{c,{d,e}}" "b{c,{d,e}}"
// 第二次递归 遍历出 "a{c,d}" "a{c,e}" "b{c,d}" "b{c,e}"
// 第三次递归 遍历出 "ac" "ad" "ac" "ae" "bc" "bd" "bc" "be"
//因为treeset 判断字符串是靠compareTo()方法 单线程情况下不能存储相同元素
//最后出来["ac","ad","ae","bc","bd","be"]
//HashSet根据对象的hashCode()和equals()方法去重
//TreeSet根据对象实现的compareTo()方法去重
遇见困难题不要怕,直接面向题解,好习惯养成日常.....
读了一会题我就懵了,又是花括号又是要计算括号内外的拼接 还不能重复
瞬间脑子里面就蹦出来if判断跟split和指针 接着就懵在原地了
看了一眼官方的,真TM长
然后发现一名字母大神,写的这叫一个通俗易懂,递归无敌
基本注释补充的都在上面的代码块里 在代码块里面写了就不在下面写了
简单描述思路就是
以,分割元素变成数组 然后判断左右花括号外所剩的字符串,拼接后再次递归判断
直到找不到花括号为止 以treeset去除重复字符串 再返回arraylist(顺序添加就顺序返回)
八股
ThreadLocal 是什么?有哪些使用场景?
线程局部变量是局限于线程内部的变量,不在多线程共享。而java提供的threadlocal
支持线程局部变量是一种实现线程安全的方式
说一下 synchronized 底层实现原理?
synchronized串行化,能保证代码运行时同一时刻只有一个方法进入,同时保证内存可见性
同步普通方法 锁当前实例对象
静态同步方法 锁当前class对象
同步方法块 锁是方法块对象
synchronized 和 volatile 的区别是什么?
前者是锁定当前变量,可以用在方法,类,变量上,且保证变量的可见性和原子性,
可能会造成线程阻塞,但也可以被编译器优化
后者是要去主存读取,只能用在变量上,不保证变量的原子性,保证变量的可见性
不会造成线程阻塞,但也不能被编译器优化
synchronized 和 Lock 有什么区别?
前者是java内关键字,无法判断锁状态,但是会自动释放锁,但是发生抢夺共享资源时,可能会造成死锁,且锁可重入不可中断,非公平
后者是java类,可以判断锁状态,但要手动释放锁,发生抢夺共享资源时不一定会一直等待造成死锁,且可重入可中断,公平
nginx第五天--初级篇完结
动静分离
让本来在tomcat代理的静态资源扔到nginx里面
配置反向代理和location
location / {
proxy_pass http://127.0.0.1:8080;
}
location /css {
root /usr/local/nginx/static;在root用户下的/css
index index.html index.htm;页面
}
location /images {
root /usr/local/nginx/static;
index index.html index.htm;
}
location /js {
root /usr/local/nginx/static;
index index.html index.htm;
}
使用正则
location 前缀
/ 通用匹配,任何请求都会匹配到。
= 精准匹配,不是以指定模式开头
~ 正则匹配,区分大小写
~* 正则匹配,不区分大小写
^~ 非正则匹配,匹配以指定模式开头的location
location匹配顺序
多个正则location直接按书写顺序匹配,成功后就不会继续往后面匹配
普通(非正则)location会一直往下,直到找到匹配度最高的(最大前缀匹配)
当普通location与正则location同时存在,如果正则匹配成功,则不会再执行普通匹配
所有类型location存在时,“=”匹配 > “^~”匹配 > 正则匹配 > 普通(最大前缀匹配)
使用正则
location ~*/(css|img|js) {
root /usr/local/nginx/static;
index index.html index.htm;
}
alias与root
location /css {
alias /usr/local/nginx/static/css;
index index.html index.htm;
}
root用来设置根目录,而alias在接受请求的时候在路径上不会加上location。
1)alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alias目录下查找的; 2)root指定
的目录是location匹配访问的path目录的上一级目录,这个path目录一定要是真实存在root指定目录下的; 3)使用
alias标签的目录块中不能使用rewrite的break(具体原因不明);另外,alias指定的目录后面必须要加上"/"符
号!! 4)alias虚拟目录配置中,location匹配的path目录如果后面不带"/",那么访问的url地址中这个path目录后
面加不加"/"不影响访问,访问时它会自动加上"/"; 但是如果location匹配的path目录后面加上"/",那么访问的url地
址中这个path目录必须要加上"/",访问时它不会自动加上"/"。如果不加上"/",访问就会失败! 5)root目录配置
中,location匹配的path目录后面带不带"/",都不会影响访问
URLRewrite
rewrite语法格式及参数语法:
同样在server location
rewrite是实现URL重写的关键指令,根据regex (正则表达式)部分内容,
重定向到replacement,结尾是flag标记。
rewrite <regex> <replacement> [flag];
关键字 正则 替代内容 flag标记
rewrite ^2.html$ /index.jsp?pageNum=2 break;
就是访问2.html 就去转第2页 详细看最后实例
关键字:其中关键字error_log不能改变
正则:perl兼容正则表达式语句进行规则匹配
替代内容:将正则匹配的内容替换成replacement
flag标记:rewrite支持的flag标记
rewrite参数的标签段位置:
server,location,if
flag标记说明:
last #本条规则匹配完成后,继续向下匹配新的location URI规则
break #本条规则匹配完成即终止,不再匹配后面的任何规则
redirect #返回302临时重定向,浏览器地址会显示跳转后的URL地址(重定向到真实地址)
permanent #返回301永久重定向,浏览器地址栏会显示跳转后的URL地址(给爬虫看的)
实例
rewrite ^/([0-9]+).html$ /index.jsp?pageNum=$1 break;
【0-9】页+可以右好多位 $1 第一个匹配的规则 如果规则很多 就$2..3..4..
同时使用负载均衡
就相当于把第一台nginx当作网关了 用来处理后面的应用服务器
无法直接访问后端的应用服务器,但可以用nginx反向代理访问
例如在四号nginx添加了防火墙 这时候让1号nginx去反向代理4号
应用服务器防火墙配置
开启防火墙
systemctl start firewalld
重启防火墙
systemctl restart firewalld
重载规则
firewall-cmd --reload
查看已配置规则
firewall-cmd --list-all
指定端口和ip访问
firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.44.101"
port protocol="tcp" port="8080" accept"
移除规则
firewall-cmd --permanent --remove-rich-rule="rule family="ipv4" source
address="192.168.44.101" port port="8080" protocol="tcp" accept"
网关配置
upstream httpds {
server 192.168.44.102 weight=8 down;
server 192.168.44.103:8080 weight=2;
server 192.168.44.104:8080 weight=1 backup;
}
location / {
rewrite ^/([0-9]+).html$ /index.jsp?pageNum=$1 redirect;
proxy_pass http://httpds ;
}
老师的图片
防盗链
http协议中的referer
通过网页的referer判断跟自己的站点跟原始站点是不是一个站点 如果不是就是非法请求
就是非法引用资源
防盗链配置
valid_referers none | blocked | server_names | strings ....;
none, 检测 Referer 头域不存在的情况。
blocked,检测 Referer 头域的值被防火墙或者代理服务器删除或伪装的情况。这种情况该头域的值不以
“http://” 或 “https://” 开头。
server_names ,设置一个或多个 URL ,检测 Referer 头域的值是否是这些 URL 中的某一个。
在需要防盗链的location中配置
valid_referers 192.168.44.101; 允许这个地址
if ($invalid_referer) {如果不是上面的地址
return 403;就返回403
}
第一台nginx防盗链配置
使用浏览器或者curl检测
先装上curl
在nginx里面
yum install -y curl
使用curl测试
curl -I http://192.168.44.101/img/logo.png 直接去访问这个地址图片
带引用
curl -e "http://baidu.com" -I http://192.168.44.101/img/logo.png
测试结果
none的情况下 没有referer能访问 有就不行
返回错误码
返回错误页面
老师的图
整合rewrite返回报错图片
匹配所有/(盗链的地址) 返回/img/x.png图片
高可用场景及解决方案
如果主机挂了 需要一个备用机
有一个keepalive软件 解决nginx之间 看它们之间是不是挂掉了
安装Keepalived
编译安装
方式1
下载地址
https://www.keepalived.org/download.html#
使用 ./configure 编译安装
如遇报错提示
configure: error:
!!! OpenSSL is not properly installed on your system. !!!
!!! Can not include OpenSSL headers files. !!!
方式2
安装依赖
yum install openssl-devel
yum安装
yum install keepalived
配置
使用yum安装后配置文件在
/etc/keepalived/keepalived.conf
最小配置
第一台机器
! Configuration File for keepalived
global_defs {
router_id lb101
}
vrrp_instance atguigu {//实例名称
state MASTER //当前机器名字(自己起的)
interface ens33 //要跟网卡ens33对应上
virtual_router_id 51
priority 100 //优先级
advert_int 1 //间隔检测时间
authentication {//和另一个机器配对(分组认证)
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {//虚拟IP地址 也就是图里面的VIP 虚拟的
192.168.44.200
}
}
第二台机器
! Configuration File for keepalived
global_defs {
router_id lb100
}
vrrp_instance atguigu {//与上面相对应
state BACKUP
interface ens33//与上面相对应
virtual_router_id 51//与上面相对应
priority 50
advert_int 1//与上面相对应
authentication {//与上面相对应
auth_type PASS//与上面相对应
auth_pass 1111//与上面相对应
}
virtual_ipaddress {//与上面相对应
192.168.44.200//与上面相对应
}
}
启动服务
systemctl start keepalived
之后就是访问虚拟IP 也可以PING
192.168.44.200
如果nginx挂了 但是keepalived没挂 就又问题了
所有需要写一个脚本去监听nginx这个是否报错(redis也一样)
脚本在nginx本机跑着 与keepalived没关系
当nignx宕机了 脚本就会kill进程 让keepalived也挂了
就是Master会和Slave通信,Slave会和本机的Nginx通信,这样如果Master和Slave是正常的,但是Nginx跪了,是不会切换的。因为Nginx跪了,Master感知不到
Https证书配置
不安全的http协议
http可以被很多监听器监听到或拦截到或劫持到
所以需要安全协议TCP/IP 去 让客户端和服务器 遵守
(对称加密-内置到服务器)
而数据就会被加密(例如凯撒加密算法) 服务器在收到就会将密文转成明文
服务器再通过相同算法返回密文
(不安全 因为nginx必须是公开开源的)
(客户端也是需要内置)
(非对称加密)
客户端发送给服务器时同时发送一个密匙 让服务器去解密客户端发的数据
(也不安全 同样密钥也容易被劫持)
用户在请求时先不走80端口,先去服务器443获得server公钥
再将明文通过公钥+算法 得到密文发送
服务器拿私钥+算法 得到明文 再返回客户端
例子
A如果想向B发送消息,就会先从B哪里获取B的公钥,A再用获取的公钥加密消息,B收到消息后再用私钥解密,用公钥加密的消息,只有私钥才能解密。私钥一直在服务器,这样私钥就不存在因为传递而泄露的风险
还要满足
公钥加密 公钥解不开 私钥不在互联网传递
例如 有些人拦截客户端的请求后
直接伪造请求去服务器去要公钥 然后把假的公钥给客户端 让客户端通过假公钥发给拦截者
这样,客户端与拦截者建立了安全连接 拦截者与服务器建立了安全连接
拦截者就干了客户端的事情(黑中介--外挂理论)
CA 签名(第三方机构)
对公钥认证- 去看这个网站是不是你的 你是不是这个网站的所有者(管理权限)
操作系统内置了ca系统的公钥 CA认证的公钥就会颁发证书 虽然证书也可以被拦截
但是本身操作系统内置CA签名了 所以会被拦截后的证书回来会提示证书不安全
因为证书是ca颁发的 如果证人是假的,公钥也是假的 那就不可信了
(别人解开也没用 里面只是放的公钥而已 你也不能篡改 篡改证书就失效了)
(中间人使用CA公钥将数字证书解开之后,拿出数据并篡改,由于拿不到CA的私钥,他只能使用别的方式进行加密这个数据,但是当客户端拿到这个经过中间人加密的数据后,再使用CA的公钥去解密不出来)
(服务器有服务器的公钥,服务器的私钥,ca私钥,即认证你有权限后,将服务器的公钥加密成证书,发送给用户,用户跟坏蛋都可以拿到服务器公钥,用户在拿服务器公钥进行加密,最后有服务器用私钥进行解密)
(重要的不是CA,而是取消了请求公钥这个流程。将公钥进行内置,相当于只有公钥可能很多的烦恼的对称加密。但没有泄露私钥的风险。)
所以用的https
自签名
OpenSSL
openssl包含:SSL协议库、应用程序以及密码算法库
系统内置
图形化工具 XCA
下载地址
https://www.hohnstaedt.de/xca/index.php/download
线上实战
购买域名流程
阿里云 or腾讯云
产品服务--域名--注册域名--结算--审核等一等就正常了
购买vps
买一台云服务器ecs--创建...然后...配置一下...下单吧
控制台修改vps密码
在实例里面的操作里面 可以重置设置密码
就会获得IP地址 ,可以通过shell去远程连接公网
安装LNMP环境防火墙配置
然后去shell的公网地址安装命令
安装完毕后会告诉你各个文件在哪里
去外网IP访问失败
去防火墙(阿里云是网络与安全里面的安全组-安全组规则)
入方向,去开一个端口 80 + 443
去外网IP访问成功
到网页了
修改nginx默认页
学了五天了,还不会自己配吗
就是去conf里面改root地址
解析域名到主机
去阿里云域名解析-解析设置
再来个泛解析
可以去ping自己的域名网址
然后去html改自己的网页
(域名解析和网站空间解析是两个,域名如果买的大陆的必须要先备案才能买,然后网站空间也需要备案,域名备案快方便,网站的空间的就麻烦了,需要提供什么客户表之类的东西。否则网站空间提供商是不允许域名访问的)
在线申请证书
有很多证书机构,以阿里云为例
搜索数字证书管理服务
ssl证书 建议来个免费的 小企业也够用了 每年20个
证书申请
RSA非加密算法
把申请的证书配置到NGINX上
把下载的证书放到服务器里
在conf里面加入上面的
server_name 是自己写的域名localhost 就是自己域名主机
下面俩ssl就是要写的
相对路径或者绝对路径 嫌烦就粘贴到一个文件夹里面
之后添加https:// 就行了 https://www
安装Discuz与协议自动跳转--还有很多例如tupecho、wordpress
解压缩后 去网页安装
访问http协议的80端口 会显示安装网页
如果是想要证书443+ssl的 就把下面的信息拿过来
进去后
就改权限就是了 练习就全改777 但是公司不是
chmod -R 777 目录 递归目录权限
生产环境里面一般目录所有者改成nginx
然后安装完成
发现
太常见了 基本上论坛都是这样的
初级篇完结了
高级篇暂时不看了 我不知道别人是不是这样,我反正是看了就忘,学了就忘,大不了
用的时候再学,这种东西长时间不用,肯定会忘的,docker就是这样,linux在没学
redis时,还没见到docker 我就已经忘光了,也就现在学中间件老是用,才慢慢熟练