做人呢最重要的就是开心

问题

项目中导出 Excel 的时候,由于文件名设置的是中文字符串,在 Google 浏览器是正常的导出,文件名不会出现乱码,但是在 Edge/IE 浏览器下就会出现乱码的情况。Google 一下你会搜到很多答案,当然很多都是不靠谱的,最后问题是解决了。但是遇到这个问题的人并没有说明为什么会出现这个问题,究其根源,看博客的意思应该是从墙外找到的答案。

解决

解决这个很简单,但是需要做的就是去了解为什么要这样做,下面是解决办法。

1
2
3
4
5
6
7
8
9
10
11
function encodeFileName($filename) {
$ua = strtolower($_SERVER('HTTP_USER_AGENT'));

//判断是否为IE或Edge浏览器
if(preg_match('/msie/', $ua) || preg_match('/edge/', $ua)) {
//使用 urlencode 对文件名进行重新编码
return str_replace('+', '%20', urlencode($filename));
}

return $filename;
}

其实问题解决是很简单的,使用urlencode函数就可以了。下面慢慢道来。

这篇文章主要记录 Nginx 一些学习和笔记。还有遇到的问题。

nginx.conf 文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
... # 全局模块

# events 模块
events {
....
}

# http块
http {
# http 全局块
...
# server 块
server
{
# server 全局块
...
# location 块
location [PATTERN]
{
...
}
}
}

由上面以大括号为分割 Nginx.conf 可分为三大块全局块, events, http。其中 http 模块里面有分为 全局块, server 两个块。server 可以有多个。server 里面又分为 全局location 两个模块。location 也是可以有多个。在 Nginx 使用方面,大多数都体现在配置文件。应用方面也体现在配置了,当然 Nginx 配置是很丰富多样的。

全局模块

全局模块是默认配置文件开始到 events 这一块内容,主要设置一些 Nginx 运行指令,通常包括 Nginx 运行的用户/组,worker proccess 数,Master 进程 PID 存储路径,日志的存放路径以及配置文件的引入等等。

events 模块

events 模块主要是 Nginx 与用户链接的设置。包括设置事件驱动模型和每个 worker proccess 允许的最大链接数目,这部分设置对 Nginx 的性能影响很大,主要体现事件驱动上。

Http 模块

Http 模块是 Nginx 服务的重要部分,代理,缓存和日志等绝大数功能和第三方模块都是放在这个模块中。

Http全局块

Http 全局块中配置的指令包括文件引入,MIME-Type 定义,日志自定义,是否使用 sendfile 传输文件,链接超时时间,但链接请求上限等。

Server 模块

Http 块中可以包含多个 Server 模块,每个 Server 模块对应着一台虚拟机,可以为多台主机提供服务,注意这里是一组逻辑上关系。

Server 全局模块

Server 全局模块提供一些基础功能, 包括虚拟机的监听配置和配置虚拟机的名称或者 IP 地址。

Location 模块

Server 模块的大多数功能都在 Location 中实现,其实整个 Nginx 配置重要的配置都在这块,location 主要对 Nginx 的接收的 uri 进行匹配,对特定的请求进行处理,地址定向,数据缓存和应答等控制功能都是在这部分实现,第三方模块也提供了这块的功能

时序图

用流程图表示可能更加明显,来看一下

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
graph LR;
Nginx.conf-->id1{全局模块};
Nginx.conf-->id2{Event模块};
Nginx.conf-->id3{Http模块};
id1{全局模块}-->运行用户组;
id1{全局模块}-->workerProccess数目;
id1{全局模块}-->MasterPID存储路径;
id1{全局模块}-->日志存放路径;
id1{全局模块}-->配置文件引入;
id2{Event模块}-->事件驱动模型;
id2{Event模块}-->允许最大链接数目;
id3{Http模块}-->Http全局模块;
Http全局模块-->文件引入;
Http全局模块-->MIME-Type定义;
Http全局模块-->自定义日志;
Http全局模块-->sendfile传输文件;
Http全局模块-->链接超时;
Http全局模块-->链接请求上限;
id3{Http模块}-->Server模块;
Server模块-->Server全局模块;
Server全局模块-->虚拟机的监听;
Server全局模块-->配置虚拟机名称或者IP地址;
Server模块-->Location模块;
Location模块-->请求处理;
Location模块-->地址定向;
Location模块-->数据缓存;
Location模块-->请求应答;
Location模块-->反向代理;

问题一

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

解决

上来不由分说,先来一个 For 再说😄

1
2
3
4
5
6
7
8
9
10
11
12
func sumtotarget(nums []int, target int) []int {
l := len(nums)
for i, v := range nums {
r := target - v
for j:=i+1; j < l; j++ {
if r == nums[j] {
return []int{i, j}
}
}
}
return []int{}
}

完美成功运行了,哈哈。想想这样不行啊 O(n^2)。然后仔细想了一下,可以利用 map 的特性来处理,就有下面这一段代码。

1
2
3
4
5
6
7
8
9
10
11
12
func sumtotarget(nums []int, target int) []int {
numbers := make(map[int]int)
for i, v := range nums {
j, ok := numbers[v]
numbers[target - v] = i
if ok {
return []int{j, i}
}
}

return []int{}
}

第一刷,简单题都扛不住了。只能慢慢来了

问题二

Given a 32-bit signed integer, reverse digits of an integer.

Example 1:

Input: 123
Output: 321

Example 2:

Input: -123
Output: -321

Example 3:

Input: 120
Output: 21

Note:
Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

解决

注意的就是 int32 溢出的问题了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func reverse(x int) int {
var num int
for {
n := x % 10
if x == 0 && n == 0 {
break
}
x = x / 10
num = num * 10 + n
}
// -2147483648 到 2147483647
if num > math.MaxInt32 || num < math.MinInt32 {
return 0
}
return num
}

问题三

Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.

Example 1:

Input: 121
Output: true

Example 2:

Input: -121
Output: false

Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.
Example 3:

Input: 10
Output: false

Explanation: Reads 01 from right to left. Therefore it is not a palindrome.

解决

这道题数字回文,其实和上面的反转是一个道理,就是比较反转后的数字和实参是否相等就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func isPalindrome(x int) bool {
if x < 0 {
return false
}
var num int
cache := x
for {
n := x % 10

if x == 0 && n == 0 {
break
}
x = x / 10
num = num * 10 + n
}
if cache == num {
return true
}

return false
}

问题

博客最近切换到了 hexo 博客, 目前用起来感觉很方便。很喜欢 markdown 写作。当然利用 Github Pages 也省下了一笔费用,某套路云的套路令人感到恶心。这里就不多说了。为什么要换评论呢?因为我使用的 next 主题的评论都不太合我的心意,Disqus 很好,但是广告太多,其他仿 git 的评论总觉得别扭,于是我也翻了找找看,发现了 utterance,觉得挺不错的,简洁风格也不缺失。所以就打算换这个了。

解决

hexo 的主题改起来还是很方便的,从配置开始,然后添加一小段的模块代码就可以了。标签也可以仿照其他文件,So Easy!开始动手吧。在这之前需要到 utterances app 申请一下,这样 utterances 才有权限访问你的仓库。

添加配置

找到 Next 主题的配置文件 _config.yml,配置随便你添加到哪里。你可以放在文件末尾或者可以和其他评论模块配置放在一块。

1
2
3
4
5
utterance:
enable: true
repo: #仓库名字
theme: #主题
issue-term: #映射配置

评论主题的 theme 选项如下:

  • github-light
  • github-dark
  • github-dark-orange
  • icy-dark
  • dark-blue
  • photon-dark

评论 issue-term 映射配置选项如下:

  • pathname
  • url
  • title
  • og:title
  • issue-number
  • specific-term

引入 Utterance 插件

_third-party\comments 文件夹下创建 utterance.swig 文件。写入代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/javascript">
(function() {
// 匿名函数,防止污染全局变量
var utterances = document.createElement('script');
utterances.type = 'text/javascript';
utterances.async = true;
utterances.setAttribute('issue-term','{{ theme.utterance.issue-item }}')
utterances.setAttribute('theme','{{ themm.utterance.theme }}')
utterances.setAttribute('repo','{{ theme.utterance.repo }}')
utterances.crossorigin = 'anonymous';
utterances.src = 'https://utteranc.es/client.js';
// content 是要插入评论的地方
document.getElementById('gitment-container').appendChild(utterances);
})();
</script>

增加 comment 样式

找到 _partials\comments.swig 然后增加下面代码:

1
2
3
4
{% elif theme.utterance.enable %}
<div class="comments" id="comments">
<div id="gitment-container"></div>
</div>

还需要在 _third-party\comments\index.swig 添加如下代码

1
2
{% elif theme.utterance.enable %}
{% include 'utterance.swig' %}

以上就是整个配置过程。如果有任何问题可以在评论区留言。

问题

公司需要将老项目迁移, 由于之前的错误返回都是不规范的, 对于错误处理都是直接返回 200 http code。大量的使用这样的返回。

1
response()->json(200, ['msg'=>$msg])

已经没有办法更改了, 客户端都是根据接口的的业务代码 code 来判断的。但是在新的项目中严格规范使用异常抛出, 但是异常抛出后 http code 返回的是 500。导致测试通过不了, 没有办法只能修改。

解决

使用了异常抛出业务错误, 在 laravel 框架中, 异常都是统一处理的, 在 App\Exceptions\Handle 里面进行了全局处理。对于框架抛出的异常, 全部由它来处理。但是当你想要在 Handle 里面修改处理的时候, 你会发现并不会有任何效果。到底什么原因导致的呢?

在入口文件可以看到这么一段代码

1
2
3
4
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

在这里进行了全局的异常服务注册, 这里先不关注异常在哪里处理的。既然实际是没有通过 Hanle 类处理, 猜测这里服务可能被 Dingo 重新注册过了。带着这个想法可以看一下 Dingo 的服务注册。在 Dingo\Api\Provider\DingoServiceProvider 看到了异常注册

1
2
3
4
5
6
protected function registerExceptionHandler()
{
$this->app->singleton('api.exception', function ($app) {
return new ExceptionHandler($app['Illuminate\Contracts\Debug\ExceptionHandler'], $this->config('errorFormat'), $this->config('debug'));
});
}

这里肯定会有疑问了, 这个单例模式也没有重新注册 Illuminate\Contracts\Debug\ExceptionHandler::class 这个类啊?没错, 因为 Dingo 接管了路由, 你实际使用的 \Dingo\Api\Routing\Router::class, 所以 Dispatch 的会由 Dingo 来执行。这里就不细说了。有兴趣的可以看一下 Dingo 的处理。
Dispatch 处理可以在 Dingo\Api\Dispatcher里面找到。来看一下代码处理。

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
protected function dispatch(InternalRequest $request)
{
$this->routeStack[] = $this->router->getCurrentRoute();

$this->clearCachedFacadeInstance();

try {
$this->container->instance('request', $request);

$response = $this->router->dispatch($request);

if (! $response->isSuccessful() && ! $response->isRedirection()) {
throw new InternalHttpException($response);
} elseif (! $this->raw) {
$response = $response->getOriginalContent();
}
// 主要看这里的 exception 处理 这里就是接管的异常
} catch (HttpExceptionInterface $exception) {
$this->refreshRequestStack();

throw $exception;
}

$this->refreshRequestStack();

return $response;
}

先不用管这个接口类的异常, 来看看 Dingo 的 Handle 如何处理的。下面会提到处理为何这里是接口, 还有该如何修改。

你需要从 render 方法来是查找, 你最终会看到, 过程就不细讲了。

1
2
3
4
protected function getExceptionStatusCode(Exception $exception, $defaultStatusCode = 500)
{
return ($exception instanceof HttpExceptionInterface) ? $exception->getStatusCode() : $defaultStatusCode;
}

这里就和上面的接口类对应了, 为什么返回 500 呢?看这段代码已经一目了然了。如果不是实现了 HttpExceptionInterface 的异常类, StatusCode 都是返回 500。所以需要做的就很简单了, 实现 HttpExceptionInterface 就可以了。简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class BaseException extends \Exception  implements HttpExceptionInterface
{

public function getStatusCode()
{
return 200;
}

/**
* Returns response headers.
*
* @return array Response headers
*/
public function getHeaders()
{
return [];
}

这是一个 exception 基类, 需要的就是继承就可以了。这样就可以解决该问题了。

在使用 Laravel 框架的时候, 开发第三方服务的时候, 由于是本地开发, 所以 Https 回调的时候回出现这样的错误。那么该如何解决呢? 具体信息如下

cURL error 60: SSL certificate problem: unable to get local issuer certificate (see http: curl.haxx.se libcurl c libcurl errors.html)

为什么会出现这种错误?

由于敏感信息的传输通常在数字证书下进行。 该证书将有助于向收件人确认发件人实际上是他们声称的人。 数字证书由证书机构颁发。

当数字证书应用于服务器时,服务器上会安装可信证书颁发机构及其根证书的列表。 通过常规 HTTPS 进行的事务将恢复到此列表以进行通信。 但是,CURL 不遵守规则。 您需要告诉 Curl 有关 Ca 根证书的信息。

如何解决?

要解决该错误,您需要定义 CURL 证书颁发机构信息路径
要做到这一点,在此处下载最新的 curl 认可证书,
cacert.pem 文件保存在可到达的目标中。
然后,在 php.ini 文件中,向下滚动到找到 [curl] 的位置。
您应该看到注释掉了 CURLOPT_CAINFO 选项。 取消注释并将其指向 cacert.pem 文件。 你应该有这样的一行:

1
curl.cainfo =“证书路径\cacert.pem”

保存并关闭 php.ini。 重新启动您的网络服务器并再次尝试您的请求

如果未设置正确的位置,则会出现 CURL 77 错误。

安装 man 中文手册

在使用 mac 或者 linux 的时候,需要用到命令,而大量的命令含有大量 options,一般很难记住,使用 man 可以查看这些命令的 options,但对于英语薄弱的人来说,例如我,看起来还是有一定的困难,所以需要安装以下中文手册。
准备前,我们需要安装两个 package,前提你是使用了 brew 管理

1
2
> brew install automake
> brew install opencc

然后需要下载中文包。

1
2
3
4
5
6
> git clone  https://github.com/man-pages-zh/manpages-zh
> cd manpages-zh
> autoreconf --install --force
> ./configure
> sudo make
> sudo make install

等待编译完成之后,我们还需要配置一些东西。如果你之前没有更新 groff,需要先更新 groff,不然会出现乱码。

1
2
3
4
5
> brew install groff 
> echo "MANPATH /usr/local/share/man/zh_CN" >> /etc/man.conf
> echo "NROFF preconv -e UTF8 | /usr/local/bin/nroff -Tutf8 -mandoc -c" >> /etc/man.conf
> export alias cman='man -M /usr/local/share/man/zh_CN' // 你可以加入到环境变量里
> cman ls

这样就可以愉快的使用 man 中文手册啦

Linux 文件权限

在深入了解之前,先来理解一下文件权限的基本信息。首先创建一个信息的文件

1
2
3
> touch new_file
> ll new_file
> -rw-r--r-- 1 root root 0 4月 23 22:59 new_file

文件 new_file 拥有下面三组权限:

rw- :文件的属主,登陆用户的权限 r w 分别是读写

r– : 文件属主的所属组的权限 r 读

r–: 其他用户的权限 r 读

当然还有一个 x 可执行权限这里没有,可以通过 chmod 命令来增加。这个后面再说。我们先来说说文件权限的由来,touch 命令之后的默认权限是如何来的。要知道首先要知道有 umask 的存在。通过 umask 命令可以获取默认值。这个值很有用。

1
2
> umask
> 0022 // 这是我机器上面的值

要了解这个 umask 的是如何工作的,首先要理解一下八进制模式的安全性设置。八进制模式的安全设置先获取这三个 rwx 权限的值,然后将其转换为三位二进制值,用一个八进制值来表示。在这个二进制表示中,每个位置代表一个二进制位。例如唯一读权限 r– 转换成二进制就是 100, 那么代表的八进制就是 4。
如下表表示的这样:

权限 二进制 八进制 描述
000 0 没有任何权限
–x 0001 1 只有可执行权限
-w- 010 2 只有可写权限
-wx 011 3 只有可写可执行权限
r– 100 4 只有可读权限
r-x 101 5 只有可读可执行权限
rw- 110 6 只有可读可写权限
rwx 111 7 可读可写权限可执行

当了解对应权限以及八进制值之后,看看上面的 new_file 拥有权限是 644。那么这里有点困惑了,这个值是如何得来的呢。 umask0022 有什么意义呢?其实这个值只是个掩码。他会屏蔽掉不像授予该安全级别的权限。对于文件来说,全权限是 666,减去 umask 的值就是新创建文件的权限。 644 正是我们所期望的那样,默认屏蔽了文件所属组和其他用户 w 权限。这里说明一下文件夹的全权限是 777。如果你想屏蔽更多的权限,可以修改 umask 的值。

1
> umask 026

我们使用 ssh 链接 linux 主机时,可能出现 “Host key verification failed.“ 的提示,ssh 连接不成功。
可能的提示信息如下:

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
23:00:20:83:de:02:95:f1:e3:34:be:57:3f:cf:2c:e7.
Please contact your system administrator.
Add correct host key in /home/xahria/.ssh/known_hosts to get rid of this message.
Offending key in /home/xahria/.ssh/known_hosts:8
RSA host key for localhost has changed and you have requested strict checking.
Host key verification failed.

网上很多的解决方案是:vi ~/.ssh/known_hosts 删除与想要连接的主机相关的行;或者直接删除known_hosts 这个文件。 当然这个方案也是可行的,但并非解决问题的根本办法,因为继续使用,今后还会出现这样的情况,还得再删除。
下面简单讲一下这个问题的原理和比较长久的解决方案。
OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在 ~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH 会核对公钥。如果公钥不同,OpenSSH会发出警告,避免你受到DNS Hijack之类的攻击。
SSH 对主机的 public_key 的检查等级是根据 StrictHostKeyChecking 变量来配置的。默认情况下,StrictHostKeyChecking=ask。简单所下它的三种配置值:

1.StrictHostKeyChecking=no最不安全的级别,当然也没有那么多烦人的提示了,相对安全的内网测试时建议使用。如果连接server的 key 在本地不存在,那么就自动添加到文件中(默认是known_hosts),并且给出一个警告。

2.StrictHostKeyChecking=ask 默认的级别,就是出现刚才的提示了。如果连接和key不匹配,给出提示,并拒绝登录。

3.StrictHostKeyChecking=yes 最安全的级别,如果连接与key不匹配,就拒绝连接,不会提示详细信息。

对于我来说,在内网的进行的一些测试,为了方便,选择最低的安全级别。在 .ssh/config(或者 /etc/ssh/ssh_config )中配置:

1
2
 StrictHostKeyChecking no
UserKnownHostsFile /dev/null

(注:这里为了简便,将 knownhostfile 设为 /dev/null,就不保存在 known_hosts 中了)