0%

只要从现在开始努力,最坏不过是大器晚成

举个栗子

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
type Foo struct {
Name string
}

var bar = "hello biezhi"

// -------------方法返回值----------------
func returnValue() string {
return bar
}

func returnPoint() *string {
return &bar
}

// --------------方法入参-----------------
func modifyNameByPoint(foo *Foo) {
foo.Name = "emmmm " + foo.Name
}

func nameToUpper(foo Foo) string {
foo.Name = strings.ToUpper(foo.Name)
return foo.Name
}

// --------------实例方法-----------------
func (foo Foo) setName(name string) {
foo.Name = name
}

func (foo *Foo) setNameByPoint(name string) {
foo.Name = name
}

这里我列出了 3 组方法,分别是指针类型和值类型的示例。这几个方法在编写代码的过程中都会经常遇到,我们从使用者的维度和内存的视角来分析一下这几个方法:

使用区别

大部分人都在讨论函数的入参是指针还是值类型呢?我们先来看看第一组方法,返回值的情况:

1
2
3
4
5
s1 := returnValue()
s2 := returnPoint()

fmt.Printf("s1: %v \n", s1) // s1: hello biezhi
fmt.Printf("s2: %v \n", *s2) // s2: hello biezhi

这两个方法一个返回了指针一个返回值类型,值类型是非 nil 的(在 Go 中所有的值类型都会有 初值),指针类型可以判断是否为 nil。 获取到的数据是相同的,不同之处在于取值的方式,指针类型需要使用 * 号读取数据。

下面尝试传递参数,分别是指针类型参数和值类型参数:

1
2
3
4
5
6
7
8
foo := Foo{Name:"biezhi"}
fmt.Println("foo.name:", foo.Name) // foo.name: biezhi

modifyNameByPoint(&foo)
fmt.Println("foo.name:", foo.Name) // foo.name: emmmm biezhi

fmt.Println("foo.name:", nameToUpper(foo)) // foo.name: EMMMM BIEZHI
fmt.Println("foo.name:", foo.Name) // foo.name: emmmm biezhi
  • modifyNameByPoint 需要指针类型,所以我们取 foo 的指针传入(foo是值类型所以这里用 & 取其地址)。
  • nameToUpper 需要一个值类型的参数,所以 foo 直接传入,返回值是转大写的 Name。
  • nameToUpper 不会修改 foo.Name 的数据,最后一次输出还是旧数据

Receiver Type

如果你编写 Java 代码的话经常会看到这样的代码

1
2
3
4
5
6
public class Bar {
String name;
public void setName(String name){
this.name = name;
}
}

可以看到这里有 this 关键字,在 Go 中是没有的,这里的 this 可以调用当前对象的成员变量和实例方法,当使用 this 修改了成员变量就相当于在 Go 中使用了指针,看看下面的 Go 代码:

1
2
3
4
5
6
7
func (foo *Foo) setNameByPoint(name string) {
foo.Name = name
}

func (foo Foo) setName(name string) {
foo.Name = name
}

Go 中想要为结构体定义属于自己的方法就使用如上的两种方式,这两个方法在 Go 中称为 Receiver Type(接受者类型),可以使用结构体变量调用,我们今天只讨论结构体这种情况,来看看这两个方法有什么不同:

1
2
3
4
5
6
foo := Foo{Name:"biezhi"}
foo.setName("2333")
fmt.Println("foo.Name:", foo.Name) // foo.Name: biezhi

foo.setNameByPoint("2333")
fmt.Println("foo.Name:", foo.Name) // foo.Name: 2333

根据输出发现一个结构体,如果不使用指针类型的时候值是不会被修改的。这点也很容易理解,在 setName 方法中 foo 变量被作为值传递,所以如果这时候输出 foo 的内存地址会发现和外面调用的是不一样的,来看看:

1
2
3
4
5
6
7
8
9
func (foo Foo) setName(name string) {
fmt.Printf("setName: %v address: %p \n", foo, &foo) // 2
foo.Name = name
}

func (foo *Foo) setNameByPoint(name string) {
fmt.Printf("setNameByPoint: %v address: %p \n", foo, &foo) // 4
foo.Name = name
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
foo := Foo{Name:"biezhi"}
fmt.Printf("src foo: %v address: %p \n", foo, &foo) // 1

foo.setName("set name")
fmt.Printf("by value foo: %v address: %p \n", foo, &foo) // 3

foo.setNameByPoint("2333")
fmt.Printf("by point foo: %v address: %p \n", foo, &foo) // 5

// output
» src foo: {biezhi} address: 0xc00000e1e0
» setName: {biezhi} address: 0xc00000e200
» by value foo: {biezhi} address: 0xc00000e1e0
» setNameByPoint: &{biezhi} address: 0xc00000c030
» by point foo: {2333} address: 0xc00000e1e0

而 setNameByPoint 方法和前面的指针类型传递是一样的,方法内部内存地址是一份指针的拷贝,修改数据会影响到外部指针变量的数据。

一般而言,工程化的项目中会出现非常多结构体定义方法的代码,这些方法的调用也会很频繁,使用结构体将其封装起来,和 Java 中类封装是一样的,大多数情况下建议都使用指针传递,避免值拷贝的情况。

其他类型

在前面我们有一张图中分了值类型和引用类型,除了那些常用的基本类型,还有像 map 和 slice 这种引用类型,它们在使用上有点像指针(但不用任何操作符如 &、*),来看个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func updateMap(mmp map[string]int)  {
mmp["biezhi"] = 2333
}

func main() {
mmp := make(map[string]int)
mmp["biezhi"] = 1024
fmt.Printf("src mmp: %v address: %p \n", mmp, &mmp) // 1

updateMap(mmp)
fmt.Printf("new mmp: %v address: %p \n", mmp, &mmp) // 2
}

// output
» src mmp: map[biezhi:1024] address: 0xc000094018
» new mmp: map[biezhi:2333] address: 0xc000094018

如果你尝试 slice 的话是同样的效果,可以看到给方法传递的并非是一个指针类型,但是 map 的值确实被修改了,这是为什么呢
其实拷贝一个 map 或者 slice 的时候并没有拷贝这个类型(引用类型)里面指向的数据,而是拷贝了引用类型(可简单理解为指针),如何验证这一说法呢?我们在 updateMap 中添加一行输出代码:

1
2
3
4
func updateMap(mmp map[string]int) {
fmt.Printf("param mmp: %v address: %p \n", mmp, &mmp)
mmp["biezhi"] = 2333
}

再次运行代码

1
2
3
src mmp: map[biezhi:1024] address: 0xc000094018 
input mmp: map[biezhi:1024] address: 0xc00000c038
new mmp: map[biezhi:2333] address: 0xc000094018

你会发现 input mmp 这行的地址发生了变化,正因为拷贝的是这个特殊的“引用类型”,会产生一个新的地址,而这个地址 0xc00000c038 和 0xc000094018 指向的是同一份数据,所以修改后外部的变量也会得到新的数据。

小结

Receiver Type 为什么推荐使用指针?

  • 推荐在实例方法上使用指针(前提是这个类型不是一个自定义的 map、slice 等引用类型)
  • 当结构体较大的时候使用指针会更高效
  • 如果要修改结构内部的数据或状态必须使用指针
  • 当结构类型包含 sync.Mutex 或者同步这种字段时,必须使用指针以避免成员拷贝
  • 如果你不知道该不该使用指针,使用指针!

结构较大” 到底多大才算大可能需要自己或团队衡量,如超过 5 个字段或者根据结构体内占用来计算。

方法参数该使用什么类型?

  • map、slice 等类型不需要使用指针(自带 buf)
  • 指针可以避免内存拷贝,结构大的时候不要使用值类型
  • 值类型和指针类型在方法内部都会产生一份拷贝,指向不同
  • 小数据类型如 bool、int 等没必要使用指针传递
  • 初始化一个新类型时(像 NewEngine() *Engine)使用指针
  • 变量的生命周期越长则使用指针,否则使用值类型

https://blog.biezhi.me/2018/10/values-or-pointers-in-golang.html

世界上最廉价的东西就是男人一事无成时的温柔。

Go 语言也提供了接口类型,使得我们可以面向接口编程,将实现和接口分离。在我看来,软件的抽象之美也应该以此来表达,和 Java 语言不同的是 Go 并不是那么 “强制”,它使用了一种 鸭子类型 的方式让动态类型成为可能。

Duck Typing

在 Go 中没有 implements 和 extends 这种关键字,这对我们而言反倒轻松了一些,它认为 Go 的接口就像鸭子测试里的描述:

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为 “鸭子” 的对象,并调用它的 “走” 和 “叫” 方法。

在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的 “走” 和 “叫” 方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。

任何拥有这样的正确的 “走” 和 “叫” 方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

举个例子

我们用 Go 语言来实现一个鸭子类型:

1
2
3
4
type Duck interface {
Swim() // 游泳
Feathers() // 羽毛
}

这里使用 Go 提供的 interface 关键字定义了一个鸭子接口类型,这个接口中提供了鸭子的两种行为:游泳和羽毛是什么样的,但是没有提供实现。

组合接口

现在我们给鸭子再添加一种嘎嘎叫的能力,一种方式是在原有的接口上添加 “嘎嘎叫” 方法,这样做的话就表示所有的鸭子都应该拥有此能力,假设我们的玩具鸭并不能开口嘎嘎叫,所以它没有这种能力。这时候我们可以将会嘎嘎叫的鸭子单独定义一种类型,在 Go 可以使用组合的方式来实现:

1
2
3
4
type QuackDuck interface {
Quack() // 嘎嘎叫
Duck // 嵌入接口
}

样 QuackDuck 类型就拥有了之前 Duck 提供的两种抽象能力,同时还应该拥有嘎嘎叫的能力。

接口实现

前面我们只给出了鸭子的能力定义,还没有任何实现,由于 Go 中没有继承和实现的关键字,想成为上述接口的实现非常简单,只要实现它们定义的方法就可以了。

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
// RealDuck - 真正的鸭子
type RealDuck struct { }

func (RealDuck) Swim() {
fmt.Println("用鸭璞向后划水")
}

func (RealDuck) Feathers() {
fmt.Println("遇到水也不会湿的羽毛")
}

func (RealDuck) Quack() {
fmt.Println("嘎~ 嘎~ 嘎~")
}

// ToyDuck - 玩具鸭
type ToyDuck struct { }

func (ToyDuck) Swim() {
fmt.Println("以固定的速度向前移动")
}

func (ToyDuck) Feathers() {
fmt.Println("白色的固定的塑料羽毛")
}

接口使用

接下来我们可以使用一下这个类型了:

1
2
3
4
var duck Duck
duck = ToyDuck{}
duck.Swim()
duck.Feathers()

当快乐到来时,我就不再思考人生 当寂寞来临时,我就低头努力读书

什么是指针:即一个指针变量指向一个值的内存地址。

使用值类型和指针类型的区别

首先,我们来看一个计算面积的代码,如下所示。

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
package main

import (
"fmt"
)

type Rect struct { //定义一个结构体
width float64
length float64
}

func (rect Rect) area() float64 { //定义一个方法,按值传递
return rect.width * rect.length
}

func (rect *Rect) area1() float64 { //定义一个方法,按指针传递
rect.width *= 2
rect.length *= 2
return rect.width * rect.length
}

func main() {
var rect = new(Rect) //使用new函数创建一个结构体指针rect,也就是说rect的类型是*Rect
rect.width = 100
rect.length = 200
fmt.Println("Width:", rect.width, "Length:", rect.length,"Area:", rect.area()) //通过结构体指针类型的变量调用area()方法
fmt.Println("Width:", rect.width, "Length:", rect.length,"Area:", rect.area1())
}

在Go语言中,默认是按值传递。当一个变量当作参数传递的时候,会创建一个变量的副本,然后传递给函数或者方法,你可以看到这个副本的地址和变量的地址是不一样的。当变量当做指针被传递的时候,一个新的指针被创建,它指向变量同样的内存地址,所以你可以将这个指针看成原始变量指针的副本。

故此

  • 1.是否使用结构体指针,取决于是否要在函数内部改变传递进来的参数的值。如果你的struct足够大,使用指针可以加快效率。如果不使用指针,在函数内部则无法修改struct中的值。
  • 2.结构体赋值默认是按值传递,你要改变原来的那个值,要使用指针(即如果你要修改对象本身,那就要传指针,否则修改的是副本)。

go什么情况下使用指针

  • 推荐在方法上使用指针(前提是这个类型不是 map、slice 等引用类型)
  • 当结构体较大的时候使用指针会更高效,可以避免内存拷贝,“结构较大” 到底多大才算大可能需要自己或团队衡量,如超过 5 个字段或者根据结构体内存占用来计算
  • 如果要修改结构体内部的数据或状态必须使用指针
  • 如果方法的receiver是map、slice 、channel等引用类型不要使用指针
  • 小数据类型如 bool、int 等没必要使用指针传递
  • 如果该函数会修改receiver或变量等,使用指针

天之涯,地之角,知交半零落。 一瓢浊酒尽余欢,今宵别梦寒。

添加点击事件

1
2
3
4
5
6
7
8
9
$(document).on('click', '#MenuToggle', function(){
$("#divBox").addClass("box-overflow");
$(".divBoxOverlay").addClass("push-overlay");
$(".divBoxOverlay").on('touchmove', function (e) {
e.preventDefault();
});
$("#topMenu").addClass("animate-translate");
});

或者

1
2
3
4
5
document.addEventListener('click', function (e) {
if (e.target && e.target.matches('#btn')) {
window.alert('hello ios safari')
}
})

iOS 的 Safari 下,委托点击事件在某些情况下会出现点击失效

解决办法

  • 最简单的,给 CSS 加上 cursor: pointer;
  • 停止委托,直接给 #btn 绑定事件处理器;
  • 给div元素加上 onclick=’void(0);’;
  • 将 div 换成其它不受该 bug 特性影响的元素,比如 a、button 等。

https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile
https://blog.zfanw.com/ios-safari-click-not-working/

春风再美也比不上你的笑 没见过你的人不会明了

环境

  • Laravel 5.5
  • ElasticSearch 6.5

代码片段

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
$params = [];
$length = count($conditions['id']);
foreach ($conditions['id'] as $key => $id) {
$params[] = ['id' => $id, 'score' => $length - $key];
}

$source = ['id', 'title', 'url', 'price', 'list_price', 'images', 'thumbs', 'status', 'rating', 'sort', 'reviews_amount', 'cids'];
$params = [
'index' => $index,
'type' => 'products',
'body' => [
'query' => [
'function_score' => [
'query' => [
'bool' => [
'must' => [
[
'terms' => [
'id' => $conditions['id']
]
]
]
]
],
'script_score' => [
'script' => [
'lang' => 'painless',
'params' => [
'scoring' => $params
],
'source' => "for(i in params.scoring) { if(doc['id'].value == i.id ) return i.score; } return 0;"
]
]
]
],
'_source' => $source,
],
'from' => ($page - 1) * $perPage,
'size' => $perPage,
];

$results = Searches::getEsInstance()->search($params);
$total = $results['hits']['total'];

if (empty($items = $results['hits']['hits'])) {
return [];
}

注意

不同版本的ES语法可能不同, 具体请参阅官方文档

我有整个宇宙想讲给你听,张嘴却吐不出半粒星尘。

Introduction

Cloudflare is a service that sits between the visitor and the website owner’s server, acting as a reverse proxy for websites. Cloudflare provides a Content Delivery Network (CDN), as well as DDoS mitigation and distributed domain name server services.

Nginx is a popular web server responsible for hosting some of the largest and highest-traffic sites on the internet. It’s common for organizations to serve websites with Nginx and use Cloudflare as a CDN and DNS provider.

In this tutorial you will secure your website served by Nginx with an Origin CA certificate from Cloudflare and configure Nginx to use authenticated pull requests. The advantages of using this setup are that you benefit from Cloudflare’s CDN and fast DNS resolution while ensuring that all connections pass through Cloudflare. This prevents any malicious requests from reaching your server.

Step 1 — Generating an Origin CA TLS Certificate

The Cloudflare Origin CA lets you generate a free TLS certificate signed by Cloudflare to install on your Nginx server. By using the Cloudflare generated TLS certificate you can secure the connection between Cloudflare’s servers and your Nginx server.

To generate a certificate with Origin CA, navigate to the Crypto section of your Cloudflare dashboard. From there, click on the Create Certificate button in the Origin Certificates section:

Leave the default option of Let CloudFlare generate a private key and a CSR selected.

Click Next and you will see a dialog with the Origin Certificate and Private key. You need to transfer both the origin certificate and private key from CloudFlare to your server.

We’ll use the /etc/ssl/certs directory on the server to hold the origin certificate. The /etc/ssl/private directory will hold the private key file. Both folders already exist on the server.

First, copy the contents of the Origin Certificate displayed in the dialog box in your browser.

Then, on your server, open /etc/ssl/certs/cert.pem for editing:

1
sudo nano /etc/ssl/certs/cert.pem

Paste the certificate contents into the file. Then save and exit the editor.

Then return to your browser and copy the contents of the Private key. Open the file /etc/ssl/private/key.pem for editing:

1
sudo nano /etc/ssl/private/key.pem

Paste the key into the file, save the file, and exit the editor.

Warning: Cloudflare’s Origin CA Certificate is only trusted by Cloudflare and therefore should only be used by origin servers that are actively connected to Cloudflare. If at any point you pause or disable Cloudflare, your Origin CA certificate will throw an untrusted certificate error.

Now that you copied the key and certificate files to your server, you need to update the Nginx configuration to use them.

Step 2 — Installing the Origin CA certificate in Nginx

In the previous section, you generated an origin certificate and private key using Cloudlfare’s dashboard and saved the files to your server. Now you’ll update the Nginx configuration for your site to use the origin certificate and private key to secure the connection between Cloudflare’s servers and your server.

Nginx creates a default server block during installation. Remove it if it exists, as you’ve already configured a custom server block for your domain:

1
sudo rm /etc/nginx/sites-enabled/default

Next, open the Nginx configuration file for your domain:

1
sudo nano /etc/nginx/sites-available/example.com

The file should look like this:

1
example.com'>/etc/nginx/sites-available/example.com
1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
listen [::]:80;

root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;

server_name example.com www.example.com;

location / {
try_files $uri $uri/ =404;
}
}

We’ll modify the Nginx configuration file to do the following:

  • Listen on port 80 and redirect all requests to use https.
  • Listen on port 443 and use the origin certificate and private key that you added in the previous section.

Modify the file so it looks like the following:

1
example.com'>/etc/nginx/sites-available/example.com
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
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 302 https://$server_name$request_uri;
}

server {

# SSL configuration

listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl on;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;

server_name example.com www.example.com;

root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;


location / {
try_files $uri $uri/ =404;
}
}

Save the file and exit the editor.

Next, test to make sure that there are no syntax errors in any of your Nginx configuration files:

1
sudo nginx -t

If no problems were found, restart Nginx to enable your changes:

1
sudo systemctl restart nginx

Now go to the Cloudflare dashboard’s Crypto section and change SSL mode to Full. This informs Cloudflare to always encrypt the connection between Cloudflare and your origin Nginx server.


Now visit your website at https://example.com to verify that it’s set up properly. You’ll see your home page displayed, and the browser will report that the site is secure.

In the next section, you will set up Authenticated Origin Pulls to verify that your origin server is indeed talking to Cloudflare and not some other server. By doing so, Nginx will be configured to only accept requests which use a valid client certificate from Cloudflare and requests which have not passed through CloudFlare will be dropped.

Step 3 — Setting Up Authenticated Origin Pulls

The Origin CA certificate will help Cloudflare verify that it is talking to the correct origin server. But how can your origin Nginx server verify that it is actually talking to Cloudflare? Enter TLS Client Authentication.

In a client authenticated TLS handshake, both sides provide a certificate to be verified. The origin server is configured to only accept requests that use a valid client certificate from Cloudflare. Requests which have not passed through Cloudflare will be dropped as they will not have Cloudflare’s certificate. This means that attackers cannot circumvent Cloudflare’s security measures and directly connect to your Nginx server.

Cloudflare presents certificates signed by a CA with the following certificate:

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
-----BEGIN CERTIFICATE-----
MIIGBjCCA/CgAwIBAgIIV5G6lVbCLmEwCwYJKoZIhvcNAQENMIGQMQswCQYDVQQG
EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjEUMBIGA1UECxMLT3JpZ2lu
IFB1bGwxFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3Ju
aWExIzAhBgNVBAMTGm9yaWdpbi1wdWxsLmNsb3VkZmxhcmUubmV0MB4XDTE1MDEx
MzAyNDc1M1oXDTIwMDExMjAyNTI1M1owgZAxCzAJBgNVBAYTAlVTMRkwFwYDVQQK
ExBDbG91ZEZsYXJlLCBJbmMuMRQwEgYDVQQLEwtPcmlnaW4gUHVsbDEWMBQGA1UE
BxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEjMCEGA1UEAxMa
b3JpZ2luLXB1bGwuY2xvdWRmbGFyZS5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDdsts6I2H5dGyn4adACQRXlfo0KmwsN7B5rxD8C5qgy6spyONr
WV0ecvdeGQfWa8Gy/yuTuOnsXfy7oyZ1dm93c3Mea7YkM7KNMc5Y6m520E9tHooc
f1qxeDpGSsnWc7HWibFgD7qZQx+T+yfNqt63vPI0HYBOYao6hWd3JQhu5caAcIS2
ms5tzSSZVH83ZPe6Lkb5xRgLl3eXEFcfI2DjnlOtLFqpjHuEB3Tr6agfdWyaGEEi
lRY1IB3k6TfLTaSiX2/SyJ96bp92wvTSjR7USjDV9ypf7AD6u6vwJZ3bwNisNw5L
ptph0FBnc1R6nDoHmvQRoyytoe0rl/d801i9Nru/fXa+l5K2nf1koR3IX440Z2i9
+Z4iVA69NmCbT4MVjm7K3zlOtwfI7i1KYVv+ATo4ycgBuZfY9f/2lBhIv7BHuZal
b9D+/EK8aMUfjDF4icEGm+RQfExv2nOpkR4BfQppF/dLmkYfjgtO1403X0ihkT6T
PYQdmYS6Jf53/KpqC3aA+R7zg2birtvprinlR14MNvwOsDOzsK4p8WYsgZOR4Qr2
gAx+z2aVOs/87+TVOR0r14irQsxbg7uP2X4t+EXx13glHxwG+CnzUVycDLMVGvuG
aUgF9hukZxlOZnrl6VOf1fg0Caf3uvV8smOkVw6DMsGhBZSJVwao0UQNqQIDAQAB
o2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4E
FgQUQ1lLK2mLgOERM2pXzVc42p59xeswHwYDVR0jBBgwFoAUQ1lLK2mLgOERM2pX
zVc42p59xeswCwYJKoZIhvcNAQENA4ICAQDKDQM1qPRVP/4Gltz0D6OU6xezFBKr
LWtDoA1qW2F7pkiYawCP9MrDPDJsHy7dx+xw3bBZxOsK5PA/T7p1dqpEl6i8F692
g//EuYOifLYw3ySPe3LRNhvPl/1f6Sn862VhPvLa8aQAAwR9e/CZvlY3fj+6G5ik
3it7fikmKUsVnugNOkjmwI3hZqXfJNc7AtHDFw0mEOV0dSeAPTo95N9cxBbm9PKv
qAEmTEXp2trQ/RjJ/AomJyfA1BQjsD0j++DI3a9/BbDwWmr1lJciKxiNKaa0BRLB
dKMrYQD+PkPNCgEuojT+paLKRrMyFUzHSG1doYm46NE9/WARTh3sFUp1B7HZSBqA
kHleoB/vQ/mDuW9C3/8Jk2uRUdZxR+LoNZItuOjU8oTy6zpN1+GgSj7bHjiy9rfA
F+ehdrz+IOh80WIiqs763PGoaYUyzxLvVowLWNoxVVoc9G+PqFKqD988XlipHVB6
Bz+1CD4D/bWrs3cC9+kk/jFmrrAymZlkFX8tDb5aXASSLJjUjcptci9SKqtI2h0J
wUGkD7+bQAr+7vr8/R+CBmNMe7csE8NeEX6lVMF7Dh0a1YKQa6hUN18bBuYgTMuT
QzMmZpRpIBB321ZBlcnlxiTJvWxvbCPHKHj20VwwAz7LONF59s84ZsOqfoBv8gKM
s0s5dsq5zpLeaw==
-----END CERTIFICATE-----

You can also download the certificate directly from Cloudflare here.

Copy this certificate.

Then create the file /etc/ssl/certs/cloudflare.crt file to hold Cloudflare’s certificate:

1
sudo nano /etc/ssl/certs/cloudflare.crt

Paste the certificate into the file. Then save the file and exit the editor.

Now update your Nginx configuration to use TLS Authenticated Origin Pulls. Open the configuration file for your domain:

1
sudo nano /etc/nginx/sites-available/example.com

Add the ssl_client_certificate and ssl_verify_client directives as shown in the following example:

1
example.com'>/etc/nginx/sites-available/example.com
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
. . .

server {

# SSL configuration

listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl on;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;
ssl_client_certificate /etc/ssl/certs/cloudflare.crt;
ssl_verify_client on;

. . .

Save the file and exit the editor.

Next, test to make sure that there are no syntax errors in your Nginx configuration.

1
2
sudo nginx -t
sudo systemctl restart nginx

Finally, to enable Authenticated Pulls, open the Crypto section in the Cloudflare dashboard and toggle the Authenticated Origin Pulls option .

Now visit your website at https://example.com to verify that it was set up properly. As before, you’ll see your home page displayed.

To verify that your server will only accept requests signed by Cloudflare’s CA, toggle the Authenticated Origin Pulls option to disable it and then reload your website. You should get the following error message :

Your origin server raises an error if a request is not signed by Cloudflare’s CA.

Now that you know it works properly, return to the Crypto section in the Cloudflare dashboard and toggle the Authenticated Origin Pulls option again to enable it.

https://www.digitalocean.com/community/tutorials/how-to-host-a-website-using-cloudflare-and-nginx-on-ubuntu-16-04

行路难,不在水,不在山,只在人情反覆间

Laravel框架默认存储每次请求(每次命令行执行也相当于一次请求)的所有数据库查询语句!!!
在普通的http中数据库请求语句并不多,所有不会导致问题,但是需要大量数据库查询的命令行工具就显然不能这么干,

禁用query日志

1
DB::connection()->disableQueryLog();  //禁用query log

打印sql

AppServiceProvider.php 的 boot 方法中添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
public function boot()
{
if (env('APP_ENV') === 'local') {
DB::connection()->enableQueryLog();
Event::listen('kernel.handled', function ($request, $response) {
if ( $request->has('sql-debug') ) {
$queries = DB::getQueryLog();
dd($queries);
}
});
}
}

最难相处的女生是那种很爱文艺但又没什么文化的

一般的查询:

1
2
3
4
5
6
7
8
9
10
SELECT
*
FROM
`rainlab_translate_messages`
WHERE
( ( lower( message_data ) LIKE '%"\u4ef7\u683c"%' ) )
AND `domain_id` = 1
ORDER BY
`message_data` ASC
LIMIT 500 OFFSET 0

这样的查询是查不到结果的,应该改成这样

1
2
3
4
5
6
7
8
9
10
SELECT
*
FROM
`rainlab_translate_messages`
WHERE
( ( lower( message_data ) LIKE '%"_u4ef7_u683c"%' ) )
AND `domain_id` = 1
ORDER BY
`message_data` ASC
LIMIT 500 OFFSET 0

针对PHP里的,我们直接输入汉字, 可以这样进行操作:

1
$query = $query->searchWhere(str_replace("\\","_",json_encode($search)), ['message_data']);

你爱上小溪 是因为没有见过大海 我已见过银河 但我只爱一颗星

安装 pip

Pip 是 Python 的包管理工具,这里我们用 pip 安装 shadowsocks。

1
2
yum install python-pip
pip install shadowsocks

配置 shadowsocks

新建配置文件:

1
vi /etc/shadowsocks.json

填写以下内容:

1
2
3
4
5
6
7
8
9
10
11
{
"server":"your_server_ip", #ss服务器IP
"server_port":your_server_port, #端口
"local_address": "127.0.0.1", #本地ip
"local_port":1080, #本地端口
"password":"your_server_passwd",#连接ss密码
"timeout":300, #等待超时
"method":"rc4-md5", #加密方式
"fast_open": false, # truefalse。如果你的服务器 Linux 内核在3.7+,可以开启 fast_open 以降低延迟。开启方法: echo 3 > /proc/sys/net/ipv4/tcp_fastopen 开启之后,将 fast_open 的配置设置为 true 即可
"workers": 1 # 工作线程数
}

启动shadowsocks服务

1
2
3
sslocal -c /etc/shadowsocks.json

nohup sslocal -c /etc/shadowsocks.json &>> /var/log/sslocal.log &

设置shadowsocks开机自启

1
sudo vim /etc/systemd/system/shadowsocks.service

填写如下内容:

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Shadowsocks Client Service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/sslocal -c /etc/shadowsocks.json

[Install]
WantedBy=multi-user.target

配置生效:

1
systemctl enable /etc/systemd/system/shadowsocks.service

测试

运行

1
curl --socks5 127.0.0.1:1080 http://httpbin.org/ip

如果返回你的 ss 服务器 ip 则测试成功:

1
2
3
{
"origin": "23.105.222.129"
}

安装 Privoxy

Shadowsocks 是一个 socket5 服务,因此我们需要使用 Privoxy 把流量转到 http/https 上。直接使用yum安装即可:

1
yum install privoxy

安装好后,修改一下配置:

1
vim /etc/privoxy/config

搜索forward-socks5t

1
forward-socks5t / 127.0.0.1:9050 .

取消注释并修改为:

1
forward-socks5t / 127.0.0.1:1080 .

启动 privoxy

1
privoxy /etc/privoxy/config

或以指定用户如www运行privoxy:

1
privoxy --user www /etc/privoxy/config

设置privoxy开机自启

1
sudo vim /lib/systemd/system/privoxy.service

填写如下内容:

1
2
3
4
5
6
7
8
9
[Unit]
Description=Privoxy Web Proxy With Advanced Filtering Capabilities
Wants=network-online.target
After=network-online.target

[Service]
Type=forking
PIDFile=/run/privoxy.pid
ExecStart=/usr/sbin/privoxy --pidfile /run/privoxy.pid /etc/privoxy/config

配置生效:

1
systemctl enable /lib/systemd/system/privoxy.service

配置/etc/profile

执行vim /etc/profile,添加如下代码:

1
2
export http_proxy=http://127.0.0.1:8118
export https_proxy=http://127.0.0.1:8118

修改后使配置生效:

1
source /etc/profile

测试生效:

1
curl www.google.com

返回一大堆 HTML 则说明 shadowsocks 正常工作了。

简化使用

进过上面的步骤我们的确代理成功了。。但是每次都要输入这么多命令太麻烦,这时我们可以利用 命令别名 来简化我们的操作

1
2
3
alias ssinit='nohup sslocal -c /etc/shadowsocks.json &>> /var/log/sslocal.log &'
alias sson='export http_proxy=http://127.0.0.1:8118 && export https_proxy=http://127.0.0.1:8118 && systemctl start privoxy'
alias ssoff='unset http_proxy && unset https_proxy && systemctl stop privoxy && pkill sslocal'

使用方法

1
2
3
4
5
### 开启ss代理
ssinit
sson
## 关闭ss代理
ssoff

https://i.jakeyu.top/2017/03/16/centos%E4%BD%BF%E7%94%A8SS%E7%BF%BB%E5%A2%99/
https://xeylon.com/server/140.html

为大概率坚持,为小概率备份。

比如我在A分支做了一些修改,现在由于某种原因(如A分支已经合并到master)不能把A分支上修改的东西保留下来但是需要把A分支上修改的东西继续在新分支继续修改。那么现在我们可以有两种简单的做法完成这一需求。

第一种方法

我们不需要在A分支做commit,只需要在A分支新建B分支,然后切换过去。这个时候你会发现修改的东西在A,B分支都有。这个时候在B分支commit,那么这些修改保留在B分支上,再切换到A分支上会发现修改都没有保留下来。

第二种方法

使用Git stash 将A分支暂存起来,然后在某一个分支(如master分支)新建一个分支B,然后在B分支上使用git stash pop 将修改弹出到B分支上,然后这些修改就在B分支上了。然后我们又可以愉快的玩耍了~