vimからmake

はじめに

vimを開発環境として使う場合に標準搭載されているmakeコマンドを使用すると便利です。

サンプルプログラムの作成

次の通りCのプログラムとMakefileを用意します。

$ ls
Makefile    hello.c

hello.c

#include<stdio.h>

int main(){
    printf("Hello!\n");

    return 0;
}

Makefileにはbuildとrunのタスクを定義します。

Makefile

build :
         cc hello.c
run :
        ./a.out

vimrcにショートカットを定義

vimからMakefileに定義したタスクを簡単に実行するため、 ショートカットキーを設定しておきます。 これにより、vim上ではコマンドモード、挿入モードに関わらず Ctrl+bで保存と:make buildの実行、Ctrl+uで保存と:make runの実行ができるようになります。

~/.vimrc

nmap <C-b> :w<CR>:make build<CR>
imap <C-b> <Esc>:w<CR>:make build<CR>
nmap <C-u> :w<CR>:make run<CR>
imap <C-u> <Esc>:w<CR>:make run<CR>

動作確認

hello.cの最終行から閉じ括弧をわざと削除しておきます。
f:id:hiroki-sawano:20171117023708p:plain

Ctrl+bでmake buildを実行します。
f:id:hiroki-sawano:20171117022533p:plain

実行結果はQuickfix Listで確認できます。
f:id:hiroki-sawano:20171117023755p:plain

コードを正しく修正し、再度make buildを行います。
f:id:hiroki-sawano:20171117023850p:plain

コンパイルが正常終了しました。
f:id:hiroki-sawano:20171117024015p:plain

Ctrl+uでmake runを実行します。
f:id:hiroki-sawano:20171117023856p:plain

正常にプログラムが動作しました。
f:id:hiroki-sawano:20171117022839p:plain

Remove all ads

ブロックチェーン基盤 Hyperledger Fabric v1.0 アーキテクチャ概説

はじめに

オープンソースブロックチェーン基盤であるHyperledger Fabric(v1.0)を最近学習中なので理解した範囲で本エントリーにまとめます。 Hyperledger Fabricのアーキテクチャトランザクションの流れを理解することや、 アプリケーションの実装方法を学ぶことが目的なので、ブロックチェーンそのものがどのような場面で適用できるかなどは述べる気はないです。そういった話はここを読むといいかもしれません。
Introduction — hyperledger-fabricdocs master documentation

システム・アーキテクチャ

以降ではここに書いてある説明を参考にしています。
Architecture Explained — hyperledger-fabricdocs master documentation

まず、Hyperledger Fabricでは次に示すように複数のノードが協調して元帳(Ledger)の管理を実現しています。
f:id:hiroki-sawano:20171029200149p:plain

  • Application(SDK)
    トランザクションの実行を要求します。 ClientはNode.jsやJavaで実装できます(今後PythonやGo用のSDKも提供される予定)。
  • Membership
    PeerUserの登録・承認をすることで、許可されたノードだけがブロックチェーンネットワークに参加できます。 不特定多数が参加するBitcoinのようなブロックチェーンには存在しない、Enterprise用に実装されているHyperledger Fabricならではの機能です。
  • Peer
    すべてのPeerが同じデータをPeerLedgerに保持しています。 事前にPeerにデプロイしたChaincode(スマートコントラクト)によって、同トランザクション中に各PeerLedgerが更新されます。 ChaincodeはGoやJavaで実装できます。
  • Ordering-service
    Peerトランザクションの実行を承認した後、Ordering-serviceトランザクションの順序を整列します。続けて、すべてのPeerに対して更新情報を伝達します。

トランザクションの種類

公式では次の4種類のトランザクションについて触れられています。

  • Deploy transaction
    新しいChaincodeをデプロイします。Deploy transactionの成功にともなって、Chaincodeブロックチェーン上にインストールされます。
  • Invoke transaction
    事前にデプロイされたChaincodeが提供するオペレーションを実行します。 Invoke transactionの実行は、指定するオペレーションによってLedgerの更新や参照結果の返却を伴います。
  • Query transaction(included in v1)
    v1.0で追加となった読み取り専用トランザクションです。
  • Cross-chaincode transaction(post-v1 feature)
    複数のChaincodeを使用したトランザクションですが、v1.0では未実装です。

トランザクション承認の基本フロー

前述のInvoke Transactionを例に絵を書いてみました(大体あってるはずです..)。Deploy transactionの場合には、tx.txPayloadChaincodeソースコード等が設定される点で違いがありますが流れは基本的に同じようです。Query transactionは後日確認します。

f:id:hiroki-sawano:20171029183421p:plain

要約すると次の流れになります。

  1. [Client]トランザクションの提案
    PROPOSEメッセージでどのChaincodeのメソッドを実行したいのかをEndorsing Peer(承認者となるPeer)に伝える。

  2. [Peer]トランザクションのシミュレーション
    Endorsing Peerは自身のPeerLedger上でトランザクションを仮実行し、read version dependencies(readset)とstate updates(writeset)を取得する(おそらく下図のようなイメージです...)。

f:id:hiroki-sawano:20171029210749p:plain

  1. [Peer]トランザクションの承認
    Peerは内部的にtran-proposalを自身の承認ロジックに渡す。デフォルトでは単にtran-proposalに署名するだけだが、 任意の機能を実行することも可能(e.g. txtran-proposalを入力としたレガシーシステムとの連携によるトランザクション承認)。

  2. [Client]承認されたトランザクションOrdering-serviceに送信
    TRANSACTION-ENDORSEDメッセージをすべて受信した後にOrdering-servicebroadcast(blob)を実行する。 Ordering-serviceに渡すblobは署名されたTRANSACTION-ENDORSEDメッセージのコレクションであり、endorsementとも呼ばれる。 endorsementが集まらなかった場合にはトランザクション提案を取り下げる(後でリトライすることもできるらしい)。

  3. [Ordering-service]トランザクションを整列してPeerに更新情報を転送
    トランザクションを整列して、Peerdeliverイベントを実行する。 多くの場合には効率的に処理するため、Clientから送信された各々のトランザクション(blobs)を出力せずにグルーピングし、1つのdeliverイベントにblocksを設定するとのこと。

  4. [Peer]承認内容の検証
    今回処理するトランザクションseqno未満のseqnoで処理される状態の更新が完了した後に、 blob.endorsementが妥当であるかをChaincode(blob.tran-proposal.chaincodeID)のポリシーに従って検証する。

  5. [Peer]MVCC
    read version dependencies(blob.endorsement.tran-proposal.readset)が破られていないかを検証する。 つまり、承認時のLedgerの状態が他の更新系トランザクションによって変わっていないか(Anomalyの発生)を確認する必要があるということだと思う(i.e. MultiVersion Concurrency Controlを用いたSnapshot Isolationの実装(トランザクション分離レベルではいわゆるRead Commited))。 (MVCCについては理解が浅いので別途確認します.. )

  6. [Peer]トランザクションのコミット
    トランザクションをコミットし、blob.endorsement.tran-proposal.writesetブロックチェーンの状態に反映する。

さいごに

本エントリーでは、Hyperledger Fabric v1.0の構成要素やトランザクション実行の流れを説明しました。 次回は実際に開発環境を準備し、ブロックチェーンを活用したアプリケーションの実装方法を確認していきます。

Red5でニコニコ動画のようなストリーミングサービス構築

環境

  • CentOS7

概要

4年ぐらい前に実装したニコニコ動画もどきが見つかったので軽くリファクタリングして公開してみる。 当時はオンライン講義での利用を想定して設計をしたため、 教育機関に属するユーザのロールに基づいたコメント公開範囲の制御や、コメントの意図に応じた強調表示などの機能がある。
※実用したわけではなくただの遊びなのでいろいろ適当な箇所が多い…。改善するモチベーションを高めるためにあえて人の目に晒すことにする。

実装

クライアントサイドはAS3で実装。 github.com

コメントの受付と配布を行うサーバー(以降、コメントサーバー)はJavaで実装。 github.com

ストリーミングサーバーはRed5のサンプルアプリケーション'oflaDemo'をそのまま活用した。
※前述の通り随分古い環境で扱っていたためRed5 0.9.1でしか試してない。

動作確認

CentOS上でコメントサーバーを実行する。

# java -jar commentserver.jar 
2017/06/25 08:35:09.684 INFO  - port_num : 10007 maxNumUser : 100 commentListDir : /vagrant/tmp
2017/06/25 08:35:09.696 INFO  - Comment server started

Red5も起動。

sh red5.sh

Webサーバ上に配置したFlashアプリケーションにアクセスする。
配信者画面
http://your_server_ip_address/publish.html f:id:hiroki-sawano:20170625181535p:plain

聴講者画面
http:/your_server_ip_address/subscribe.html f:id:hiroki-sawano:20170625183847p:plain

まとめ

といった感じにRed5を使うと結構簡単にストリーミングサービスが作れて面白い。
ちゃんとした説明はREADMEにいずれ書くつもり..。

実行可能なJARファイル

問題

Mavenプロジェクトで生成されたJARファイルが'java -jar'コマンドで実行できないときの話。

$ java -jar your_app.jar
no main manifest attribute, in your_app.jar

原因

your_app.jarファイルを展開してみるとMANIFEST.MFにMain-Classの指定がない。

$ cat META-INF/MANIFEST.MF 
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: xxxxxxxxxx
Build-Jdk: 1.8.0_101

JARファイルの仕様についてはこちら。 JAR File Specification

対策

このような場合には、pom.xmlに以下のプラグインを追加することで、MANIFEST.MFの設定を行う。

<plugins>      
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.5.2</version>
    <configuration>
      <finalName>executable_app</finalName>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
      <appendAssemblyId>false</appendAssemblyId>
      <archive>
        <manifest>
          <mainClass>com.mypackage.MyClass</mainClass>
        </manifest>
      </archive>
    </configuration>
    <executions>
      <execution>
        <id>make-assembly</id>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
</plugins>

結果

出力されたexecutable_app.jar内のMETA-INF/MANIFEST.MFにはmainClassタグの指定に従ってMain-Classが追加されている。

$ cat MANIFEST.MF 
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xxxxxxxxxx
Created-By: Apache Maven 3.0.5
Build-Jdk: 1.8.0_101
Main-Class: com.mypackage.MyClass

executable_app.jarはコマンドラインから次の通り実行可能となる。

$ java -jar executable_app.jar

chef-zero+knife-zeroでCentOS7上にGlassFish4環境構築を自動化

実行環境

インストー

Chef DKとchef-zero、knife-zeroをインストールする。

>curl -L https://www.opscode.com/chef/install.sh | sudo bash -s -- -P chefdk
>chef gem install knife-zero chef-zero json --no-ri --no-rdoc

chef-clientがローカルモードで動作するようにknife.rbに'local_mode true'を設定する。

~/.chef/knife.rb

local_mode               true

レシピを作成

chefのリポジトリとクックブックを作成する。

>chef generate repo chef-repo
>cd chef-repo
>knife cookbook create sample

今回はGlassFish4のセットアップ自動化を目標に次のようなレシピを作成した。 bashリソースを使用している箇所の冪等性担保などはいまいちなので今後改善していく予定。

chef-repo/cookbooks/sample/recipes/default.rb

package %w(java-1.8.0-openjdk unzip wget expect) do
  action :install
end

group 'glassfish' do
  group_name 'glassfish'
  action :create
end

glassfish_home = "/home/glassfish"

user 'glassfish' do
  manage_home true
  group 'glassfish'
  home "#{glassfish_home}" 
  shell '/bin/bash'
  action :create
end

remote_file "#{glassfish_home}/glassfish-4.1.zip" do
  source 'http://download.java.net/glassfish/4.1/release/glassfish-4.1.zip'
  owner 'glassfish'
  group 'glassfish'
  mode '0644'
end

bash 'unzip glassfish' do
  user 'glassfish'
  group 'glassfish'
  cwd "#{glassfish_home}"
  code <<-EOC
    unzip glassfish-4.1.zip
    rm -f glassfish-4.1.zip 
  EOC
  not_if { File.exists?("#{glassfish_home}/glassfish4/glassfish/lib/client/appserver-cli.jar")}
end

cookbook_file '/etc/systemd/system/glassfish.service' do
  source 'glassfish.service'
  owner 'root'
  group 'root'
  mode '0644'
  action :create_if_missing
end

service 'glassfish' do
  action [ :enable, :start ]
  supports :start => true, :stop => true, :reload => true
end

cookbook_file "#{glassfish_home}/glassfish_setting.exp" do
  source 'glassfish_setting.exp'
  owner 'root'
  group 'root'
  mode '0644'
  action :create_if_missing
end

bash 'setup glassfish' do
  user 'root'
  group 'root'
  cwd "#{glassfish_home}"
  code <<-EOC
    expect glassfish_setting.exp "admin" "" "password"
    systemctl stop glassfish
    systemctl start glassfish
    touch done
  EOC
  not_if { File.exists?("#{glassfish_home}/done")}
end

GlassFishのサービス設定は、以下の'glassfish.service'をcookbook_fileリソースでノードに配置する。

chef-repo/cookbooks/sample/files/glassfish.service

[Unit]
Description = GlassFish Server v4.1
After = syslog.target network.target

[Service]
User=glassfish
ExecStart = /usr/bin/java -jar /home/glassfish/glassfish4/glassfish/lib/client/appserver-cli.jar start-domain
ExecStop = /usr/bin/java -jar /home/glassfish/glassfish4/glassfish/lib/client/appserver-cli.jar stop-domain
ExecReload = /usr/bin/java -jar /home/glassfish/glassfish4/glassfish/lib/client/appserver-cli.jar restart-domain
Type = forking

[Install]
WantedBy = multi-user.target

Glassfishのadminパスワード設定とlocalhost以外からの管理コンソールアクセスを許可するために、expectスクリプトで'asadmin'との対話を自動化する。

chef-repo/cookbooks/sample/files/glassfish_setting.exp

#!/usr/bin/expect

set admin_user_name [lindex $argv 0]
set old_pw [lindex $argv 1]
set new_pw [lindex $argv 2]
set timeout 10

spawn env LANG=C /home/glassfish/glassfish4/glassfish/bin/asadmin change-admin-password
expect "Enter admin user name \[default: admin\]>"
send "${admin_user_name}\n"
expect "Enter the admin password>"
send "${old_pw}\n"
expect "Enter the new admin password>"
send "${new_pw}\n"
expect "Enter the new admin password again>"
send "${new_pw}\n"
expect "Command change-admin-password executed successfully."

spawn env LANG=C /home/glassfish/glassfish4/glassfish/bin/asadmin enable-secure-admin
expect "Enter admin user name>"
send "${admin_user_name}\n"
expect "Enter admin password for user \"admin\">"
send "${new_pw}\n"
expect "Command enable-secure-admin executed successfully."

exit 0

動作確認

動作確認用に次のVagrantfileでVMを用意する。

chef-repo/Vagrantfile

VAGRANTFILE_API_VERSION = '2'

Vagrant.require_version '>= 1.5.0'

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  if Vagrant.has_plugin?("vagrant-vbguest")
    config.vbguest.auto_update = true
  end
  
  if Vagrant.has_plugin?("vagrant-omnibus")
    config.omnibus.chef_version = 'latest'
  end

  config.vm.hostname = 'vm'
  config.vm.box = 'centos/7'
  config.vm.network "forwarded_port", guest: 8080, host: 8080
  config.vm.network "forwarded_port", guest: 4848, host: 4848
  config.vm.network "private_network", ip: "192.168.33.11"
  config.vm.synced_folder ".", "/vagrant", type: "virtualbox"  
end

vagrant up'でVMを立ち上げる。

>vagrant up

‘knife zero bootstrap'でセットアップ対象のノードにchef-clientをインストールし、'knife node'と'knife zero converge'でレシピを適用する。

>knife zero bootstrap 192.168.33.11 -i .vagrant/machines/default/virtualbox/private_key --ssh-user vagrant --sudo -use-sudo-password
>knife node run_list add vm sample
>knife zero converge "name:vm" -x vagrant -i ./.vagrant/machines/default/virtualbox/private_key --sudo -a knife_zero.host

ブラウザから、GlassFishが動作していることを確認する。 f:id:hiroki-sawano:20170614012908p:plain

管理コンソールへのアクセスも確認できた。 f:id:hiroki-sawano:20170614012916p:plain