WESEN / zh-CN wesen,追求事务的本质,学习也必有其规律性。用心把分享网络推广技巧和职场技能这件事,做好。 Sun, 10 Jan 2021 13:37:00 +0800 Sun, 10 Jan 2021 13:37:00 +0800 [go]常用go包集锦 /post/112.html /post/112.html Sun, 10 Jan 2021 13:37:00 +0800 wesen go学习中用到的包整理

第三方工具包

1.自动重载air:

go get -u github.com/cosmtrek/air

2.路由包gorilla/mux:

go get -u github.com/gorilla/mux

3.测试包stretchr/testify:

go get github.com/stretchr/testify

4.mysql驱动go-sql-driver/mysql:

go get github.com/go-sql-driver/mysql

5.ORM包:gorm.io/gorm

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

6.配置库spf13/viper:

go get github.com/spf13/viper

7.会话管理gorilla/sessions:

go get -u github.com/gorilla/sessions

8.加解密crypto/bcrypt:

go get golang.org/x/crypto/bcrypt

]]>
0 /post/112.html#comments /feed/post/112.html
[go]初始化go项目 /post/111.html /post/111.html Sun, 10 Jan 2021 13:33:00 +0800 wesen 从零开始学go,项目所需的初始化配置都记录在这里

一、安装

1.下载安装包

养成习惯,从官网下载最新的安装包安装

cd ~ 
wget -c https://golang.google.cn/dl/go1.15.6.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.15.6.linux-amd64.tar.gz
rm go1.15.6.linux-amd64.tar.gz

2.添加环境变量并检查安装结果

vim ~/.bashrc
#配置go环境变量
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=$HOME/Applications/Go

保存退出

#更新
source ~/.bashrc
go version

3.启用Go modules并修改模块代理

Go modules通过配置GO111MODULE开启或关闭,默认auto,启用Go modules时,模块源使用国内资源。建议弃用$GOPATH,以便能在任意地方创建的 Go 项目,并实现依赖版本控制。
direct是用来告诉go get在获取源码包时先尝试https://goproxy.cn,如果遇到 404 等错误时,再尝试从源地址抓取

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

4.创建项目并初始化

mkdir godemo
cd godemo
go mode init godemo

5.自动重载

使用air实现自动重载
安装:

go get -u github.com/cosmtrek/air

添加别名(mod 模式下可能无法访问air命令)vim ~/.bashrc,在最后添加alias air="$GOPATH/bin/air",保存后source ~/.bashrc更新.air -v查看安装结果
使用:

air
]]>
0 /post/111.html#comments /feed/post/111.html
[nginx]反向代理后,laravel获取用户真实ip /post/110.html /post/110.html Fri, 08 Jan 2021 16:21:19 +0800 wesen 需求描述

laravel+react前后端分离不同源项目,api接口被throttle斌率限制中间件拦截:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
     ……
    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        ……
        'api' => [
            'throttle:60,1',
            'bindings',
            \Barryvdh\Cors\HandleCors::class,
        ],
       ……
    ];
    ……
}

通过nginx反向代理解决跨域问题后,laravel获取的用户ip,永远是服务器ip,导致大量访问时,api被频控。

解决方法

1.设置nginx反向代理请求头:

location ^~ /api/ {
        rewrite ^/api/(.*)$ /api/$1 break;
        include            uwsgi_params;
        proxy_method       POST;
        proxy_pass         127.0.0.1:8081;
        proxy_set_header   Host $host;
        proxy_set_header   X-real-ip $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    }

2.配置laravelApp\Http\Middleware\TrustProxies.php中间件,设置$proxies:

 protected $proxies = ['1.1.1.1'];//这里填受信代理服务器的ip
]]>
0 /post/110.html#comments /feed/post/110.html
[Redis]laravel中使用Redis分布式锁解决并发问题 /post/109.html /post/109.html Fri, 25 Dec 2020 13:15:00 +0800 wesen 需求描述

应用中一个第三方接口回调会产生并发请求,单次同时推送很多条信息,出现重复入库情况,需要在入库前拦截。

解决方案

  1. 使用laravel队列(不在此文章讨论范围);
  2. 使用Redis

实现方法

1.请求处理开始前,先尝试获取锁,如果获取成功则继续执行,否则,终止执行。加锁时,需要考虑如果后续任务执行失败,能定时清理掉该锁,以防出现死锁。代码示例如下:

/**
     * 尝试获取锁
     * @param String $key               锁
     * @param String $requestId         请求id
     * @param int $expireTime           过期时间
     * @return bool                     是否获取成功
     */
    public static function tryGetLock(String $key, int $expireTime, String $requestId) {

        $lua ="return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2])";

        $result = Redis::eval($lua, 1,$key, $requestId, $expireTime);
        return self::LOCK_SUCCESS === (String)$result;
    }

2.该请求执行完成后,解除锁。示例代码如下:

/**
     * 解除锁
     * @param String $key               锁
     * @param String $requestId         请求id
     */
    public static function releaseLock( String $key, String $requestId) {
        $lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        $result = Redis::eval($lua, 1, $key, $requestId);
        return self::RELEASE_SUCCESS === $result;
    }

完整代码

use Illuminate\Support\Facades\Redis;

class RedisTool
{
    const LOCK_SUCCESS = 'OK';
    const RELEASE_SUCCESS = 1;

    /**
     * 尝试获取锁
     * @param String $key               锁
     * @param String $requestId         请求id
     * @param int $expireTime           过期时间
     * @return bool                     是否获取成功
     */
    public static function tryGetLock(String $key, int $expireTime, String $requestId) {

        $lua ="return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2])";

        $result = Redis::eval($lua, 1,$key, $requestId, $expireTime);
        return self::LOCK_SUCCESS === (String)$result;
    }


    /**
     * 解除锁
     * @param String $key               锁
     * @param String $requestId         请求id
     */
    public static function releaseLock( String $key, String $requestId) {
        $lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        $result = Redis::eval($lua, 1, $key, $requestId);
        return self::RELEASE_SUCCESS === $result;
    }
}

总结备注

1.为什么要用Lua脚本来实现?

· 加锁时,先通过setnx加锁,然后在通过expire设置过期时间,无法保证redis原子性,在setnx执行后,程序可能挂掉,造成死锁;
· 解锁时,如果通过Redis::del($key),可能解除的是其他请求的锁;
总结:执行单个redis时,是可以保证原子性,如果是两个操作,则无法保证原子性。

2.加锁时为什么不直接用Redis::set($Key, $requestId, ['nx', 'ex' => $expireTime])?

· 这里我的laravel使用的是predisReis::set()方法不支持这种写法。

3.请求id$requestId是做什么的?怎么保证唯一?

· $requestId是区分本次请求与其他请求的标识。在加锁区间的业务执行完成后,需要解锁,$requestId保证了解锁的是当前请求的锁,而不是其他锁。
· $requestId要保证全局唯一才安全,可以使用Str::uuid()生成。


参考文章:

浅谈 Redis 分布式锁实现
Redis 使用 Lua 脚本替代 SETNX / DECR 保证原子性
]]>
0 /post/109.html#comments /feed/post/109.html
[git]全局配置中已经配置了user.name和user.email仍无法提交问题 /post/108.html /post/108.html Sat, 12 Dec 2020 12:56:09 +0800 wesen 问题描述

git提交修改是,提示未配置user.nameuser.email,git config --list查看git配置,显示正确配置了。重新执行git config --global user.name "Your Name"git config --global user.email you@example.com也无效

解决方法

可能是git无法读取全局配置,具体没有深究,通过git config --local -l查看本地配置,发现没有user.nameuser.email。重新设置,解决问题

git config user.name "Your Name"
git config user.email you@example.com
]]>
0 /post/108.html#comments /feed/post/108.html
[wsl]利用.bashrc启动时自动启动LNMP /post/107.html /post/107.html Sun, 01 Nov 2020 10:23:05 +0800 wesen 需求描述

由于wsl中无法使用开机自动启动LNMP服务,如果每次都需要一个个手动启动过于麻烦,需要实现wsl开机自动启动这些服务。

解决方法

可以将启动服务添加到.bashrc,实现打开wsl自动启动服务。.bashrc就相当于 shell 的配置文件.
系统中有多个.bashrc隐藏文件,他们的作用不同。这里主要实现打开wsl是调用,所以就使用用户文件夹下的.bashrc文件。
其路径示例:/home/username/.bashrcusername为用户名。

实现步骤

  1. 在用户文件夹下创建自启服务的startservers.sh脚本:
cd /home/username  #切换到用户文件夹

#创建startservers.sh文件

cat>startservers.sh<<EOF
#!/bin/bash
# Edit this for your relevant services.
services=(php7.3-fpm nginx redis-server mysql)
for service in "\${services[@]}"
do
         if (( \$(ps -ef | grep -v grep | grep \$service | wc -l) == 0 ))
         then
         # Uncomment to get messages. #
         #echo "Starting service \${service}"
         (sudo service \$service start > /dev/null &)
         fi
done
EOF
  1. 给与startservers.sh可执行权限
chmod +x startservers.sh
  1. startservers.sh添加到/home/username/.bashrc 文件:
sed -i '$a ~/startservers.sh' .bashrc

这样,当wsl启动后,将会自动开启配置在startservers.sh中的服务。

]]>
2 /post/107.html#comments /feed/post/107.html
[docker]docker安装-Windows10家庭版的功能中没有Hyper-V的解决方法 /post/106.html /post/106.html Sun, 13 Sep 2020 09:57:34 +0800 wesen 适用场景

截止到目前(2020年9月13日),win10家庭版系统尚不支持Hyper-V,因此需要自己添加此功能。添加后如何启用,自行找度娘。

操作步骤

1. 新建text文本,粘贴以下内容:

pushd "%~dp0"
dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt
for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"
del hyper-v.txt
Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL

2. 保存关闭text文本,修改文件扩展名为.cmd

3. 右键以管理员身份运行

4. 系统会自动开始安装,安装完成后,根据提示,键入Y重启电脑

]]>
0 /post/106.html#comments /feed/post/106.html
[supervisor]centos使用supervisor 管理laravel 队列 /post/105.html /post/105.html Mon, 01 Jun 2020 19:13:00 +0800 wesen 需求描述

使用laravel队列时,artisan命令queue:work需要保证不挂掉。但是当服务器意外重启后,需要手动启动队列,因此需要用supervisor守护进程。

实现步骤

1. 安装

sudo pip install supervisor

#或者

easy_install supervisor

2. 配置

运行supervisord服务的时候,需要指定supervisor配置文件,如果没有显示指定,默认使用/etc/supervisord.conf
为了避免重装supervisor时丢失配置,建议通过echo_supervisord_conf程序重新生成supervisor的初始化配置文件:

mkdir /etc/supervisor
echo_supervisord_conf > /etc/supervisor/supervisord.conf

打开最后的include注释:

[include]
files=/etc/supervisor/conf.d/*.conf

新增的Supervisor配置文件放在 /etc/supervisor/conf.d/目录下,并且以.conf结尾。
下面增加一个配置文件:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /www/artisan queue:work --tries=3
autostart=true
autorestart=true
user=nginx
numprocs=8
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-queue.log

3. 启动Supervisor

可以使用我们新增的配置文件启动:

supervisord -c /etc/supervisor/supervisord.conf

如果是服务启动后新增的配置文件,那么需要重载、更新及重启:

sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*

到这里如果没有报错,那么Supervisor就启动成功了。

4. 配置服务文件

/usr/lib/systemd/system/下,新建supervisord.service文件:

# supervisord service for systemd (CentOS 7.0+)
# by ET-CS (https://github.com/ET-CS)
[Unit]
Description=Supervisor daemon

[Service]
Type=forking
ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.conf //指定启动的配置文件
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

5. 开机自启服务

supervisorctl start all
systemctl enable supervisord

验证开机自启是否配置成功:

systemctl is-enabled supervisord

这里由于之前已经启动了supervisor,这里建议重启一下系统,一遍遇到奇怪的问题。然后查看服务状态,应该一切正常了。

systemctl status supervisord.service

附:配置文件说明

[unix_http_server]
file=/tmp/supervisor.sock ;UNIX socket 文件,supervisorctl 会使用
;chmod=0700 ;socket文件的mode,默认是0700
;chown=nobody:nogroup ;socket文件的owner,格式:uid:gid

;[inet_http_server] ;HTTP服务器,提供web管理界面
;port=127.0.0.1:9001 ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
;username=user ;登录管理后台的用户名
;password=123 ;登录管理后台的密码

[supervisord]
logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
logfile_backups=10 ;日志文件保留备份数量默认10,设为0表示不备份
loglevel=info ;日志级别,默认info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false ;是否在前台启动,默认是false,即以 daemon 的方式启动
minfds=1024 ;可以打开的文件描述符的最小值,默认 1024
minprocs=200 ;可以打开的进程数的最小值,默认 200

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord

; [program:xx]是被管理的进程配置参数,xx是进程的名称
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run ; 程序启动命令
autostart=true ; 在supervisord启动的时候也自动启动
startsecs=10 ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
startretries=3 ; 启动失败自动重试次数,默认是3
user=tomcat ; 用哪个用户启动进程,默认是root
priority=999 ; 进程启动优先级,默认999,值小的优先启动
redirect_stderr=true ; 把stderr重定向到stdout,默认false
stdout_logfile_maxbytes=20MB ; stdout 日志文件大小,默认50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数,默认是10
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
stopasgroup=false ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false ;默认为false,向进程组发送kill信号,包括子进程

;包含其它配置文件
[include]
files = relative/directory/*.ini ;可以指定一个或多个以.ini结束的配置文件

参考文章

centos 7 使用supervisor 管理laravel 队列
Supervisor踩过的坑
]]>
0 /post/105.html#comments /feed/post/105.html
[AS]在不打开android studio的情况下打开模拟器 /post/104.html /post/104.html Wed, 04 Mar 2020 09:06:34 +0800 wesen 需求描述

使用vscode进行react-native开发,使用android studio的模拟器,需要打开AS。如何后台直接打开安卓模拟器呢?

实现步骤

  1. 找到emulator的存放位置,一般在C:\Users\Username\AppData\Local\Android\Sdk\emulator目录下。
  2. 通过AS查看模拟器的名称,比如my_phone
  3. 新建vbs文件,并输入以下代码:
Set ws = CreateObject("Wscript.Shell") 
ws.run "C:\Users\Username\AppData\Local\Android\Sdk\emulator\emulator.exe -netdelay none -netspeed full -avd my_phone",vbhide

保存为name.vbs,点击执行即可。

]]>
0 /post/104.html#comments /feed/post/104.html
[React]扩展包-react-native-vector-icons使用记录 /post/103.html /post/103.html Mon, 02 Mar 2020 15:18:40 +0800 wesen 扩展包作用

提供各种图标
github地址:react-native-vector-icons

安装

1. npmyarn安装

#npm
npm install --save react-native-vector-icons
#yarn
yarn add react-native-vector-icons

2. Android中使用

在编译时,可选择自动加载静态文件,也可手动添加

自动打包

android/app/build.gradle添加以下代码,以便在编译应用是打包字体文件:

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

由于此包包含了多个图标字体文件,可以选择性打包指定的一个或多个字体文件:

project.ext.vectoricons = [
    iconFontNames: [ 'MaterialIcons.ttf', 'EvilIcons.ttf' ] // Name of the font files you want to copy
]

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

手动拷贝静态资源(略)

3. 建立连接

react-native link

在组件中使用

import Icon from 'react-native-vector-icons/FontAwesome';
const myIcon = <Icon name="rocket" size={30} color="#900" />;
]]>
0 /post/103.html#comments /feed/post/103.html