Home

タムタムの日記

ATOKのカナロック解除方法

ATOKを使っていると突如カナロックがかかってしまう現象があります。わたしの環境ではメモリ不足になると(仮想メモリを使うようになると)発生するみたいです。

で、これの解除方法ですが、CTRL+SHIFT+CAPS LOCKで直りました。わたしのノートだけかもしれませんが(´・ω・)

 

zfsのキャッシュサイズ(c_max)調整

ZFSはデフォルトで搭載メモリの3/4までキャッシュするような実装になっています。メモリ4GB積んでいると3GBまでキャッシュするようになっているわけですね。ファイルサーバとしてしか使わないのであれば、それでも良いのですが、他にもメモリを使いたいのでちょっと調整してみます。

まずは、現在の値を確認します。

root@tamsvr00:~# kstat | grep c_max
        c_max                           3206670336

そして変更します。/etc/systemに以下の記述を追加します。

set zfs:zfs_arc_max = 0x40000000

これで1GBになります。再起動必須です。

再起動後、もっかい確認すると設定が変更されています。

root@tamsvr00:~# kstat | grep c_max
        c_max                           1073741824

「zfs_arc_max」で検索をすると、それっぽい記事が(というかGoogleの一番上にZFSのTuningに関するWikiが)ヒットします。

どこかの勉強会で、メモリ云々でソースを書き換えたとか言ってた人が居たので、この設定で変更できますよっと。(というかソース書き換えたって事はソース見ているって事だから設定値読んでいるって事も分かっているはず。という事は、これの事ではないのかな・・?)

 

ちなみにこのネタは会社の人に教えて貰ったものだったのでした。

ZFS on Solaris

久しぶりの更新です。
非常にバタバタしている・・というか、月300時間労働超えました(´・ω・)ウエェ・・。

クローラーを動かしたり、色々と実験をしているのでファイル置き場が無くなってその度にサーバを止めてHDDを追加していくのは大変です。というわけで、今回はそこそこ容量の大きなファイルサーバを追加しました。

構成は以下になります。

  • OpenSolaris (Solaris10はライセンスが・・・)
  • AtomD510
  • メモリ4GB
  • HDD IDE 80GB (システム用)
  • HDD SATA 2TB * 4基 (データ用)
  • GigabitLAN * 1

最初はUSBにインストールして使おうとしましたが、USBへの書き込みが発生するとシステム全体が止まって使い物にならなかったのでHDDにインストールする事にしました。

DVDドライブはないので、LiveUSBでブート。それからHDDにインストール。(Liveで起動するとインストールするというアイコンがあるので、それで)。最初はLiveUSBブート → USBにインストールでしたが、↑の理由により断念。

ZFSは非常に簡単。今回はrootで以下のコマンドを叩くだけ。

    zpool create data raidz c4t0d0 c4t1d0 c7d0 c8d0

これだけです。 これだけで/dataにマウントまでしてくれます。マウントを変えたい場合は「zfs set mountpoint」で変更するだけ。NFSで共有したい場合は、

    zfs set sharenfs=on data

これだけです。

raidzはRAID5と同等です。(いやー本当はraidz2にしたいのですがHDDが足りなくて・・・)。raid5とか会社で運用しているところがあればプギャー・・ですね。

root@tamsvr00:~# zpool list
NAME    SIZE   USED  AVAIL    CAP  HEALTH  ALTROOT
data   7.25T  1.31M  7.25T     0%  ONLINE  -
rpool    76G  6.62G  69.4G     8%  ONLINE  -
root@tamsvr00:~# zpool status
  pool: data
 state: ONLINE
 scrub: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        data        ONLINE       0     0     0
          raidz1-0  ONLINE       0     0     0
            c0t0d0  ONLINE       0     0     0
            c0t1d0  ONLINE       0     0     0
            c7d0    ONLINE       0     0     0
            c8d0    ONLINE       0     0     0

errors: No known data errors

  pool: rpool
 state: ONLINE
 scrub: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          c1d0s0    ONLINE       0     0     0
root@tamsvr00:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
rpool/ROOT/opensolaris
                       71G  4.3G   67G   7% /
swap                  4.8G  324K  4.8G   1% /etc/svc/volatile
/usr/lib/libc/libc_hwcap1.so.1
                       71G  4.3G   67G   7% /lib/libc.so.1
swap                  4.8G  8.0K  4.8G   1% /tmp
swap                  4.8G   48K  4.8G   1% /var/run
data                  5.4T  927K  5.4T   1% /data
rpool/export           67G   23K   67G   1% /export
rpool/export/home      67G   23K   67G   1% /export/home
rpool/export/home/tamtam
                       67G   45M   67G   1% /export/home/tamtam
rpool                  67G   81K   67G   1% /rpool

こんな感じでパリティ有で5.4TBの領域が出来ました。
これを各サーバで共有するのと、WindowsからはSambaでファイル置き場として共有します。CPUがあまり気味なのでMercurialやMailサーバくらい置こうかな。

レモン牛乳

栃木っこにはおなじみのレモン牛乳。
実は都内でも普通に売っています。

私はこれを飲むと運動会を思い出すのです。運動会では、このレモン牛乳(200ミリリットルのやつ)とコーヒー牛乳と普通の牛乳が売店で売り出すのです。

レモン牛乳

都内では500ミリリットルのやつが189円でした。こんなに高かったっけ・・。
というか、このヨーグルトってなんですか。こんな派生製品知らないよ!

(追記)
私がいつも購入する場所は、スーパーオオゼキの戸越銀座店です。日曜日にはいつも置いてあります。
(これ書くと私の生息範囲がわかっちゃうのと、私の購入分が減るのが嫌なんですが・・・w)

(さらに追記)
ヨーグルト食べました。
なんでもかんでも商品化すれば良いってもんじゃねーぞっ! という感想です。
「わぁー黄色い。まるでヨーグルトの糖尿病やぁ~!」

デザイン変更案

遅くなりましたが、あけましておめでとうございます。
そろそろブログのデザインを変更したいなと思いまして、ちょっとワンポイントとなるイラストを探してました。

ポップな感じにするならこんな素材かなぁ。

繊細な感じにするならこんな素材?

うーん。迷う。
コンセプトアート的な素材集ってないかなぁ・・。もうプロの人に依頼しようかな・・・w

ちなみに、いずれの画像も用途制限無しの素材なのです。画力の無い自分には嬉しい限りです。
素材集のお値段は1980円です。

DVD-ROM付き 萌えキャラフリーイラスト集

Postgresをインストールしてみた

MySQLはJDBCからアクセスする時に全件取得するのでバッチ処理が書きづらいという理由でPostgreSQLをインストールしてみました。(フェッチサイズをInteger.MIN_VALUEに設定すれば1件づつの取得になりますが、この間に他のトランザクション走らせることができないので。)(あれ・・・コネクションを2本使えばいいのでは・・?)

インストールしたバージョンは8.4.2です。

ビルドします。

./configure --prefix=/lunar
make
make check
make install

DB初期化します。

initdb --encoding=utf-8 -U totoro --pgdata=/lunar/data/postgres

起動します。

pg_ctl -w -o "-i" -D /lunar/data/postgres -l /lunar/var/postgres.log start

スキーマを作ります。

createdb -E utf-8 -O totoro -W

 外部から接続できるように/lunar/data/postgres/pg_hba.confに設定を追加します。 allとか本当は良くないけど(・ε・)キニシナイ!!

host    all         all         192.168.0.0/24        md5

パスワードを設定しなおしました。

ALTER USER totoro with password 'xxxxxxxx';

他のホストから接続してみる。

psql -h 192.168.1.202 -U totoro postgres

手間はMySQLよりも1つ多いくらい。
やばい。。Postgresの設定パラメータの意味がわからない(´・ω・)
メモリ設定とかDISKのフラッシュ設定とかログ設定とか運用設定とか。

調べるか(´・ω・)

 

ClassLoaderを使って実行時にクラスを更新する方法(リロード処理)

JavaのClassLoaderの話をちょっとだけ。意外にClassLoaderを知らない人が居るようです。

ざっくりと説明をしますと、
クラスローダーにはBootstrapクラスローダーやシステムクラスローダー、コンテキストクラスローダーがあります。(あと拡張クラスローダーですかね)。そんでもって、クラスローダーは親子関係を持っています。委譲モデルと言って、あるクラスローダーにクラスをロードする依頼があると、親に依頼を委譲します。最初に発見されたクラスが実際にロードされるわけです。

1. Bootstrap (rt.jar)
2. 親クラスローダーA
3. 子クラスローダーB

こんなクラスローダーがあるとしてまだロードされていないクラスXをロードしようとした場合は次のように動作します。

  1. B.loadClass(X)
  2. Aに委譲
  3. Bootstrapに委譲
  4. BoostrapにXは存在しない
  5. AにXは存在しない
  6. BにXが存在するのでロードする。

ちなみにClassLoader#findClassをオーバーライドして自前の処理で上書きしてしまえば、この委譲モデルを破壊する事ができます。まぁそんな事は非推奨ですけど(´・ω・)

JVMプロセス内においては異なるClassLoaderで読み込んだクラスは例え同じバイナリでも異なるクラスとして扱われます。ClassLoaderAによってロードされたクラスXと、ClassLoaderBによってロードされたクラスXは別物です。
※クラスAとBが親子関係を持っていて、AがXをロードしている時にBでロードしても委譲モデルでAが既にロードしてあるXを使うので、こういう場合は同じ定義を指します。
TomcatのWebApps1とWebApps2の関係を想像すると理解しやすいと思います。

この特性を生かすとリロード処理が実現できるわけです。

ClassLoaderの細かい話はDeveloperWorksの記事が丁寧なのでそちらを参照した方が良いです。今回はTomcatにも実装されているリロード処理のサンプルを作ってみたいと思います。TomcatのドキュメントにTomcatのClassLoaderの戦略が書いてあるのでそれを参考にすると分かりやすいと思います。

例えばRPCサーバやスケジューラーを考えます。これらを設計する時には大きく二つに分けることができ、RPCサーバではサーバ本体と各命令、スケジューラーでも本体と各タスクという感じです。Tomcatの場合はServletエンジンとビジネスロジックを書いたServlet。(ビジネスロジックはServletに書くものではありませんけどね。)
RPCの命令、スケジューラーのタスク、ServletコンテナのServletは抽象化する事ができ、インターフェースを切ることが多いです。(HttpServletとかね。)

擬似コードだとこんな感じですね。

class MyServer {
  void run() {
    ITask task = getTask();
    task.execute();
  }
}

このMyServerとITaskを本体側のClassLoaderに、ITaskを実装したクラスを別のClassLoaderに読み込ませます。
例えば本体はCLASSPATHを指定してシステムクラスローダーに読み込ませます。(TomcatはBoostrap用のクラスを挟んでいますけどね。)
タスクは独自のクラスローダーをインスタンス化(このオブジェクトをc1とする)して(URLClassLoaderを直接でも良い)、そのClassLoaderを使ってクラス定義を読み込ませます。C言語だとdlopenみたいなイメージです。

異なるクラスローダーから読み込んだクラスは別のクラスになるので、リロードしたい場合はClassLoaderをもう1回インスタンス化(c2)してあげればいいのです。

文章だけで説明してもアレなので簡単なサンプルを書いてみました。
Thread#setContextClassLoaderを使うともっと簡単に書けると思います。

サンプルの内容は定期的にタスクを実行するだけのものです。Reload処理はJMX経由で行うようにしました。
Eclipse上で作る時はタスククラスは別プロジェクトで作るのが良いです。同じプロジェクトに入れてしまうとシステムクラスローダーによってタスクもロードされてしまうので。色々適当です。Sleepの代わりにSemaphoreを使っている理由はsleepの場合Future#cancelで割り込みが発生しなかったからです。Semaphoreだと割り込みが入ってすぐに終了できます。
tasklist.propertiesに登録したいタスクのクラス名を書く必要があります。まぁこんな事をしなくてもLunarClassLoaderに手を加えて特定のインターフェースを実装しているクラスを検索(列挙)すれば良いだけです。今回はそこまで余力がありませんでした。 タスク用のクラスパスはソースに直書きしています。自分の環境に合わせて修正してください。VMの引数に-Dhogehogeを指定してシステムプロパティとして読み込むのが一般的な方法かと思います。

package at.orz.lunar;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class LunarBootstrap {

    public static void main(String[] args) throws Exception {

        URL[] urls = new URL[]{
            new File("C:/usr/local/eclipse3.5/workspace/lunar-subsystem/lunar-task.jar").toURI().toURL(),
            new File("C:/usr/local/eclipse3.5/workspace/lunar-subsystem/bin").toURI().toURL()
        };

        final ExecutorService service = Executors.newCachedThreadPool();
        final LunarServer lunarServer = LunarServer.newInstance(urls, Thread.currentThread().getContextClassLoader());
        final Future<?> future = service.submit(lunarServer);
        service.shutdown(); // 新規追加STOP

        MBeanServer mbServer = ManagementFactory.getPlatformMBeanServer();
        mbServer.registerMBean(new ServerOperationMXBean() {
            @Override
            public void stop() {
                lunarServer.stop();
                future.cancel(true);
                if (!service.isShutdown()) {
                    service.shutdown();
                }
            }
            @Override
            public void reload() {
                lunarServer.reload();
            }
        },  new ObjectName("at.orz.lunar.LunarBootstrap:name=LunarServer"));

    }

}

シングルインスタンスモデルにするか、プロトタイプモデルにするか迷ったのでclsMapとかでClass定義を保持しているわけです。今回はシングルインスタンスモデルにしたので実はclsMapは必要ありません。

package at.orz.lunar;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public class LunarClassLoader extends URLClassLoader {

    private HashMap<String, Class<? extends LunarTask>> clsMap = new HashMap<String, Class<? extends LunarTask>>();
    private HashMap<String, LunarTask> insMap = new HashMap<String, LunarTask>();

    public LunarClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
        init();
    }

    @SuppressWarnings("unchecked")
    private void init() {

        try {

            Enumeration<URL> en = getResources("tasklist.properties");
            while (en.hasMoreElements()) {
                URL url =  en.nextElement();

                Properties prop = new Properties();
                InputStream is = url.openStream();
                prop.load(is);
                is.close();

                for (String clsName: prop.stringPropertyNames()) {
                    try {
                        Class<?> c = loadClass(clsName);
                        if (LunarTask.class.isAssignableFrom(c)) {
                            Class<? extends LunarTask> cls = (Class<? extends LunarTask>) c;
                            clsMap.put(clsName, cls);
                            System.out.printf("register class:%s%n", clsName);

                            LunarTask task = cls.newInstance();
                            task.init();
                            insMap.put(clsName, task);
                            System.out.printf("init class:%s%n", clsName);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    void destroy() {
        for (LunarTask task : insMap.values()) {
            try {
                task.destroy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        insMap.clear();
        clsMap.clear();
    }

    public LunarTask getTask(String className) {
        return insMap.get(className);
    }

    public Iterable<LunarTask> taskIterator() {
        return new HashMap<String, LunarTask>(insMap).values();
    }

}
package at.orz.lunar;

import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class LunarServer implements Runnable, ServerOperationMXBean {

    private LunarClassLoader loader;
    private volatile boolean running;
    private LunarServer() {

    }

    @Override
    public void run() {

        ExecutorService service = Executors.newFixedThreadPool(3);

        running = true;
        while (running) {
            for (final LunarTask task : loader.taskIterator()) {
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        task.execute();
                    }
                });
            }
            try {
                Semaphore sema = new Semaphore(0);
                sema.tryAcquire(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        service.shutdown();

    }

    @Override
    public void reload() {
        LunarClassLoader curLoader = loader;
        LunarClassLoader newLoader = new LunarClassLoader(curLoader.getURLs(), curLoader.getParent());
        this.loader = newLoader;
        curLoader.destroy();
    }

    @Override
    public void stop() {
        running = false;
    }

    public static LunarServer newInstance(URL[] urls, ClassLoader parent) {
        LunarServer server = new LunarServer();
        server.loader = new LunarClassLoader(urls, parent);
        return server;
    }

}
package at.orz.lunar;

public interface LunarTask {
    public void init();
    public void destroy();
    public void execute();
}
package at.orz.lunar;

public interface ServerOperationMXBean {
    public void stop();
    public void reload();
}

ここからタスクです。Eclipse上では別プロジェクトにした方が良いです。今回は2つのクラスを登録します。

package at.orz.lunar.task;

import at.orz.lunar.LunarTask;

public class SampleTask1 implements LunarTask {
    @Override
    public void destroy() {
        System.out.println("SampleTask1.destroy()");
    }
    @Override
    public void execute() {
        System.out.println("☆SampleTask1.execute()");
    }
    @Override
    public void init() {
        System.out.println("SampleTask1.init()");
    }
}
package at.orz.lunar.task;

import at.orz.lunar.LunarTask;

public class SampleTask2 implements LunarTask {
    @Override
    public void destroy() {
        System.out.println("SampleTask2.destroy()");
    }
    @Override
    public void execute() {
        System.out.println("★SampleTask2.execute()");
    }
    @Override
    public void init() {
        System.out.println("SampleTask2.init()");
    }
}

クラスパスのルートにtasklist.propertiesを作成します。

at.orz.lunar.task.SampleTask1
at.orz.lunar.task.SampleTask2

実行した後にSampleTask2の内容を変更(★を★★★★★に変更)した後にjconsoleからreloadを呼び出した結果が↓です。

register class:at.orz.lunar.task.SampleTask2
SampleTask2.init()
init class:at.orz.lunar.task.SampleTask2
register class:at.orz.lunar.task.SampleTask1
SampleTask1.init()
init class:at.orz.lunar.task.SampleTask1
★SampleTask2.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
☆SampleTask1.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
register class:at.orz.lunar.task.SampleTask2
SampleTask2.init()
init class:at.orz.lunar.task.SampleTask2
register class:at.orz.lunar.task.SampleTask1
SampleTask1.init()
init class:at.orz.lunar.task.SampleTask1
SampleTask1.destroy()
SampleTask2.destroy()
☆SampleTask1.execute()
★★★★★SampleTask2.execute()
☆SampleTask1.execute()
★★★★★SampleTask2.execute()

リロード処理の所にログ仕込むの忘れたのでリロードが発端になっているかは確認できませんが、リロード処理は呼ばれています(; ・`д・´)
そしてリ新しいクラスの内容が読み込まれているのも確認できます。

※途中から書くのが面倒になったのがソースからわかっちゃうと思いますが、サンプルなので気にしない(・ε・)

ただし、このような処理はちょっとミスするとPermGenが解放されずにOutOfMemoryErrorを引き起こす原因になりやすいので気をつけましょー。(IBMのVM使えばいいじゃんとかそういう野暮な事は言わないの。)

 

Solr1.4のbackupのバグ

Solr1.4ではとあるURLにアクセスする事でバックアップ(スナップショット)を取れるようになっています。

http://wiki.apache.org/solr/SolrReplication

↑のSolrのWikiにもあるようにReplicationの機能をONにする必要がありますが、スレーブの設定をしなくても動作するのでマスタのみでもきちんと動作します。
以下のURLにアクセスするとデータディレクトリと同列に「snapshot.日時」というディレクトリを作成します。

http://master_host:port/solr/replication?command=backup

この時、日付はyyyyMMddHHmmssという書式が指定されているはずなのですが、実はHHがhhになっており、午後の18時に実行しても18にならずに06になってしまいます。

SnapShooterというクラスのDATE_FMTの指定を見ると思いっきり小文字で指定されています。 

むむー。バグだよね。これは。

まぁだいたいが早朝にバックアップをすると思うから気がつかないんだと思うけど。。

Amazonの関連商品

AmazonにはProduct Advertising APIというサービスがあります。ECSとかAWSとか言われていたやつです。

関連商品の取得方法にはいくつか方法があり大きく分けると、サーチ系のAPIのResponseGroupにSimilaritiesを指定するかSimilarityLookupというAPIを使うかです。

ItemLookupのResponseGroupに指定すると5件、SimilarityLookupを使うと10件取得できます。SimilarityLookupのResponseGroupにSimilaritiesを指定するとさらにその10件の関連商品を取得できます。(まぁこの場合は殆どが被っていますが・・・。)

ただしAmazonのホームページを見ると70件近くの関連商品が表示されています。
SimilarityLookupで複数のItemIdを指定しRandom抽出(和集合)を取得するようにしても、同じ集合が取れません(´・ω・`)
商品Aの関連商品A1~A10のASINがあって、A-A1の和集合、A-A2の和集合.... をマージ等、色々試してみましたがやっぱり公式サイトと同じリストを再現できません。あそこのリストどうやって作っているのでしょう(´・ω・)

煮詰まってしまったので公式サイトのHTMLから引っこ抜いてみました。

http://www.amazon.co.jp/dp/{ASIN}

このページにアクセスしますが、User-AgentをきちんとAjaxをサポートしているブラウザのやつに指定する必要があります。適当なUser-Agentつけて送ったらJavaScriptのコードが出力されませんでした。Firefox3.5のUser-Agentを使ったらJavaScbriptのコードも出力されました。

var purchaseAsinList = new Array("aaa","bbb","ccc");

この様なコードが出力されているのでパースするだけです。Javaなら↓のようなコードでぱぱっと。

int beginIndex = html.indexOf("var purchaseAsinList");
int endIndex = html.indexOf("\n", beginIndex);
String pscript = (html.substring(beginIndex, endIndex));

Matcher m = Pattern.compile("\"(\\w+)\"").matcher(pscript);
while (m.find()) {
    System.out.println(m.group(1));
}

微妙・・。きちんとAPIを叩いて処理したいです(´・ω・)
というわけで情報募集です。

こんなコードを書いていた私涙目。

こちらの記事にあったので私はこんなコードを書いていましたという返信。

ttp://1978th.net/tech/promenade.cgi?id=68

インラインアセンブラです。
x86オンリーなのかもしれませんが私の場合は動作させる環境が固定なので。

static __inline__ void __attribute__ ((__always_inline__))
	atomic_add(volatile int *pv1, int v2)
{
	__asm__ volatile (
		"lock; addl %1,%0"
		: "=m" (*pv1)
		: "ir" (v2), "m" (*pv1));
}

これを、
#if defined(_GCC) とかそういうマクロで判定して使ってます。
速度?そんなの知りません( ゚Д゚)y─┛~~

Home

Search
Feeds

Return to page top