shadowsocks go 一键安装

本脚本适用环境:

系统支持:CentOS,Debian,Ubuntu
内存要求:≥128M
日期:2015年08月01日

关于本脚本:

一键安装 go 版的 shadowsocks 最新版本 1.1.4。据说 go 版本有 buff 。与 python 版不同的是,其客户端程序能使用多个服务端配置,本脚本安装的是服务端程序。作者默认推荐 aes-128-cfb 加密,基于一致性,脚本使用了 aes-256-cfb 加密方式。

默认配置:

服务器端口:自己设定(如不设定,默认为 8989)
客户端端口:1080
密码:自己设定(如不设定,默认为teddysun.com)

客户端下载:

http://sourceforge.net/projects/shadowsocksgui/files/dist/

使用方法:

使用root用户登录,运行以下命令:

1
2
3
wget --no-check-certificate https://raw.githubusercontent.com/iMeiji/shadowsocks_install/master/shadowsocks-go.sh
chmod +x shadowsocks-go.sh
./shadowsocks-go.sh 2>&1 | tee shadowsocks-go.log

安装完成后,脚本提示如下:

1
2
3
4
5
6
7
8
9
Congratulations, shadowsocks-go install completed!
Your Server IP:your_server_ip
Your Server Port:your_server_port
Your Password:your_password
Your Local Port:1080
Your Encryption Method:aes-256-cfb

Welcome to visit:http://teddysun.com/392.html
Enjoy it!

卸载方法:

使用 root 用户登录,运行以下命令:

1
./shadowsocks-go.sh uninstall

其他事项:

客户端配置的参考链接:http://teddysun.com/339.html
安装完成后即已后台启动 shadowsocks-go ,运行:

1
/etc/init.d/shadowsocks status

可以查看 shadowsocks-go 进程是否已经启动。
本脚本安装完成后,已将 shadowsocks-go 加入开机自启动。

使用命令:

启动:/etc/init.d/shadowsocks start
停止:/etc/init.d/shadowsocks stop
重启:/etc/init.d/shadowsocks restart
状态:/etc/init.d/shadowsocks status

多用户多端口配置文件 sample(2015年01月08日):
配置文件路径:vi /etc/shadowsocks/config.json

1
2
3
4
5
6
7
8
9
10
11
{
"port_password":{
"8989":"password0",
"9001":"password1",
"9002":"password2",
"9003":"password3",
"9004":"password4"
},
"method":"aes-256-cfb",
"timeout":600
}

参考链接:
https://github.com/shadowsocks/shadowsocks-go
https://github.com/iMeiji/shadowsocks_install/wiki/shadowsocks-go-%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85

MySql导出指定列的数据

MySql导出整库或者指定表的数据使用mysqldump命令即可,但是导出表中指定列的数据就需要用到下面命令了,如下:

1
mysql -uroot -p123456 database_name -e "SELECT name from t_xxx where type = 3 INTO OUTFILE'/data/xxx.sql'"

xxx is not in the sudoers file解决方法

用sudo时提示”xxx is not in the sudoers file. This incident will be reported.其中XXX是你的用户名,也就是你的用户名没有权限使用sudo,我们只要修改一下/etc/sudoers文件就行了。下面是修改方法:

  1. 进入超级用户模式。也就是输入”su -“,系统会让你输入超级用户密码,输入密码后就进入了超级用户模式。(当然,你也可以直接用root用)
  2. 添加文件的写权限。也就是输入命令chmod u+w /etc/sudoers
  3. 编辑/etc/sudoers文件。也就是输入命令vim /etc/sudoers,输入”i”进入编辑模式,找到这一 行:root ALL=(ALL) ALL在起下面添加xxx ALL=(ALL) ALL(这里的xxx是你的用户名),然后保存(就是先按一 下Esc键,然后输入”:wq”)退出。
  4. 撤销文件的写权限。也就是输入命令chmod u-w /etc/sudoers

Use Go Channels as Promises and Async/Await

原文地址:https://levelup.gitconnected.com/use-go-channels-as-promises-and-async-await-ee62d93078ec

If you’ve ever programmed with Javascript, you definitely know about Promise and async/await. C#, *Java, Python, *and some other programming languages apply the same pattern but with other names such as Task or Future.

On the contrary, Go doesn’t follow the pattern at all. Instead, it introduces goroutines and channels. However, it isn’t difficult to replicate the pattern with goroutines and channels.


Single async/await

First, let’s experiment with a simple use case: await a result from an async function.

1
2
3
4
5
6
7
8
9
10
// Javascript.

const longRunningTask = async () => {
// Simulate a workload.
sleep(3000)
return Math.floor(Math.random() * Math.floor(100))
}

const r = await longRunningTask()
console.log(r)
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
// Go.

package main

import (
"fmt"
"math/rand"
"time"
)

func longRunningTask() <-chan int32 {
r := make(chan int32)

go func() {
defer close(r)

// Simulate a workload.
time.Sleep(time.Second * 3)
r <- rand.Int31n(100)
}()

return r
}

func main() {
r := <-longRunningTask()
fmt.Println(r)
}

Single async/await in Javascript vs. Golang

To declare an “async” function in Go:

  • The return type is <-chan ReturnType.
  • Within the function, create a channel by make(chan ReturnType) and return the created channel at the end of the function.
  • Start an anonymous goroutine by go func() {...} and implement the function’s logic inside that anonymous function.
  • Return the result by sending the value to channel.
  • At the beginning of the anonymous function, add defer close(r) to close the channel once done.

To “await” the result, simply read the value from channel by v := <- fn().


Promise.all()

It’s very common that we start multiple async tasks then wait for all of them to finish and gather their results. Doing that is quite simple in both Javascript and Golang.

1
2
3
4
5
6
7
8
9
10
// Javascript.

const longRunningTask = async () => {
// Simulate a workload.
sleep(3000)
return Math.floor(Math.random() * Math.floor(100))
}

const [a, b, c] = await Promise.all(longRunningTask(), longRunningTask(), longRunningTask())
console.log(a, b, c)
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
// Go.

package main

import (
"fmt"
"math/rand"
"time"
)

func longRunningTask() <-chan int32 {
r := make(chan int32)

go func() {
defer close(r)

// Simulate a workload.
time.Sleep(time.Second * 3)
r <- rand.Int31n(100)
}()

return r
}

func main() {
aCh, bCh, cCh := longRunningTask(), longRunningTask(), longRunningTask()
a, b, c := <-aCh, <-bCh, <-cCh

fmt.Println(a, b, c)
}

We have to do it in 2 lines of code and introduce 3 more variables, but it’s clean and simple enough.

We can not do <-longRun(), <-longRun(), <-longRun(), which will longRun() one by one instead all in once.


Promise.race()

Sometimes, a piece of data can be received from several sources to avoid high latencies, or there’re cases that multiple results are generated but they’re equivalent and the only first response is consumed. This first-response-win pattern, therefore, is quite popular. Achieving that in both Javascript and Go is very simple.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Javascript.

const one = async () => {
// Simulate a workload.
sleep(Math.floor(Math.random() * Math.floor(2000)))
return 1
}

const two = async () => {
// Simulate a workload.
sleep(Math.floor(Math.random() * Math.floor(1000)))
sleep(Math.floor(Math.random() * Math.floor(1000)))
return 2
}

const r = await Promise.race(one(), two())
console.log(r)
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
// Go.

package main

import (
"fmt"
"math/rand"
"time"
)

func one() <-chan int32 {
r := make(chan int32)

go func() {
defer close(r)

// Simulate a workload.
time.Sleep(time.Millisecond * time.Duration(rand.Int63n(2000)))
r <- 1
}()

return r
}

func two() <-chan int32 {
r := make(chan int32)

go func() {
defer close(r)

// Simulate a workload.
time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))
time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))
r <- 2
}()

return r
}

func main() {
var r int32
select {
case r = <-one():
case r = <-two():
}

fmt.Println(r)
}

select-case is the pattern that Go designed specifically for racing channel operations. We can even do more stuff within each case, but we’re focusing only on the result so we just leave them all empty.


Promise.then() and Promise.catch()

Because Go’s error propagation model is very different from Javascript, there’s any clean way to replicate Promise.then() and Promise.catch(). In Go, error is returned along with return values instead of being thrown as exception. Therefore, if your function can fail, you can consider changing your return <-chan ReturnType into <-chan ReturnAndErrorType, which is a struct holding both the result and error.

SpringBoot中统一包装响应

SpringBoot 中可以基于 ControllerAdviceHttpMessageConverter 实现对数据返回的包装。

实现如下,先来写一个 POJO 来定义一下返回格式:

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
import com.example.demo.common.exception.base.ErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public class Response<T> {

private int code = HttpStatus.OK.value();

private String msg = "success";

private T data;

public Response(T data) {
this.data = data;
}

public Response(int code, String msg) {
this.code = code;
this.msg = msg;
}

public Response(int code, T data) {
this.code = code;
this.data = data;
}

public Response(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.msg = errorCode.getMessage();
}

public Response(ErrorCode errorCode, T data) {
this.code = errorCode.getCode();
this.msg = errorCode.getMessage();
this.data = data;
}
}

这里用到了lomboklombok的使用介绍不在本文范围内。

用一个 ResponseBodyAdvice 类的实现包装 Controller 的返回值:

以下是我以前的实现方式:

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
import com.example.demo.common.RequestContextHolder;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class FormatResponseBodyAdvice implements ResponseBodyAdvice {
private static Logger logger = LoggerFactory.getLogger(FormatResponseBodyAdvice.class);

@Autowired
private ObjectMapper objectMapper;

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

Object wrapperBody = body;
try {
if (!(body instanceof Response)) {
if (body instanceof String) {
wrapperBody = objectMapper.writeValueAsString(new Response<>(body));
} else {
wrapperBody = new Response<>(body);
}
}
} catch (Exception e) {
logger.error("request uri path: {}, format response body error", request.getURI().getPath(), e);
}
return wrapperBody;
}

}

为什么要对返回类型是 String 时进行特殊处理呢?因为如果直接返回 new Response<>(body) 的话,在使用时返回 String 类型的话,会报类型转换异常,当时也没有理解什么原因导致的,所以最后使用了 jacksonResponse 又做了一次序列化。

今天找到了导致这个异常的原因:

因为在所有的 HttpMessageConverter 实例集合中,StringHttpMessageConverter 要比其它的 Converter 排得靠前一些。我们需要将处理 Object 类型的 HttpMessageConverter 放得靠前一些,这可以在 Configuration 类中完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2HttpMessageConverter());
}
}

然后 FormatResponseBodyAdvice 就可以修改为如下实现:

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
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;


@ControllerAdvice
public class FormatResponseBodyAdvice implements ResponseBodyAdvice {

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

if (!(body instanceof Response)) {
return new Response<>(body);
}

return body;

}
}

比之前的实现方式优雅了很多而且不用再处理 jackson 的异常了。

写一个 Controller 来尝试一下:

1
2
3
4
5
6
7
8
9
@RestController
public class HelloController {

@GetMapping("/hello")
public String hello() {
return "hello world!";
}

}

请求这个端点得到结果:

1
2
3
4
5
{
"code": 200,
"msg": "success",
"data": "hello world!"
}

说明我们的配置是成功的,同时可以在相应头中看到:

1
content-type: application/json;charset=UTF-8

如果是之前的实现方式,这里的值就是:

1
content-type: html/text

也不太符合 restful 规范。

转载自:https://jpanj.com/2018/SpringBoot-%E4%B8%AD%E7%BB%9F%E4%B8%80%E5%8C%85%E8%A3%85%E5%93%8D%E5%BA%94/

Git清理历史提交记录

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

git checkout --orphan latest_branch

# 2. Add all the files

git add -A

# 3. Commit the changes

git commit -am "commit message"

# 4. Delete the branch

git branch -D master

# 5. Rename the current branch to master

git branch -m master

# 6. Finally, force update your repository

git push -f origin master

HexoClient1.3.3版本发布

更新内容

  • 加上一个启动失败的错误引导。
  • 修复好博客导航数据没有分页的问题。
  • 修改好博客导航页面布局,页面更美观。

扫码进群交流

image.png

功能预览

image.png
image.png

相关链接

HexoClient1.3.2版本发布

更新内容

  • 新增好博客导航功能,搜集和推荐优质技术博客(欢迎自荐和推荐优质博客)
  • 添加QQ交流群(QQ群:618213781)

扫码进群交流

image.png

功能预览

image.png
image.png

相关链接

HexoClient1.3.1版本发布

更新内容

  • 修复检查更新提示错误。#64
  • 修复Windows系统下的一个样式错误。#65

功能预览

image.png
image.png

相关链接

HexoClient1.3.0版本发布

更新内容

  • 修复阿里云oss图片上传后url不正确的问题。#60
  • 支持一键调用hexo generate -d命令发布文章,thanks EVINK
    image.png

功能预览

image.png
image.png

相关链接