- sync.Map活用
- シナリオ分析基盤
curl -OL https://raw.githubusercontent.com/karamaru-alpha/isucon-memo/main/Makefile
make setup
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install -y gh
gh auth login # GitHub.com -> SSH -> /home/isucon/.ssh/id_rsa.pub -> Paste an authentication token -> https://github.com/settings/tokens
sudo cat <<EOL >> ~/.bashrc
alias cm="git commit -m"
alias ad="git add ."
alias co="git checkout"
alias save="git add . && git commit -m"
alias cob="git checkout -b"
alias mg="git merge"
alias rename="git branch -m"
alias del="git branch -D"
alias refresh="git checkout . && git clean -df"
EOL
source ~/.bashrc
git remote add origin git@github.com:karamaru-alpha/isucon12-q.git
git add . && git commit -m "init"
git branch -M master main
git push -u origin main
systemctl list-unit-files --type=service | grep -e isu -e nginx -e sql
systemctl status isu.service
arch
free -h
fgrep 'cpu cores' /proc/cpuinfo | sort -u | sed 's/.*: //'
systemctl list-unit-files --type=service | grep -e isu -e nginx -e sql
mysql --version
mysqldump -uisucon -pisucon --host 127.0.0.1 --port 3306 ${DATABASE} --compact --no-data --compact --no-data | grep -v "^SET" | grep -v "^/\*\!" | perl -ple 's@CREATE TABLE @\nCREATE TABLE @g';
SELECT
table_name, engine, table_rows,
floor((data_length+index_length)/1024/1024) AS total_mb,
floor((data_length)/1024/1024) AS data_mb,
floor((index_length)/1024/1024) AS index_mb
FROM
information_schema.tables
WHERE
table_schema=database()
ORDER BY
(data_length+index_length) DESC;
- Makefileに変数を適用する
- 設定ファイルをホームディレクトリに持ってくる (
mysql --help | grep my.cnf
で探せる)
cp /etc/mysql/my.cnf my.cnf
- スロークエリを吐くようにmy.cnf設定する
sudo touch /var/log/mysql/slow-query.log
sudo chown -R mysql /var/log/mysql/slow-query.log
[mysqld]
log_error = /var/log/mysql/error.log
slow_query_log_file = /var/log/mysql/slow-query.log
slow_query_log = ON
long_query_time = 0.0
log_output = FILE
# general_log = OFF 計測が終わったら上記をコメントアウトしこの行を追加
skip-log-bin
sudo systemctl restart mysql
- 設定ファイルをホームディレクトリに持ってくる
cp /etc/nginx/nginx.conf nginx.conf
cp /etc/nginx/sites-enabled/$(APP).conf $(APP)
- アクセスログを吐くようにnginx.confを設定する
sudo touch /var/log/nginx/access.log
sudo touch /var/log/nginx/error.log
sudo chmod 777 /var/log/nginx/access.log
sudo chmod 777 /var/log/nginx/error.log
http {
log_format with_time '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time';
access_log /var/log/nginx/access.log with_time;
}
sudo touch /var/log/go.log
sudo chmod 777 /var/log/go.log
import (
"log"
log2 "github.com/labstack/gommon/log"
)
func main() {
log.SetFlags(log.Lshortfile)
logfile, err := os.OpenFile("/var/log/go.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic("cannnot open test.log:" + err.Error())
}
defer logfile.Close()
log.SetOutput(logfile)
log.Print("main!!!!")
e.Logger.SetOutput(logfile)
e.Logger.SetLevel(log2.ERROR)
}
package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Fatal(http.ListenAndServe(":6060", nil))
}()
}
sudo apt install -y graphviz
go tool pprof -http=0.0.0.0:1080 http://localhost:6060/debug/pprof/profile\?seconds=60
-> http://localhost:6060/debug/pprof
wget https://go.dev/dl/go1.19.linux-amd64.tar.gz -O go.tar.gz
sudo tar -C /usr/local -xzf go.tar.gz
sudo rm -rf go.tar.gz
sudo cat <<EOL >> ~/.bashrc
export PATH=$PATH:/usr/local/go/bin
EOL
source ~/.bashrc
go tool dist list
GOOS=linux GOARCH=arm64 go build -o isucon *.go
package main
import (
"log"
"sync"
"time"
"golang.org/x/sync/singleflight"
)
var flight singleflight.Group
func main() {
// 同一 name が処理中なら一緒に結果を待つ
v, err, _ := flight.Do("group1", func() (interface{}, error) {
// 時間がかかる処理
return time.Now(), nil
})
if err != nil {
log.Fatal(err)
}
}
func main() {
http.DefaultTransport.(*http.Transport).MaxIdleConns = 0
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 1024
http.DefaultTransport.(*http.Transport).ForceAttemptHTTP2 = true
}
- goccy/go-json
import (
"github.com/goccy/go-json"
"github.com/labstack/echo/v4"
)
type JSONSerializer struct{}
func (j *JSONSerializer) Serialize(c echo.Context, i interface{}, indent string) error {
return json.NewEncoder(c.Response()).Encode(i)
}
func (j *JSONSerializer) Deserialize(c echo.Context, i interface{}) error {
return json.NewDecoder(c.Request().Body).Decode(i)
}
func main() {
e := echo.New()
e.JSONSerializer = &JSONSerializer{}
}
- bytedance/sonic
import (
"github.com/bytedance/sonic/decoder"
"github.com/bytedance/sonic/encoder"
"github.com/labstack/echo/v4"
)
type JSONSerializer struct{}
func (j *JSONSerializer) Serialize(c echo.Context, i interface{}, indent string) error {
buf, err := encoder.Encode(i, 0)
if err != nil {
return err
}
_, err = c.Response().Write(buf)
return err
}
func (j *JSONSerializer) Deserialize(c echo.Context, i interface{}) error {
var buf bytes.Buffer
buf.ReadFrom(c.Request().Body)
return decoder.NewDecoder(buf.String()).Decode(i)
}
func main() {
e := echo.New()
e.JSONSerializer = &JSONSerializer{}
}
- map[T]
package main
import (
"time"
"github.com/patrickmn/go-cache"
)
type Cache[V any] struct {
cache *cache.Cache
}
func (c *Cache[V]) Get(key string) (V, bool) {
v, ok := c.cache.Get(key)
if ok {
return v.(V), true
}
var defaultValue V
return defaultValue, false
}
func (c *Cache[V]) Set(key string, value V, ttl time.Duration) {
c.cache.Set(key, value, ttl)
}
func (c *Cache[V]) Flush() {
c.cache.Flush()
}
type User struct {
Name string
}
var userCache = Cache[User]{
cache: cache.New(cache.NoExpiration, cache.NoExpiration),
}
func main() {
user := User{Name: "karamaru"}
userCache.Set(user.Name, user, time.Second*1)
userCache.Get(user.Name)
}
- slice[T]
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
type Cache[V any] struct {
cache *cache.Cache
}
func (c *Cache[V]) Get(key string) (V, bool) {
v, ok := c.cache.Get(key)
if ok {
return v.(V), true
}
var defaultValue V
return defaultValue, false
}
func (c *Cache[V]) Set(key string, value V, ttl time.Duration) {
c.cache.Set(key, value, ttl)
}
func (c *Cache[V]) Flush() {
c.cache.Flush()
}
type User struct {
Name string
}
var userCache = Cache[[]User]{
cache: cache.New(cache.NoExpiration, cache.NoExpiration),
}
func main() {
users := []User{{Name: "karamaru"}}
userCache.Set("users", users, time.Second*1)
}
- map1:1
type omIsuT struct {
M sync.RWMutex
V map[string]*Isu
}
var omIsu omIsuT
func (o *omIsuT) Get(k string) (*Isu, bool) {
o.M.RLock()
v, ok := o.V[k]
o.M.RUnlock()
return v, ok
}
func (o *omIsuT) Set(v Isu) {
o.M.Lock()
o.V[v.ID] = v
o.M.Unlock()
}
func main() {
omIsu = omIsuT{
V: map[string]&Isu{}
}
}
- map1:N
type omIsuListT struct {
M sync.RWMutex
V map[string][]*Isu
}
var omIsuList omIsuListT
func (o *omIsuListT) Get(k string) ([]*Isu, bool) {
o.M.RLock()
v, ok := o.V[k]
o.M.RUnlock()
return v, ok
}
func (o *omIsuListT) Set(k string, v []*Isu) {
o.M.Lock()
o.V[k] = append(o.V[k], v...)
o.M.Unlock()
}
func main() {
omIsuList = omIsuListT{
V: map[string][]&Isu{} // make(map[string][]Isu, len(hogehoge))
}
}
- slice
type omIsuListT struct {
M sync.RWMutex
V []*Isu
}
var omIsuList omIsuListT
func (o *omIsuListT) Get() ([]*Isu) {
o.M.RLock()
defer o.M.RUnlock()
return o.V
}
func (o *omIsuListT) Set(v []*Isu) {
o.M.Lock()
o.V = append(o.V, v...)
o.M.Unlock()
}
func main() {
omIsuList = omIsuListT{}
}
- slice期限付き
type omIsuListT struct {
M sync.RWMutex
T time.Time
V []*Isu
}
var omIsuList omIsuListT
func (o *omIsuListT) Get() ([]*Isu, bool) {
o.M.RLock()
defer o.M.RUnlock()
if o.T.After(time.Now()) {
return o.V, true
}
return nil, false
}
// 完全置換+期限伸ばす
func (o *omIsuListT) Set(v []*Isu) {
o.M.Lock()
o.T = time.Now().Add(time.Second * 1) // キャッシュ時間
o.V = v
o.M.Unlock()
}
func main() {
omIsuList = omIsuListT{
T: time.Now(),
}
}
// init 時の掃除&ディレクトリ設置
func initialize() {
if err = os.RemoveAll(iconFilePath); err != nil {
return c.NoContent(http.StatusInternalServerError)
}
if err := os.MkdirAll(iconFilePath, os.ModePerm); err != nil {
return c.NoContent(http.StatusInternalServerError)
}
}
// 書き込み
func write() {
for _, v := range isuImages {
if err := os.WriteFile(fmt.Sprintf("%s/%s_%s", iconFilePath, v.JIAUserID, v.JIAIsuUUID), v.Image, os.ModePerm); err != nil {
c.Logger().Error(err)
return c.NoContent(http.StatusInternalServerError)
}
}
}
// 読み込み
func read() {
image, err := os.ReadFile(fmt.Sprintf("%s/%s_%s", iconFilePath, jiaUserID, jiaIsuUUID))
}
// コピー(読み込みより早い)
func copy() {
file, err := os.Open(defaultIconFilePath)
defer file.Close()
f, err := os.Create(fmt.Sprintf("%s/%s_%s", iconFilePath, jiaUserID, jiaIsuUUID))
defer f.Close()
_, err = io.Copy(f, file)
}
cf. https://github.com/narusejun/isucon11-qualify/commit/e3cc31346fb89455a0f0123e9ea08156914e28c4
cf. https://github.com/tatsumack/isu11q/commit/ae2da92c4a11b184c9fb479f7cde39e935140baf
func loop() {
for range time.Tick(time.Second) {
// something to do
}
}
func main() {
socket_file := "/home/isucon/webapp/tmp/app.sock"
os.Remove(socket_file)
l, err := net.Listen("unix", socket_file)
if err != nil {
e.Logger.Fatal(err)
}
// go runユーザとnginxのユーザ(グループ)を同じにすれば777じゃなくてok
err = os.Chmod(socket_file, 0777)
if err != nil {
e.Logger.Fatal(err)
}
e.Listener = l
e.Logger.Fatal(e.Start(""))
// http.Serve(l, mux)
}
upstream s1 {
server unix:/home/isucon/webapp/tmp/app.sock;
keepalive 32;
keepalive_requests 10000;
}
location /api {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://s1;
}
mkdir tmp
-> gitignore
cf. https://github.com/narusejun/isucon11-qualify/commit/3d9f96bfe12f263a0ea8f3aa759b8e73c2659f0a
func main() {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://172.31.5.58:5000/initialize/isu3", nil)
if err != nil {
panic(err)
}
_, err = http.DefaultClient.Do(req)
if err != nil {
return err
}
sudo -u -i isucon
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install -y percona-toolkit gh
gh auth login
cd ./webapp
git init
git config --global user.name karamaru-alpha
git config --global user.email mrnk3078@gmail.com
git config --global pull.rebase false
git config credential.helper store
git remote add origin git@github.com:karamaru-alpha/isucon12-q.git
git fetch origin main && git reset --hard origin/main
git branch -M master main
sudo touch /var/log/mysql/slow-query.log
sudo chown -R mysql /var/log/mysql/slow-query.log
sudo cat <<EOL >> ~/.bashrc
alias cm="git commit -m"
alias ad="git add ."
alias co="git checkout"
alias save="git add . && git commit -m"
alias cob="git checkout -b"
alias mg="git merge"
alias rename="git branch -m"
alias del="git branch -D"
alias refresh="git checkout . && git clean -df"
alias pullf='function _pullf() { git fetch origin $@ && git reset --hard origin/$@; }; _pullf'
EOL
source ~/.bashrc
- goのmysql.openとsqlのinit.shもHOSTをプライベートアドレスに変更する
- '%'のユーザー作成も忘れずに!
# MariaDBの場合
/etc/mysql/mariadb.conf.d/*-server.cnf
bind-address = 0.0.0.0
SELECT user, host FROM mysql.user;
DROP USER 'isucon'@'%';
CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon';
GRANT ALL PRIVILEGES ON * . * TO 'isucon'@'%';
FLUSH PRIVILEGES;
## delete
sudo apt-get remove --purge mysql-server* mysql-common
sudo rm -r /etc/mysql
sudo rm -r /var/lib/mysql
sudo apt autoremove -y
sudo apt clean
sudo aa-remove-unknown
# install
sudo apt install apt-transport-https
sudo curl -LsS -O https://downloads.mariadb.com/MariaDB/mariadb_repo_setup
sudo bash mariadb_repo_setup --mariadb-server-version=10.9
sudo rm mariadb_repo_setup
sudo apt update
sudo apt install mariadb-server mariadb-common -y
sudo mysqld --version
## delete
sudo rm -r /etc/ld.so.conf.d/mysql /etc/mysql/my.cnf /usr/local/mysql /var/log/mysql/error.log
sudo apt purge mariadb-server
sudo apt autoremove -y
sudo apt clean
sudo aa-remove-unknown
# install
sudo apt install apt-transport-https
sudo curl -LsS -O https://downloads.mariadb.com/MariaDB/mariadb_repo_setup
sudo bash mariadb_repo_setup --mariadb-server-version=10.9
sudo rm mariadb_repo_setup
sudo apt update
sudo apt install mariadb-server -y
sudo mysqld --version
SHOW ENGINE INNODB STATUS;
- Unknown collation: 'utf8mb4_0900_ai_ci'
- sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' sql/dump.sql
func main() {
// db.Open の直後
for {
if err := db.Ping(); err == nil {
break
}
time.Sleep(time.Second * 1)
}
}
func bulkInsert(isuList []Isu) {
args := make([]interface{}, 0, len(isuList)*3)
placeHolders := &strings.Builder{}
for i, v := range isuList {
args = append(args, v.Col1, v.Col2, v.Col3)
if i == 0 {
placeHolders.WriteString(" (?, ?, ?)")
} else {
placeHolders.WriteString(",(?, ?, ?)")
}
}
_, err = db.Exec("INSERT INTO table_name(col_1, col_2, col_3) VALUES" + placeHolders.String(), args...)
}
// sqlxの場合
// db.NamedExec("INSERT INTO `latest_isu_level` (`jia_isu_uuid`, `level`) VALUES (:jia_isu_uuid, :level)", latestIsuLevels)
type Isu struct {
Col1 int `db:"col_1"`
Col2 int `db:"col_2"`
Col3 int `db:"col_3"`
}
// Col1の値を複数条件で検索
func in(col1s []int) []Isu {
var isuList []Isu
inPlaceHolders := "col_1 IN (?" + strings.Repeat(",?", len(levels)-1) + ")" // n=0の時がある場合は分岐が必要
db.Select(&isuList, `SELECT * FROM isu WHERE ` + inPlaceHolders, col1s...)
}
// sqlxの場合
// query, params, err := sqlx.In(`SELECT * FROM users WHERE id IN (?)`, []int{1,2})
// db.Select(&isuList, db.Rebind(query), params...)
func main() {
const SQL_CONN_COUNT = 20
// 最大接続数
db.SetMaxOpenConns(SQL_CONN_COUNT)
// プールできるコネクションの数
db.SetMaxIdleConns(SQL_CONN_COUNT)
// 接続が確立されてからコネクションを保持できる最大時間
db.SetConnMaxLifetime(SQL_CONN_COUNT * time.Second)
defer db.Close()
}
func main() {
db := openDB()
for {
if err := db.Ping(); err == nil {
break
}
time.Sleep(time.Second * 1)
}
popularity INT NOT NULL,
popularity_desc INT AS (-popularity) INVISIBLE, -- index 使う時は STORED
`id` CHAR(26) CHARACTER SET latin1,
-- 2_Patch.sql
DROP FUNCTION IF EXISTS UUID_TO_BIN;
CREATE FUNCTION UUID_TO_BIN(_uuid BINARY(36)) RETURNS BINARY(16) LANGUAGE SQL DETERMINISTIC CONTAINS SQL SQL SECURITY INVOKER RETURN UNHEX(
CONCAT(
SUBSTR(_uuid, 15, 4),
SUBSTR(_uuid, 10, 4),
SUBSTR(_uuid, 1, 8),
SUBSTR(_uuid, 20, 4),
SUBSTR(_uuid, 25)
)
);
DROP FUNCTION IF EXISTS BIN_TO_UUID;
CREATE FUNCTION BIN_TO_UUID(_bin BINARY(16)) RETURNS BINARY(36) LANGUAGE SQL DETERMINISTIC CONTAINS SQL SQL SECURITY INVOKER RETURN LCASE(
CONCAT_WS(
'-',
HEX(SUBSTR(_bin, 5, 4)),
HEX(SUBSTR(_bin, 3, 2)),
HEX(SUBSTR(_bin, 1, 2)),
HEX(SUBSTR(_bin, 9, 2)),
HEX(SUBSTR(_bin, 11))
)
);
ALTER TABLE user ADD COLUMN bin_uuid BINARY(16);
UPDATE user SET bin_uuid = UUID_TO_BIN(uuid);
-- uuid が PK の場合: ALTER TABLE user DROP PRIMARY KEY, ADD PRIMARY KEY (`bin_uuid`);
ALTER TABLE user DROP COLUMN uuid;
ALTER TABLE user RENAME COLUMN bin_uuid TO uuid;
-- NOT NULL などの制約があった場合は付け直す: ALTER TABLE user MODIFY COLUMN uuid BINARY(16) NOT NULL UNIQUE;
func get(uuid string) {
var user User
db.Get(&user, "SELECT BIN_TO_UUID(`uuid`) FROM user WHERE uuid = UUID_TO_BIN(?)", uuid)
}
cf. https://github.com/narusejun/isucon11-qualify/commit/fda74ca7e56b70a58a7a49c773cb892d3dae6765
INSERT INTO `user` (`id`, `name`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `name`=?;
-- bulk
INSERT INTO `user` (`id`, `name`) VALUES (:id, :name) ON DUPLICATE KEY UPDATE `name`=VALUES(`name`);
DROP TRIGGER IF EXISTS tr1;
CREATE TRIGGER tr1 BEFORE INSERT ON playlist_favorite FOR EACH ROW INSERT INTO playlist_favorite_count (playlist_id,count) VALUES (NEW.playlist_id, 1) ON DUPLICATE KEY UPDATE playlist_favorite_count.count = playlist_favorite_count.count + 1;
DROP TRIGGER IF EXISTS tr2;
CREATE TRIGGER tr2 BEFORE DELETE ON `message` FOR EACH ROW UPDATE `channel` SET `message_cnt`=`message_cnt`-1;
SELECT * FROM isu_condition AS a JOIN (SELECT user_id, MAX(created_at) AS created_at FROM isu_condition GROUP BY user_id) AS b
ON a.user_id = b.user_id WHERE a.created_at = b.created_at;
UPDATE `channel`, (SELECT channel_id, COUNT(1) AS `message_cnt` FROM `message` GROUP BY channel_id) AS `summary` SET `channel`.`message_cnt`=`summary`.`message_cnt` WHERE channel.id = summary.channel_id
func main() {
db.Exec("SELECT GET_LOCK(?, ?)", id, 5)
defer db.Exec("SELECT RELEASE_LOCK(?)", id)
}
sudo apt update
sudo apt install nginx
sudo ufw allow 'Nginx Full'
sudo systemctl enable nginx
systemctl list-unit-files --type=service
HTTP/1.1を使用する&Connectionヘッダを空にする必要がある
upstream s1 {
server 127.0.0.1:3000;
keepalive 32;
keepalive_requests 10000;
}
server {
listen 80;
root /public/;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://s1;
}
}
ps ax | grep nginx | grep worker
cat /proc/${PID}/limits
sudo mkdir /etc/systemd/system/nginx.service.d
sudo vi /etc/systemd/system/nginx.service.d/limit.conf
[Service]
LimitNOFILE=32768
sudo systemctl daemon-reload
sudo systemctl restart nginx
// root /home/isucon/webapp/public;
// index index.html;
location /assets/ {
expires 1d;
try_files $uri /index.html;
}
location ~ ^/isu/(.*?) {
rewrite .* /index.html;
alias /home/isucon/webapp/public/index.html;
}
location /register {
alias /home/isucon/webapp/public/index.html;
}
http {
# キャッシュ先のファイル指定・2階層で保存・zone1キー名で1M確保・1ギガまで使う・2分で削除
proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=zone1:1m max_size=1g inactive=2m;
proxy_temp_path /var/cache/nginx/tmp;
location /path/to {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache zone1;
proxy_cache_valid 200 302 2m;
# proxy_cache_key $scheme$proxy_host$uri$is_args$args;
proxy_pass http://s1;
}
}
sudo mkdir -p /var/cache/nginx/cache
sudo mkdir -p /var/cache/nginx/tmp
sudo chmod 777 /var/cache/nginx/cache
sudo chmod 777 /var/cache/nginx/tmp
func getIsuIcon(c echo.Context) error {
// 認証してから
// ↓必要であれば存在チェック
// if _, err := os.Stat(fmt.Sprintf("%s/%s_%s", iconFilePath, jiaUserID, jiaIsuUUID)); err != nil {
// return c.String(http.StatusNotFound, "not found: isu")
// }
c.Response().Header().Set("X-Accel-Redirect", fmt.Sprintf("/icon/%s_%s", jiaUserID, jiaIsuUUID))
return c.NoContent(http.StatusOK)
}
# ここにリクエストが来て -> app
location ^~ /api/isu/(.*)/icon {
expires 1d;
add_header cache-control public;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://app;
}
# ここでaccel-redirect <- app
location /icon/ {
internal;
alias /home/isucon/webapp/icons/;
expires 1d;
add_header cache-control public;
}
location /path {
proxy_http_version 1.1;
proxy_set_header Connection "";
if ($request_method = GET) {
proxy_pass http://app1;
break;
}
proxy_pass http://app2;
}
# limit_except GET {
# proxy_pass http://app1;
# }
# proxy_pass http://app2;
location /path {
location ~ ^/path/karamaru|karaki {
proxy_pass http://s1;
}
location /path {
proxy_pass http://s2;
}
}
# $arg_{キー}でクエリストリングが取得できる
location /query {
if ( $arg_name ~ karamaru|karaki ) {
proxy_pass http://s1;
break;
}
proxy_pass http://s2;
}
location / {
location ~ ^.*?karamaru|karaki.*$ {
proxy_pass http://s1;
}
location / {
proxy_pass http://s2;
}
}
map $http_user_agent $bot {
default 0;
"~ISUCONbot" 1;
"~Mediapartners-ISUCON" 1;
"~ISUCONCoffee" 1;
"~ISUCONFeedSeeker" 1;
"~crawler \(https://isucon\.invalid/(support/faq/|help/jp/)" 1;
"~isubot" 1;
"~Isupider" 1;
"~*(bot|crawler|spider)(?:[-_ .\/;@()]|$)" 1;
}
server {
root /home/isucon/isucon10-qualify/webapp/public;
listen 80 default_server;
listen [::]:80 default_server;
if ($bot = 1) { return 503; }
# if ( $http_user_agent ~* ISUCONbot(-Mobile)? ) { return 503; }
}
gzip on;
gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg image/svg+xml image/x-icon application/octet-stream;
gzip_min_length 1k;
location /asset/ {
gzip_static always;
gunzip on;
expires 1d;
try_files $uri /index.html;
}
sudo vi /etc/security/limits.conf
* hard nofile 10240
* soft nofile 10240
ulimit -n
cd /etc/systemd/system
sudo vim golang.service
---
[Unit]
Description = isucon go servce
[Service]
ExecStart=/home/isucon/webapp/golang/isucon
WorkingDirectory=/home/isucon/webapp/golang
Restart=always
Type=simple
User=isucon
Group=isucon
# EnvironmentFile=/home/isucon/env.shcm
# (file size)
LimitFSIZE=infinity
# (cpu time)
LimitCPU=infinity
# (virtual memory size)
LimitAS=infinity
# (open files)
LimitNOFILE=64000
# (processes/threads)
LimitNPROC=64000
[Install]
WantedBy = multi-user.target
---
sudo systemctl daemon-reload
sudo権限があるuserで
sudo usermod -s /bin/bash isucon
cat ~/.ssh/id_rsa.pub | pbcopy
sudo chmod 0700 ~/.ssh
sudo vim ~/.ssh/authorized_keys
sudo chmod 0600 ~/.ssh/authorized_keys
sudo systemctl restart sshd.service