0%

孤独的孩子,悄悄在风中长大了。

Suppose I’m building a web app in Go. In my app, I want to send a message to my users. I can send a message to the users via email or SMS. This would be a perfect use case for interfaces.

For this hypothetical web app, I create the following main.go file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type User struct {
Name string
Email string
}

type UserNotifier interface {
SendMessage(user *User, message string) error
}

func main() {
user := &User{"Dirk", "dirk@email.com"}

fmt.Printf("Welcome %s\n", user.Name)
}

The User struct represents a user.
You can see I created a UserNotifier of type interface that has a single function: SendMessage().
To implement this interface, I have to create a struct that implements the SendMessage() function.

1
2
3
4
5
6
7
type EmailNotifier struct {
}

func (notifier EmailNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending email to %s with content %s\n", user.Name, message)
return err
}

As you can see, I created a new EmailNotifier struct. Then I implemented the SendMessage() method on the struct. Note that in this example, the EmailNotifier simply prints a message. In the real world, you would probably call an email API such as Mailgun.

And that’s it, the interface is now implemented.

The next thing to do is send an email to the user using the UserNotifier interface.

1
2
3
4
5
6
7
8
func main() {
user := User{"Dirk", "dirk@email.com"}
fmt.Printf("Welcome %s\n", user.Name)

var userNotifier UserNotifier
userNotifier = EmailNotifier{}
userNotifier.SendMessage(&user, "Interfaces all the way!")
}

When running this program, the SendMessage() of the EmailNotifier is called correctly.

1
2
3
4
go build -o main main.go
./main
Welcome Dirk
Sending email to Dirk with content Interfaces all the way!

Let’s implement an SMS interface as well.

1
2
3
4
5
6
7
type SmsNotifier struct {
}

func (notifier SmsNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending SMS to %s with content %s\n", user.Name, message)
return err
}

One cool feature is we can store the notifier in the user struct. In this way, each user will have a personal notifier.

1
2
3
4
5
type User struct {
Name string
Email string
Notifier UserNotifier
}

You can then add a handy method to the User struct that notifies the user using the UserNotifier interface.

1
2
3
func (user *User) notify(message string) error {
return user.Notifier.SendMessage(user, message)
}

Finally, I created two users in the main() function and called the notify() method.

1
2
3
4
5
6
7
func main() {
user1 := User{"Dirk", "dirk@email.com", EmailNotifier{}}
user2 := User{"Justin", "bieber@email.com", SmsNotifier{}}

user1.notify("Welcome Email user!")
user2.notify("Welcome SMS user!")
}

The final result worked as expected.

1
2
3
4
go build -o main main.go
./main
Sending email to Dirk with content Welcome Email user!
Sending SMS to Justin with content Welcome SMS user!

https://medium.com/better-programming/a-real-world-example-of-go-interfaces-98e89b2ddb67

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

问题

golang如何在一个平台编译另外一个平台可以执行的文件。比如在mac上编译Windows和linux可以执行的文件。那么我们的问题就设定成:如何在mac上编译64位linux的可执行文件。

解决方案

golang的交叉编译要保证golang版本在1.5以上,本解决方案实例代码1.9版本执行的。

hello.go

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Printf("hello, world\n")
}

在mac上编译64位linux的命令编译命令

1
GOOS=linux GOARCH=amd64 go build hello.go

参数解析

这里用到了两个变量:

  • GOOS:目标操作系统
  • GOARCH:目标操作系统的架构

扩展阅读

在网络上的诸多教程中可能会看到下面的编译命令

1
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build hello.go

其中CGO_ENABLED=0的意思是使用C语言版本的GO编译器,参数配置为0的时候就关闭C语言版本的编译器了。自从golang1.5以后go就使用go语言编译器进行编译了。在golang1.9当中没有使用CGO_ENABLED参数发现依然可以正常编译。当然使用了也可以正常编译。比如把CGO_ENABLED参数设置成1,即在编译的过程当中使用CGO编译器,我发现依然是可以正常编译的。

实际上如果在go当中使用了C的库,比如import “C”默认使用go build的时候就会启动CGO编译器,当然我们可以使用CGO_ENABLED=0来控制go build是否使用CGO编译器。

她曾那么可爱。然后一切都变了。

I’m a big fan of the Chrome Extension API. It is a simple (and cool) way to extend my favorite browser using HTML and JavaScript. One of the features I’ve been excited about for a while now is the DevTools API. This has been in “experimental” status for a while but is now (mostly) available in the main release.

The DevTools API allows you to integrate extensions directly into your DevTools. There are multiple samples of this API. But there aren’t any samples that detail how to create panels. There is documentation for creating panels, but it wasn’t clear how to use that API in a real extension. I dug at it for a while and finally figured it out. What follows is what I had to do to make panel creation work. It may not be the right way, but it worked. First, you must set a devtools_page value in your manifest.json. Here is my sample manifest.

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "ColdFire",
"version": "1.0.2",
"manifest_version": 2,
"description": "Adds ColdFire support.",
"devtools_page": "devtools.html",
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
]
}

the critical part here is devtools_page. You specify a HTML file that will be executed every time the devtools UI is opened. Here is my HTML file:

1
2
3
4
5
6
7
8
<!doctype html>
<html>
<head>
<script src="devtools.js"></script>
</head>
<body>
</body>
</html>

Not much, right? As far as I can tell, the actual HTML for this file is never going to be rendered - anywhere. Instead, the entire point of this file is to load a JavaScript script. If so, it would be nice if Google would let us simply point devtools_page to a JavaScript script instead. Now let’s look at the script.

1
2
3
4
5
console.log("hello from devtools");
chrome.devtools.panels.create("ColdFire",
"coldfusion10.png",
"panel.html",
function(panel) { console.log("hello from callback"); });

The main part of this script - for now - is just one line. It runs the panels.create API to define a new panel. The first argument is the title. The second is the icon. The third is the HTML file for the panel. Finally, the last argument is a callback to execute when the panel is created. There is no documentation on what size the icon should be. It seems to be 32x32.

Now let’s look at panel.html. I’ve only just begun development so for right now it is pretty simple.

1
2
3
4
5
6
7
8
9
<html>
<head>
<script src="panel.js"></script>
</head>
<body>
<h2>ColdFire</h2>

</body>
</html>

And last but not least, the JavaScript file loaded by the panel:

1
console.log("panel.js");

So - what about all those console messages? As I tried to get things working, I used console messages all over the place so I could - hopefully - see what was going on. I had very little luck with this. I’ve blogged before about how to use console with Chrome Extensions, but that tip only works when you have a background script. My test extension did not. What finally worked was this:

Click the button that detaches Chrome Dev Tools
With the Chrome Dev Tools in its own window and currently focused, use the keyboard combo to open Chrome Dev Tools
This opens a second Chrome Dev Tools and I could see all my log messages there.

I hope this helps. As I said, I just got this working so I probably have a lot more to learn.

https://www.raymondcamden.com/2012/07/15/How-to-add-a-panel-to-Chrome-Dev-Tools

你如今的气质里,藏着你走过的路,读过的书,和爱过的人。

Webからエンタープライズアプリケーションまで、RESTfulというアーキテクチャーが使われています。ステートレス設計として、別々のソフトウェアコンポーネント間の通信を提供する強力な方法であります。 Web開発者として、RESTful APIを作るために、主にPythonを使いますが、最近Goプログラミング言語に出会い、興味を持ってきました。Goプログラミング言語(golangでも呼ばれる)は、Googleのプロジェクトで、マイクロサービスアーキテクチャに適当な多くの機能も提供するので、人気が増えています。

本記事、golangを使ってシンプルなRESTful APIをどう作るか説明したいと思います。

仕様

RESTful APIは、通常CRUDができます。CRUDとは、データベースの四つの主要な機能、「作成(Create)」「読み出し(Read)」「更新(Update)」「削除(Delete)」ですが、今回は、簡単にするために、読み出しの機能しか作りません。そして、データがJSONの形式で書きます。

そして、このエンドポイントがあります:

  • / -> ホームページですが、とりあえずテキストしかを見せない
  • /items/ -> Itemのリスト、Itemデータを全部見せる
  • /items/{id} -> 特定のItemの情報
    それぞれのItemは、「title」と「description」のデータがあって、簡単にするために、データベースを使用するのではなく、データを事前に定義します。

開発プロセス

最初は、必要なパッケージをインストールします。ルーティングのために、Muxというパッケージを使っていきます。CMD / Terminalを開けて、このコマンドを実行してください:

1
go get -u github.com/gorilla/mux

その後、main.goというファイルを作成して、このコードを入れてください:

1
2
3
4
5
6
7
8
9
package main

import (
"fmt"
"log"
"net/http"

"github.com/gorilla/mux"
)

すべてのgolangを使って作られたプログラムがパッケージで構成されています。この部分では、何のパッケージが必要かが書いてあります。この場合には、四つのパッケージが必要です。

ルーティング

同じファイルで、さっきの書いたコードの下に、このコードを入れてください:

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
// Controller for the / route (home)
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the home page. Welcome!")
}

// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
// Code for returning all items here
}

// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
// Code for returning a single item here
}

func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/", homePage)
myRouter.HandleFunc("/items", returnAllItems)
myRouter.HandleFunc("/items/{id}", returnSingleItem)
log.Fatal(http.ListenAndServe(":8000", myRouter))
}

func main() {
handleRequests()
}

main()という関数は、このプログラムをスタートすると、自動的に実行されます。その中に、handleRequests()という関数が実行されます。handleRequests()の関数で、それぞれのエンドポイントがアクセスされると、何が起こるかが書いてあります。例えば、/のエンドポイントがアクセスされると、homePageという関数が実行されるとか、/itemsのエンドポイントがアクセスされると、returnAllItemsという関数が実行されます。

そして、このコード:

1
log.Fatal(http.ListenAndServe(":8000", myRouter))

の意味は、このプログラムが8000のポートで、アクセスできます。これで、もう使えるなので、スタートしてみましょう!

CMD / Terminal で、このコマンドを実行してください:

1
go run main.go

サポート関数

これから、二つのパッケージが必要なので、インポートしましょう。最初のimport部分をこれに変更してください:

1
2
3
4
5
6
7
8
9
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"

"github.com/gorilla/mux"
)

encoding/jsonとstrconvというパッケージを追加しました。もうすぐこの二つを使います。

それから、二つのサポート関数を書きましょう:

1
2
3
4
5
6
7
8
9
10
func respondWithError(w http.ResponseWriter, code int, msg string) {
respondWithJson(w, code, map[string]string{"error": msg})
}

func respondWithJson(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}

両方の関数は、JSONの形式でHTTP requestに答える役割があります。

Itemデータ構造

Itemのデータ構造を書きましょう。importの部分の下に、このコードを入れてください:

1
2
3
4
5
// Item representation
type Item struct {
Title string `json:"title"`
Description string `json:"description"`
}

仕様に基づいて、それぞれのItemは、「title」と「description」のデータがあります。そして、その下に、これを入れて下さい:

1
2
3
4
5
// Global, static list of items
var itemList = []Item{
Item{Title: "Item A", Description: "The first item"},
Item{Title: "Item B", Description: "The second item"},
}

今回は、データベースを使わないので、これでデータを事前に定義します。二つのItemだけ書きましたが、自由にいくつでもいいですよ。

コア関数

最後に、コア関数(returnAllItemsとreturnSingleItem)を書きます。

1
2
3
4
// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
respondWithJson(w, http.StatusOK, itemList)
}

基本的に、/itemsのエンドポイントをアクセスすると、returnAllItems()という関数がすべてのItemをJSONの形式で答えます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
// Get query parameters using Mux
vars := mux.Vars(r)

// Convert {id} parameter from string to int
key, err := strconv.Atoi(vars["id"])

// If {id} parameter is not valid int
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}

// If Item with ID of {id} does not exist
if key >= len(itemList) {
respondWithError(w, http.StatusNotFound, "Item does not exist")
return
}

respondWithJson(w, http.StatusOK, itemList[key])
}

そして、returnSingleItem()という関数が、idのURLパラメーターに基づいて特定のItemの情報をJSONの形式で答えます。

例えば、localhost:8000/items/1をアクセスしたら、1がidになります。それから、そのidは、indexとしてitemListという配列からデータを取ってJSONの形式で答えます。idは1の場合は、二つ目のデータが取られます。そして、idは0の場合は、一つ目のデータが取られます。

idは、数だけであるべきであり、Itemの数以上になってはいけないので、二つのチェックを書きました。これで、終わりです。作ってみましょう!

テスト

CMD / Terminal で、さっきのrunを閉じて、もう一回実行してください:

1
go run main.go

https://qiita.com/sbenemerito/items/d94b811f14ce0a546a3f

幸有我来山未孤

使用场景

反向代理的概念不多做解释,此处应用反向代理主要原因如下:

  • 应用服务器不必暴露在公网,躲在防火墙后,通过内网连接代理服务器(proxy server)
  • DNS设置简单,因为有两个不同应用服务器,对应不同的子域名(a.example.com, b.example.com),只需设置DNS *.example.com 到代理服务器IP即可
  • SSL证书管理方便,只需申请一个通配符的证书 *.example.com 配置在代理服务器

环境

系统环境都是Ubuntu 18.04.2,服务器都是使用Nginx,代理服务器配置SSL,使用HTTPS对外访问(HTTP强制跳转到HTTPS),应用服务器使用Laravel,HTTP对内网访问。

配置

应用服务器配置

应用服务器的nginx照常配置即可,可以设置server_name为对应的域名。

代理服务器配置

代理服务器从网上抄来一份,主要是设置proxy_pass,下面只是location block:

1
2
3
4
5
6
7
location / {
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 X-Forwarded-Proto $scheme;
proxy_pass http://ip;
}

注意要加 proxy_set_header X-Forwarded-Proto $scheme 这一条,让后面被代理的应用服务器知道对应的协议是什么,这样应用服务器生成url(如css,js文件)使用相应的协议。如果代理服务器确定使用https,那$scheme替换成https也可以。

问题

本以为这样就搞定了,但是在用域名测试的时候发现网站不能正常加载,浏览器有如下错误:

1
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure stylesheet '<URL>'. This request has been blocked; the content must be served over HTTPS.

在页面中混用了HTTPS和HTTP,浏览器会block掉不安全的HTTP链接。而HTTP链接主要是用laravel的asset生成的css, js文件,css文件没有加载上,所以就不能正常加载了。

https://laravel.com/docs/5.5/helpers#method-asset
多方查询,有推荐使用proxy_redirect来替换HTTP为HTTPS,但是试用不成功。

也有说这些资源文件用uri路径来表示,\css\example.css 这样,不带协议。

或者页面增加一个meta,让HTTP升级为安全的HTTPS:

1
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />

这样在生产环境没问题,但是需要改动很多代码,而且开发、测试环境也需要ssl证书,很不方便。

在查询到几篇文章中,有提到后端是tomcat,需要修改tomcat的配置来接受X-Forwarded-Proto 这个Header来处理。我思考是不是laravel也需要类似设置,google一下发现了这个:

https://laravel.com/docs/5.5/requests#configuring-trusted-proxies
When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links.

Laravel从5.5开始,内置了一个 TrustProxies模块来解决这个问题,专门应对使用了Load Balancer等情况。

所以按照文档,修改 protected $proxies = ‘‘,因为应用服务器在内网,所以改成也无所谓。
Laravel 5.5之前的版本,可以自行用composer来安装这个插件。

https://github.com/fideloper/TrustedProxy

测试之后,问题解决。

http://alexlove77.github.io/2019/06/18/ngingx-reverse-proxy-https/

知我者谓我心忧,不知我者谓我何求,悠悠苍天,此何人哉

Sending Headers Only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"net/http"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "A Go Web Server")
w.WriteHeader(200)
}

Rendering Plain Text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"net/http"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}

Rendering JSON

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

import (
"encoding/json"
"net/http"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

js, err := json.Marshal(profile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.Write(js)
}

Rendering XML

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

import (
"encoding/xml"
"net/http"
)

type Profile struct {
Name string
Hobbies []string `xml:"Hobbies>Hobby"`
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

x, err := xml.MarshalIndent(profile, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/xml")
w.Write(x)
}

Serving a File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"net/http"
"path"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
// Assuming you want to serve a photo at 'images/foo.png'
fp := path.Join("images", "foo.png")
http.ServeFile(w, r, fp)
}

Important: http.ServeFile() does not automatically sanitize the file path. So if you’re constructing a file path from untrusted user input, you must sanitize the input with filepath.Clean() before using it to avoid directory traversal attacks.

Rendering a HTML Template

1
2
3
4
File: templates/index.html

<h1>Hello {{ .Name }}</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
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
package main

import (
"html/template"
"net/http"
"path"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

fp := path.Join("templates", "index.html")
tmpl, err := template.ParseFiles(fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

Rendering a HTML Template to a String

Instead of passing in the http.ResponseWriter when executing your template (like in the above snippet) use a buffer instead:

1
2
3
4
5
6
7
...
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
templateString := buf.String()
...

Using Layouts and Nested Templates

File: templates/layout.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>{{ template "title" . }}</title>
</head>
<body>
{{ template "content" . }}
</body>
</html>

File: templates/index.html

1
2
3
4
5
6
{{ define "title" }}An example layout{{ end }}

{{ define "content" }}
<h1>Hello {{ .Name }}</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
{{ end }}

File: main.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
26
27
28
29
30
31
32
33
34
35
package main

import (
"html/template"
"net/http"
"path"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

lp := path.Join("templates", "layout.html")
fp := path.Join("templates", "index.html")

// Note that the layout file must be the first parameter in ParseFiles
tmpl, err := template.ParseFiles(lp, fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

https://www.alexedwards.net/blog/golang-response-snippets

所爱隔山海,山海不可平

描述

选择所有紧接在 “prev” 元素后的 “next” 元素.

1
2
3
JQuery("prev + next")
prev 任何可用的选择器.
next 一个选择器,过滤刚好在第一个选择器后面的元素.

例子

找出所有紧挨在label后面的inputs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<script src="../jquery.min.js"></script>
<style></style>
</head>
<body>
<form>

<label>Name:</label>
<input name="name" />
<fieldset>
<label>Newsletter:</label>
<input name="newsletter" />
</fieldset>
</form>
<input name="none" />
<script>
$("label + input").css("color", "blue").val("Labeled!")
</script>
</body>
</html>

命运是什么?命是弱者借口,运乃强者谦词

WIN10设置

进入windows开机启动目录,先在运行>命令(win+r)中输入shell:startup打开启动文件夹
新建vbs文件start-wsl.vbs:

1
2
3
4
5
Set ws = WScript.CreateObject("WScript.Shell")
cmd = "C:\Windows\System32\bash.exe -c ""bash /home/init.sh"""
ws.Run cmd, 0, false
Set ws = Nothing
WScript.quit

WSL设置

在WSL系统里创建/home/init.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

# 自动输入sudo密码
sudo -S service ssh start << EOF
123456
EOF

sudo bt start
sudo service ssh --full-restart
sudo service nginx restart
sudo service mysqld restart
sudo service redis restart
sudo service php-fpm-73 restart
sudo service cron start
sudo supervisord
sudo supervisorctl start all
# 运行bash.exe不退出
while true
do
sleep 600
done

赋予执行权限

1
chmod +x init.sh

https://tech1024.com/original/3006
https://www.jianshu.com/p/0a102c4bb3a2

都说时间是良药,但治好的都是皮外伤

产品列表

产品详情

分类列表

产品归类

产品爬虫

订单列表

订单详情

订单促销

产品促销

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

Q

I have a problem with Laravel’s ORM Eloquent chunk() method. It misses some results. Here is a test query :

1
2
3
4
5
6
7
8
$destinataires = Destinataire::where('statut', '<', 3)
->where('tokenized_at', '<', $date_active)
->chunk($this->chunk, function ($destinataires) {
foreach($destinataires as $destinataire) {
$this->i++;
}
}
echo $this->i;

It gives 124838 results.
But :

1
2
3
4
$num_dest = Destinataire::where('statut', '<', 3)
->where('tokenized_at', '<', $date_active)
->count();
echo $num_dest;

gives 249676, so just TWICE as the first code example.

A

Use chunkById() instead of chunk().

When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results.

1
2
3
4
5
6
7
8
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});

https://stackoverflow.com/questions/32700537/eloquent-chunk-missing-half-the-results