woshidan's blog

あいとゆうきとITと、とっておきの話。

memcachedの使い方について簡単にメモします

動作環境は、Max OS X EI Captain 10.11.4, memcached 1.4.24 です。

memcachedのインストール

# http://qiita.com/makotok7/items/9998b15f79fc7a53af24
brew install memcached

localhostの11211ポートでmemcachedサーバを起動

$ memcached -p 11211 -d

詳しいオプションについては

telnetmemcachedサーバに接続

localhostの11211ポートでmemcachedサーバが動作しているとします。

$ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.

memcachedサーバにデータを入れる

参考: https://www.tutorialspoint.com/memcached/memcached_set_data.htm http://l-w-i.net/t/memcached/command_001.txt

set [KVSのキー] [非圧縮/圧縮などのフラグ] [データの有効期限(秒). 0の場合は永続的に保存] [保存するデータのバイト数](return/enter)
[KVSの値](return/enter)

123という3バイトの文字列をfooというキーに非圧縮で900秒保存させる
set foo 0 900 3(return/enter)
123(return/enter)
STORED

memcachedサーバにデータの取得と更新

参考: http://l-w-i.net/t/memcached/command_001.txt

get foo
VALUE foo 0(非圧縮) 3(データ長:3バイト)
123
END
gets foo   
VALUE foo 0(非圧縮) 3(データ長:3バイト) 2(cas ID)
123
END

getsで追加で表示されている最後の数字は、cas IDといい、replaceコマンドでmemcached上の同じキーに対する値を書き換えるたびに変化します。

replace foo 0 900 3
345
STORED
gets foo 
VALUE foo 0 3 3
345
END

ユーザーAがmemcachedからデータを取得して処理したあとに保存しようとしたら、すでに他のユーザーBによって変更が加えられており、素直にユーザーAの計算結果を保存したらBの変更の履歴が消えてしまって困る、という場合があります。

それを避けるため、casコマンドを使って、データを取得した時のcas IDをパラメータに与えて、「あの時からお変わりなかったら、変更したいんですけどー」という感じで変更コマンドを打ちます。

(ユーザーA)
gets foo
=> VALUE foo 0 3 7
=> 123
=> END

(ユーザーB)
replace foo 0 0 3
abc
=> STORED ← 他のユーザーによりデータは更新されてcas IDは変化

(ユーザーA)
cas foo 0 0 3 7
zzz
=> EXISTS 既に他のユーザがデータを書き換えたのでzzzは書き込めない

memcahcedサーバに入っているデータの状態について調べる

参考: http://taka512.hatenablog.com/entry/20100324/1269428213

stats
STAT pid 8365 
STAT uptime 2670 起動時間
STAT time 1476973152
STAT version 1.4.24
STAT libevent 2.0.22-stable
STAT pointer_size 64 OSが32bit又は64bit 
STAT rusage_user 0.037374 プロセスがユーザモードで動作した累計時間?(秒.マイクロ秒)
STAT rusage_system 0.061396 プロセスがカーネルモードで動作した累計時間?(秒.マイクロ秒)
STAT curr_connections 10 現在のデータ数
STAT total_connections 11
STAT connection_structures 11 memcacheが確保したコネクション構造体数
STAT reserved_fds 20
STAT cmd_get 6 GETコマンド発行の累計
STAT cmd_set 7 SETコマンド発行の累計
STAT cmd_flush 0
STAT cmd_touch 0
STAT get_hits 5 リクエストでキーが見つかった数
STAT get_misses 1 リクエストでキーが見つからなかった数
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 1
STAT touch_hits 0
STAT touch_misses 0
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 284 ネットワークから受信したバイト数
STAT bytes_written 292 ネットワークへ送信したバイト数
STAT limit_maxbytes 67108864 memcacheの最大容量(バイト)
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT hash_power_level 16
STAT hash_bytes 524288
STAT hash_is_expanding 0
STAT malloc_fails 0
STAT bytes 71
STAT curr_items 1
STAT total_items 3
STAT expired_unfetched 1
STAT evicted_unfetched 0
STAT evictions 0
STAT reclaimed 1
STAT crawler_reclaimed 0
STAT crawler_items_checked 0
STAT lrutail_reflocked 0
END

MySQLで外部ホストのデータベースにクエリを送りたい

最近開発するために開発する環境を作っている私です。

MySQLで外部ホストにたいしてSQLを送るにはどうしたらいいのかなーと悩んでいたのですが、あっさり解決したのであっさりメモします。

まとめ

  • MySQLにおいて基本的に外部ホストへはアクセスできないようになっている
  • MySQLにおいてデータベースにどこのホストからアクセスするかはそのホストのmysqlデータベースのuserテーブルに入っているユーザー情報で管理
  • 外部ホストにアクセスしたい場合は、アクセスしたいホストにアクセス元のサーバからのアクセスを許可したユーザーを作ってあげる
// 接続先のホストにて接続元のホスト(client_host)からアクセスするユーザーを作成
mysql> GRANT ALL PRIVILEGES ON target_database.* TO access_user@client_host IDENTIFIED BY 'pass';
mysql> SELECT host,user FROM mysql.user;
+------------------------------------+---------------+
| host                               | user          |
+------------------------------------+---------------+
| client_host                        | access_user   |
+------------------------------------+---------------+
// client_host上から
mysql -u access_user -h target_host target_database < request.sql
// 省略してますが、パスワードの入力あり

参考

MySQL:外部ホストからのDBアクセス方法

MySQLに外部ホストから接続できるように設定する | WEBサービス創造記

MySQL ユーザの操作(作成、パスワード変更、削除)

SPFってなんですか?

お仕事でSPFについて確認したのでブログにもメモを置きます。

参考: http://salt.iajapan.org/wpmu/anti_spam/admin/tech/explanation/spf/

SPFについて

  • メールの送信元の詐称を防ぐための認証技術

SPFでメールを認証するとは

SMTPでメールを送信するときはメールの送信元に関する下記の3つのドメイン(あるいはドメインのIP)はすべて違っても良いそうです。

  • a1. SMTPでメールサーバに接続してメールを送るという意味で、メール(SMTPが動いてるサーバ?)を送り出すサーバのIP cf. example.jp は 192.0.2.1
  • a2. SMTPMAIL FROMコマンドの引数となる送信者のメールアドレスのドメイン(エンベローブに入ってるらしい) cf. MAIL FROM:<sender@exmaple.org>
  • a3. 電子メールのヘッダ cf. FROM: user1@exmaple.com

そうなると、なんだか電子メールのヘッダをちょこちょこといじってメール送信元詐称が簡単にできそうですね。

SPFではメールを受信したサーバが a2 のメールアドレスのドメインSPFレコードのIPアドレスを見て、a1のサーバから送り出されたメールかどうか確認(認証)します。

SPFでメールを認証する手順

  • b1. a2ドメインで利用している権威DNSに、a2ドメインでメールを送り出すときに使うサーバのIPはこれです、とa1のメールサーバのIPアドレスを登録する(SPFレコードの登録)
  • b2. a2ドメインのアドレスがエンベローブのFROMには入っているメールを受け取ったメールサーバは、ドメインを見て、DNSexample.orgSPFレコードを問い合わせる
  • b3. b2の問い合わせの結果に、メールの送信元のサーバのIPが含まれていたら認証成功

認証に失敗したらどうなるの?

  • SPFレコードの登録がなくて失敗した ... 認証結果: None
    • 受け取り拒否にならず、迷惑メールフィルタでフィルタリングされるのに任せる場合が多いが、途中で受け取り拒否になる可能性もある
  • SPFレコードの登録があって失敗した ... 認証結果: Fail
    • RFCにおいてはこの結果に従って通信中のSMTPセッションにおいて該当メールの受信拒否を行う場合、550番エラーの利用を勧めているとあるので受け取り拒否になる可能性高そう

他にもありますが、参考元をご確認ください。

SPFでメールを送れなくなる理由

メールを送信するメールサーバのIPアドレスが、SMTPMAIL FROMコマンドの引数となる送信者のメールアドレスのドメインSPFレコードに入っていない?

DNSサーバの種類とDNSサーバの受ける問い合わせの種類についてまとめました

speakerdeck.com

参考のところに載せてるんですけど、DNSについて勉強する前に読みたかった資料です*1。もし、他の人がこの記事を見てDNSについてちょっと調べてみようと思ったら3分くらいは眺めてから調べ始めるとはかどると思います。

http://2014.seccon.jp/dns/dns_basics_in_30minutes.pdf

ただ、問い合わせの特徴によって覚えて区別つけるのもいいんですけど、基本的にどのプロトコルのアプリケーションやそれらのアプリケーションたるWebサービスなんかもサーバがクライアントから受けるリクエストによって挙動を変えるものだと考えると、上位から下位へ問い合わせを続ける過程で再帰的に動いて欲しいフルサービスリゾルバに飛んでくるリクエストが再帰問い合わせということになりわかりやすいのかな、と Webエンジニアは思いました。

特徴はどっちにしろ覚えないといけないんですけどね(苦笑)

DNS周りの情報は、後でDNSキャッシュポイズニングについて再帰問い合わせ周りの分も追加してやり直そうと思っているんですが、自分がググった限りJPRSの資料が一番はっきりしています。

ネットワークエンジニアとして(個人運営サイトらしいのですが、これでネットワーク勉強してました系のブログ記事とかでよく紹介されてた) => JPRSの資料(初心者網羅的な情報は多くないですが、専門家集団が出しているので概要であったり、個別トピックがすぱっとしてめっちゃわかりやすい) => @IT(個別の質問とかの記事がめっちゃ助かる) => BINDなど実装用ソフトのノウハウ系の記事、の順で巡回すると混乱が少ないのではないでしょうか。

*1:お約束としてある程度勉強してグーグル力が上がらないと当たれないんですね

DNSの概要と耐障害性、リソースレコードの読み方(BIND式)をまとめました

再帰問い合わせとかまで行こうとしたらスライドが50枚超えてたから打ち切り。

自分のスライドの作り方どこかで見たな、と思ってたんですが、ニコ動の攻略解説動画だ、たぶん。 少しずつ説明してもらわないとわかんない残念な頭をしている...!

speakerdeck.com

それにしても、実はドメインを扱ってる会社に勤めているんですが、これを作りはじめる前はDNSのAレコードって聞くと寒気してたとかは内緒にしておいてください。

DNSキャッシュポイズニングについてまとめました

ネットワークの勉強をして色々挫折中。

とりあえず、外部連携するときにFTPとか色々クライアントかませるんだけど、そのトラブルシューティングや、サービスの中でつないでいる各種サーバへの接続設定にもこの辺の知識があると便利そうということだけわかってきた感じ。

ひとまず今日からノートに書いてあるよくわかってないことを1個ずつまとめていきますよっと。

speakerdeck.com

スライドの中で紹介した Source port randomization、最初に勉強したときの資料にはほとんど出てこなくて、それはもうベンダの方で対応してるから考えなくていいということなのか、とか、再帰的問い合わせ分からんとか壁にぶつかったりしていた。

あと、スライド中の偽造用、偽装用かもしれない...。

Support LibraryのNavigationViewでどこまでできるのか試してみる

久しぶりにサイドメニューのコードいじろうかと思ったのですが、デザインのカスタマイズ性の都合でサードパーティライブラリを使うのが辛く、ここから整理するよりSupport LibraryのNavigationViewを使って書き直した方が楽ではないか、と思ったので、どこまでできるのか試してみます。

内容

  • ライブラリのバージョンについて
  • 下記のコードを試す上で基本となるレイアウトなどのソースコード
  • HeaderViewのレイアウトを変更する
  • サイドメニューの項目を変更する場合
  • サイドメニューの項目にバッジをつけたい場合
  • 下に固定のfooterをつけたい場合

ライブラリのバージョンについて

gradleで指定していたバージョンは下記。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:design:23.4.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha3'
}

下記のコードを試す上で基本となるレイアウトなどのソースコード

Android Studioに用意されているNavigation Drawer Activityのプロジェクトのものをほとんどそのまま利用しています。

一応見たいという方はこちらよりご確認ください。

HeaderViewのレイアウトを変更する

NavigationView.removeHeaderView(View view)NavigationView.addHeaderView(View view)を使えば特に難しいことを考えずサクッと入れ替えられました。

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // HeaderViewを変更できるテストとしてHeaderViewが1個以上あれば
                // HeaderViewを消すテスト
                if(navigationView.getHeaderCount() >= 1) {
                    navigationView.removeHeaderView(navigationView.getHeaderView(0));
                } else {
                    navigationView.inflateHeaderView(R.layout.nav_header_main);
                }
            }
        });

f:id:woshidan:20160907222153g:plain

サイドメニューの項目を変更する場合

一部の項目の表示/非表示にする場合。

    boolean isMenuGroup1Visible = true;


        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 一部のmenuの項目の表示/非表示を切り替えるテスト
                navigationView.getMenu().setGroupVisible(R.id.menu_group_1, !isMenuGroup1Visible);
                isMenuGroup1Visible = !isMenuGroup1Visible;
            }
        });

f:id:woshidan:20160907223531g:plain

サイドメニューの項目をがらっと変えたい場合。

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // サイドメニューの項目をmenuリソースから生成し直す場合
                navigationView.getMenu().clear();
                navigationView.inflateMenu(R.menu.test);
            }
        });
<!-- test.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_gallery"
        android:icon="@drawable/ic_menu_gallery"
        android:title="Gallery" />
    <item
        android:id="@+id/nav_slideshow"
        android:icon="@drawable/ic_menu_slideshow"
        android:title="Slideshow" />
    <item
        android:id="@+id/nav_manage"
        android:icon="@drawable/ic_menu_manage"
        android:title="Tools" />
</menu>

f:id:woshidan:20160907224022g:plain

サイドメニューの項目にバッジをつけたい場合

stackoverflow.com

menuリソースのitem要素の属性に、バッジの元となるWidgetのクラスとしてTextViewを指定して...とありましたが、この方法だと、どうも追加した要素の高さをうまく調整できなかったので、MenuItemCompat.setActionView(MenuItem menuItem, int layoutRes)メソッドで、バッジ用に用意したレイアウトを与えて、そのレイアウトを変更してやる方がいじりやすそうでした。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/nav_gallery"
        android:icon="@drawable/ic_menu_gallery"
        android:title="Gallery" />
    <item
        android:id="@+id/nav_slideshow"
        android:icon="@drawable/ic_menu_slideshow"
        android:title="Slideshow" />
    <item
        android:id="@+id/nav_manage"
        android:icon="@drawable/ic_menu_manage"
        android:title="Tools" />
</menu>
<!-- nav_menu_badge.xml  -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:textColor="@android:color/white"
        android:id="@+id/badge_text"
        android:background="@color/colorAccent"
        android:padding="4dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
        MenuItem galleryItem = MenuItemCompat.setActionView(navigationView.getMenu().findItem(R.id.nav_gallery), R.layout.nav_menu_badge);
        TextView badgeText = (TextView) galleryItem.getActionView().findViewById(R.id.badge_text);
        badgeText.setText("99");

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // バッジを消したり、内容を変更したりするテスト
                LinearLayout galleryBadge = (LinearLayout) MenuItemCompat.getActionView(navigationView.getMenu().findItem(R.id.nav_gallery));
                TextView badgeText = (TextView) galleryBadge.findViewById(R.id.badge_text);

                if(badgeText.getText() == "99+") {
                    MenuItemCompat.setActionView(navigationView.getMenu().findItem(R.id.nav_gallery), null);
                } else {
                    badgeText.setText("99+");
                }

            }
        });

f:id:woshidan:20160908001443g:plain

下に固定のfooterをつけたい場合

NavigationViewの中にandroid:layout_gravity="bottom"の指定をつけて要素を追加したらよさそうでした。

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" >

        <TextView
            android:text="Footerのテスト"
            android:gravity="center"
            android:layout_gravity="bottom"
            android:background="@color/colorAccent"
            android:padding="16dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();
    }

f:id:woshidan:20160908075544p:plain

追加したFooterとかぶった部分についてスクロールさせたいときはどうするかについては、対処が若干微妙で、アイコン透明なものを用意した選択不可能なメニューをその部分に追加するのが自分が探した中で綺麗な方の解決方法でした....^^;