【忘却のJava#5】組み合わせ最適化問題を解くプログラムをJavaで書く(2)

やること

以下の記事で紹介したCのプログラムをJavaに書き換えます。
wing-degital.hatenablog.com

Javaへの書き換えのポイント

せっかくなのでCにはないJavaならではの方法をわざわざ使って実装しました。

classと二次元配列

どんなデータ構造?

classはオブジェクトの設計書のようなものでCの構造体のようにメンバー(変数や配列)を定義できます。
構造体と違うのはメンバーに加えてメソッド(関数)が定義できるところです。

用途は?

グラフを表現するために使いました。
頂点数と辺の集合をメンバーで持たせています。辺の集合は二次元配列で定義してます。

ArrayList

どんなデータ構造?

可変長の配列です。
配列への要素の追加、削除といった操作ができるメソッドがついていて便利です。

用途は?

辺集合とUnion-Findの部分集合を表現するために使いました。
正直、この手の組み合わせ最適化問題は固定長の集合で計算できるのでclassと配列でも表現できるのですが、無理やり使いました。

HashSet

どんなデータ構造?

要素の重複を許さない集合です。
内部的には配列とハッシュ値と線形リストの組み合わせで実現してるっぽいですね。

用途は?

Union-Findの集合の管理に使おうと思ったのですが、データ構造的に冗長過ぎるのでやめました。
配列だけで表現できます。

Iterator / 拡張for文

ArraySetはIterator、二次元配列は拡張for文でループを回してみました。
慣れてないせいなのか、どちらも直感的に分かりにくい。
特に二次元配列は拡張for文は馴染みませんね。。

Javaのコード

実際に書き換えたのがこちらです。

Graph.java

public class Graph {
    int ord;
    int [][] adj;

    public Graph(int n){
        this.ord = n;
        this.adj = new int[n][n];
    }

    public void setGraph(int input[][]){
        adj = input;
    }

    public void printGraph(){
        for (int array[] : adj){
            for (int val : array) {
                System.out.printf("%4d", val);
            }
            System.out.println("");
        }
    }

    public int countTotalEdgeCost() {
        int sum = 0;
        for (int i = 0; i < this.ord; i++) {
            for (int j = i + 1; j < this.ord; j++) {
                sum += this.adj[i][j];
            }
        }
        return sum;
    }
}

EdgeList.java

import java.util.ArrayList;
import java.util.Iterator;

public class EdgeList {

    static ArrayList<Edge> edgeList = new ArrayList<Edge>();

    public EdgeList(Graph g){
        for (int i = 0; i < g.ord; i++) {
            for (int j = i + 1; j < g.ord; j++) {
                if(g.adj[i][j]> 0) {
                    this.addEdgeList(g.adj[i][j], i, j);
                }
            }
        }
    }

    public void addEdgeList(int cost, int src, int dest) {
        Edge ed = new Edge(cost, src, dest);
        edgeList.add(ed);
    }

    public void printEdgeList() {
        for (Iterator<Edge>itr = edgeList.iterator(); itr.hasNext();) {
            Edge e = itr.next();
            e.printEdge();
        }
    }

    public void swapEdge(int i, int j) {
        Edge temp = new Edge(0, 0, 0);
        temp = edgeList.get(i);
        edgeList.set(i, edgeList.get(j));
        edgeList.set(j, temp);
    }

    public void quickSortEdgeList(int low, int high) {
        int mid = (low + high) / 2;
        int i = low;
        int j = high;
        double pivot = edgeList.get(mid).cost;
        while (true) {
            while (edgeList.get(i).cost < pivot) { i++; }
            while (edgeList.get(j).cost > pivot) { j--; }
            if (i >= j) { break; }
            swapEdge(i++, j--);
        }
        if (low < i - 1 ) { quickSortEdgeList(low, i - 1); }
        if (j+1 < high ) { quickSortEdgeList(j + 1, high); }
    }
    class Edge {
        int cost;
        int src;
        int dest;

        public Edge(int cost, int src, int dest) {
            this.cost = cost;
            this.src = src;
            this.dest = dest;
        }

        public void printEdge() {
            System.out.printf("%d<->%d (cost:%d)%n", this.src, this.dest, this.cost);
        }

    }
}

mst.java

import java.util.ArrayList;
import java.util.Iterator;

class Kruskal {

    public Graph execKruskal(Graph g, Graph tree) {
        EdgeList edl = new EdgeList(g);
        FlagmentList flml = new FlagmentList(g.ord);
        int edge_num = edl.edgeList.size();
        int src = 0;
        int dest = 0;
        int cnt_edge = 0;
        edl.quickSortEdgeList(0, edge_num-1);
        for (int i = 0; i < edge_num; i++) {
            src = edl.edgeList.get(i).src;
            dest = edl.edgeList.get(i).dest;
            // find if cycle exist or not
            if (flml.findParent(src) != flml.findParent(dest)) {
                // add edge in MST
                tree.adj[src][dest] = g.adj[src][dest];
                tree.adj[dest][src] = g.adj[dest][src];
                // marge set
                flml.Union(flml.flmList.get(src).parent, flml.flmList.get(dest).parent);
                cnt_edge++;
            }
            if (cnt_edge == (g.ord -1)) { break; }
        }
        //edl.printEdgeList();
        return tree;
    }

    class FlagmentList {

        ArrayList<Flagment> flmList = new ArrayList<Flagment>();

        public FlagmentList(int ord) {
            for (int i = 0; i < ord; i++) {
                this.addFlagmentList(i, 0);
            }
        }

        public void addFlagmentList(int parent, int rank) {
            Flagment flm = new Flagment(parent, rank);
            flmList.add(flm);
        }
        int findParent(int x) {
            return flmList.get(x).parent;
        }

        void Union(int x, int y) {
            Flagment flmX = flmList.get(x);
            Flagment flmY = flmList.get(y);
            if (flmX.rank > flmY.rank){
                flmY.setFlagment(flmX.parent, flmX.rank);
            } else {
                flmX.setFlagment(flmY.parent, flmY.rank);
                if (flmX.rank == flmY.rank) {
                    flmX.addRank();
                    flmY.addRank();
                }
            }
        }

        class Flagment {
            int parent;
            int rank;

            public Flagment(int parent, int rank){
                this.parent = parent;
                this.rank = rank;
            }

            public void setFlagment(int parent, int rank){
                this.parent = parent;
                this.rank = rank;
            }
           public void setFlagment(int parent, int rank){
                this.parent = parent;
                this.rank = rank;
            }

            public void addRank(){
                this.rank++;
            }

        }

    }
}
public class mst {
    public static void main(String[] args) {

        // create graph
        int maze[][] = {
            { 0,  7, 15,  0,  0,  0 },
            { 7,  0, 12,  6,  0,  0 },
            {15, 12,  0, 18, 17,  2 },
            { 0,  6, 18,  0, 13,  0 },
            { 0,  0, 17, 13,  0, 24 },
            { 0,  0,  2,  0, 24,  0 }
        };
        int n = maze.length;
        Graph g = new Graph(n); // graph
        Graph tree = new Graph(n); // graph
        int tc; // total cost

        // make graph
        g.setGraph(maze);

        // show test data
        System.out.println("Tree (result)");
        g.printGraph();

        // make MST by Kruskal Method
        Kruskal kruskal= new Kruskal();
        tree = kruskal.execKruskal(g, tree);
        // show result
        System.out.println("Tree (result)");
        tree.printGraph();
        tc = tree.countTotalEdgeCost();
        System.out.printf("total cost:%d\n", tc);
    }
}

実行結果

前回と同じように最適解が得られました。

# java mst
Graph (problem)
   0   7  15   0   0   0
   7   0  12   6   0   0
  15  12   0  18  17   2
   0   6  18   0  13   0
   0   0  17  13   0  24
   0   0   2   0  24   0

Tree (result)
   0   7   0   0   0   0
   7   0  12   6   0   0
   0  12   0   0   0   2
   0   6   0   0  13   0
   0   0   0  13   0   0
   0   0   2   0   0   0
total cost:40

参考

Javaコードで書くにあたり、こちらの記事でお勉強させていただきました。

qiita.com

qiita.com

www.moriwaki.net

以上。
 

 

【TECH:CAMP】現場フリーランスの生の声を聴いてきました

これからはデジタル領域にフォーカスして仕事をしていく予定なのですが、今後、IT人材不足が叫ばれる中でフリーランスの方と組んで仕事をする機会が増えるのではと思っています。
というか社内のリソースだけに閉じていてもビジネスがスケールできない時代がくると思ってます。

ということで、こちらにセミナーに行ってきました。
tech-camp.in

以下はその際のメモです。

フリーランスになったきっかけ

・自由にマイペースで仕事したいから
・企業の枠ではなく、自分が何ができるかで評価されたいから

仕事

・契約にもよるがスケジューリングは基本的に自由
・常にリモートワークでできるところもあれば、週3のコアタイムはあるところもある
・未経験からコードを書いてお金をもらうには独学だけでは知れないレベルを掴む
 現場のエンジニアに劣らないようにスキルを身に付ける
・人脈を広げるために色んなイベントにとにかく顔を出して、自分がやってること、やれることを話していた
・単価交渉には自分の実力を示す必要がある
 最初はとにかく言い値でお試しでつかってもらった
・自分を売り込むためにどんな案件をこなして、どういう技術を使ったか記録で残しておく
・将来の不安はいつ仕事が切られるかわからないこと
・前回の仕事や紹介で仕事が巡ってくる

キャリア

・会社のステータスだけでは転職できない時代がくる
 どんな物が作れるか、どんなことができるかが大切
・どんな方向に自分を育てていきたいかキャリアプランは自分で立てるしかない
・とにかく量をこなしていけば、未経験からでもなんとかなる
 未経験者は1000~1500時間が必要というデータがある
・業務知識をもっているジャンルで小さいものから入るとよい
・勉強会で築く人と人との繋がりは大事
・35歳からでも本当に働きたいという強い意思があればいける
 あとはプログラミングの適性が必要

お金

・月によるがMAX100万円/月
・自分の見積もり書をつくる時は要件からタスク分解して時間見積もりをする
・税金は高いため、経費申請とか節税対策はしている
・報酬は対面で交渉する
・育成費用や福利厚生など会社が負担してくれるお金を加味すると必ずしもフリーランスがいいわけではない

スキル

・言語は1つに絞ってまずはそれをマスターして自信を付けた方がよい
・はじめはC言語でやってたが、遅いのでMS-DOS 86アセンブラ書いていた
・基本はC#、データ処理はPhython、WebはJavaScript
・最初はRuby on Rails、今は機械学習を使うのでPhython
 RubyはWebサイト作るまで楽だし、Phythonに似ているので入りやすい
C言語はコンピュータの仕組みが透けて見える
・Techがvisualだと楽しいので、Javasciptはオススメ
・Javasctiptは言語としてみるといい言語ではないと思う
・同じ言語でも日々進化するため、再学習は忘れてはいけない
・いかに早くコードをかけるかを伸ばすこと
 他人が書いているところを見るのと、ペアプロを行う
・Progateとdotインストールの違いはちゃんと教えてくれる人がいること
・資格よりも実力が問われることが多い
・同じスキルの人はごまんといるので人間力も大切
・スキルシフトの時はチュートリアルをやりきる、技術書を写経するくらい再学習

所感

フリーエンジニアの方はIT技術が好きだからこそ生き生きとやれている
・フリーで稼ぎたい、IT技術が好きといった強い意志があることが大事
・マイペースで取り組めてワークライフバランスがとれることもモチベーション維持のポイント
・司会の方のファシリテーションがいまいち・・
・登壇者の方は経験に基づく話を丁寧にしてくれていて、エンジニアとしてよい刺激をもらえた

【忘却のJava#4】組み合わせ最適化問題を解くプログラムをJavaで書く(1)

 今回は大学時代に研究書いていたC言語のコードをJavaで書いてみたいと思います。
その前にC言語で当時のプログラムを動かすところまで事前準備でやっておきます。
実際にJavaコード書くのは次回の予定です。

組み合わせ最適化問題とは?

ある問題において、ある制約の下で選べる組み合わせの中で、最良の組み合わせを選ぶ問題です。
例えば、大きさや重さといった制約のもとナップサックに荷物を詰めるナップサック問題があります。

【参考】
組合せ最適化問題とは

最小全域木問題 (Minimum Spanning Tree Problem)

組み合わせ最適化問題の一つとして最小全域木問題があります。
全域木とは与 えられた重み付き無向グラフ中の頂点全てを含むような木のことです。
その全域木の中でも各辺の重みの和が最小となるものが最小全域木です。

【参考】
全域木とは:全域木 - Wikipedia
最小全域木とは:例えば数のようなものが最小全域木です。

f:id:wingfair:20190410224004p:plain
左:重み付き無向グラフ 右:最小全域木

Kruskalのアルゴリズム

最小全域木の最適解を導くことができるアルゴリズムのうちの1つです。
その時点で最も重みの小さい辺を閉路ができないように追加していき、部分木を順次拡大させていくことで最終的には最小全域木になるといったものです。
詳しくは以下の【参考】をみてみてください。

【参考】
クラスカル法 - Wikipedia

これを題材にしてC言語で書いたものをJavaで書いてみようと思ってます。
今回はその前に昔を思い出しながらCで書いたプログラムを実行するところまでやってみます。

事前準備

C言語のプログラムのコンパイラであるgccをインストールしておきます。

# yum install gcc

Cのコード

久しぶりにCのコード見ましたが、printf()とかputs()が懐かしい。。
あとQuickSortをちゃんと書いてるのとか軽く感動。。

Kruskalの中ではUnion-Findというロジックを使ってます。
面白い手法なので興味ある人は以下の方のサイトとか参考にしてみてください。
【参考】
最小全域木(クラスカル法とUnionFind) - アルゴリズム講習会

graph.c

#include<stdio.h>
#include<stdlib.h>
#include"graph.h"

#define min(x,y)  ( x < y ? x : y )
#define max(x,y)  ( x > y ? x : y )

/* rerutn empty graph(node number = n) */
Graph EmptyGraph(int n)
{
  Graph g;
  int i, j;
  g.ord = n;
  g.adj = (int**)malloc(sizeof(int*) * n);
  for (i = 0; i < n; ++i) {
      g.adj[i] = (int*)malloc(sizeof(int) * n);
  }
  for (i = 0; i < n; i++) {
    for (j = 0; j < n; j++) {
      g.adj[i][j] = 0;
      g.adj[j][i] = 0;
    }
  }
  return g;
}

/* free graph array */
void FreeGraphArray(Graph g)
{
  int i;
  for (i = 0; i < g.ord; i++) {
    free(g.adj[i]);
    g.adj[i] = NULL;
  }
  free(g.adj);
  g.adj = NULL;
}

/* make graph from m[]  */
Graph ArraytoGraph(Graph g, int (*m)[g.ord])
{
  int i, j;
  for (i = 0; i < g.ord; i++) {
    for (j = 0; j < g.ord; j++) {
      g.adj[i][j] = m[i][j];
    }
  }
  return g;
}

/* Print Graph g */
void PrintWeightedGraph(Graph g)
{
  int i, j;
  for (i = 0; i < g.ord; i++) {
    for (j = 0; j < g.ord; j++) {
      printf("%4d", g.adj[i][j]);
    }
    puts("");
  }
}

/* return total edge number of Graph g */
int CountEdge(Graph g)
{
  int i, j;
  int s = 0;
  for (i = 0; i < g.ord; i++) {
    for (j = i + 1; j < g.ord; j++) {
      if (g.adj[i][j] > 0) {
        s++;
      }
    }
  }
  return s;
}

/* return parent of x */
Group *FindParent(Group *x)
{
  if (x->parent != x) {
    x->parent = FindParent(x->parent);
  }
  return x->parent;
}

/* marge x and y */
void Union(Group *x, Group *y)
{
  if (x->rank > y->rank) {
    y->parent = x;
    y->rank = x->rank;
  } else {
    x->parent = y;
    x->rank = y->rank;
    if (x->rank == y->rank) {
      y->rank++;
      x->rank++;
    }
  }
}

/* swap x and y */
void swap(Edge *x, Edge *y)
{
  Edge temp;
  temp = *x;
  *x = *y;
  *y = temp;
}

/* Quick Sort */
void QuickSortEdge(Edge *e, int low, int high)
{
  int i, j, mid;
  double pivot;

  mid = (low + high) / 2;
  i = low;
  j = high;
  pivot = e[mid].w;
  while(1) {
    while(e[i].w < pivot) { i++; }
    while(e[j].w > pivot) { j--; }
    if (i >= j) { break; }
    swap(&e[i++], &e[j--]);
  }
  if (low < i-1) { QuickSortEdge(e, low, i-1); }
  if (j+1 < high) { QuickSortEdge(e, j+1, high); }
}

/* make minimum spanning tree */
Graph Kruskal(Graph g, Graph tree)
{
  int i, j, k, a, b, edge_num, cn_comp = 0;
  Group *flm;
  Edge *ed;

  edge_num = CountEdge(g);
  //Group *flagment;
  ed = (Edge *)calloc(edge_num, sizeof(Edge));
  // add edge in set
  k = 0;
  for (i = 0; i < g.ord; i++) {
    for (j = i+1; j < g.ord; j++) {
      if (g.adj[i][j] > 0) {
        ed[k].w = MakeWeightValue(g.adj[i][j], i, j);
        ed[k].p1 = i;
        ed[k].p2 = j;
        k++;
      }
    }
  }
  flm = (Group *)malloc(sizeof(Group) * g.ord);
  for (i = 0; i < g.ord; i++) {
    flm[i].parent = &flm[i];
    flm[i].rank = 0;
    flm[i].id = i;
  }
  QuickSortEdge(ed, 0, edge_num-1);
  k = 0;
  while (cn_comp < g.ord - 1) {
    a = ed[k].p1;
    b = ed[k].p2;
    // find if cycle exists or not
    if (FindParent(&flm[a]) != FindParent(&flm[b])) {
      // add edge in MST
      tree.adj[a][b] = g.adj[a][b];
      tree.adj[b][a] = g.adj[b][a];

      // marge set
      Union(flm[a].parent, flm[b].parent);
      cn_comp++;
    }
    k++;
  }
  free(flm);
  flm = NULL;
  free(ed);
  ed = NULL;
  return tree;
}

/* return sum of tree's edge consts */
int TreeTotalEdgeCost(Graph g)
{
  int i, j, cost, sum = 0;
  for (i = 0; i < g.ord; i++) {
    for (j = i + 1; j < g.ord; j++) {
      cost = g.adj[i][j];
      if (cost > 0) {
        sum += cost;
      }
    }
  }
  return sum;
}

/* return no duplicate weight value */
int MakeWeightValue(int w, int id, int p)
{
  int value;
  int a, b, c;
  a = w * 100000;
  b = max(id, p) * 1000;
  c = min(id, p);
  value = a + b + c;

  return(value);
}

main_mst.c

#define N 6 // graph node number

#include<stdio.h>
#include "graph.h"

int main(void)
{
  Graph g, tree; // graph
  int tc; // total cost

  // make graph
  g = EmptyGraph(N);
  tree = EmptyGraph(N);

  // test data
  int maze[N][N] = {
    { 0, 7,15, 0, 0, 0 },
    { 7, 0,12, 6, 0, 0 },
    {15,12, 0,18,17, 2 },
    { 0, 6,18, 0,13, 0 },
    { 0, 0,17,13, 0,24 },
    { 0, 0, 2, 0,24, 0 },
   };

  // show test data
  g = ArraytoGraph(g, maze);
  puts("Graph (problem)");
  PrintWeightedGraph(g);

  // make MST by Kruskal Method
  tree = Kruskal(g, tree);
  puts("");
  puts("Tree (result)");
  PrintWeightedGraph(tree);

  // show result
  tc = TreeTotalEdgeCost(tree);
  printf("total cost:%d\n", tc);

  // close graph
  FreeGraphArray(g);
  FreeGraphArray(tree);

  return(0);
}

make

# make
gcc -g -Wall -O2 -c main_mst.c
gcc -o mst main_mst.o graph.o

実行結果

こんな感じで最適解を求めることができます。
この"Graph"問題の最小全域木は"Tree"で最小の重みは40という結果です。

# ./mst
Graph (problem)
   0   7  15   0   0   0
   7   0  12   6   0   0
  15  12   0  18  17   2
   0   6  18   0  13   0
   0   0  17  13   0  24
   0   0   2   0  24   0

Tree (result)
   0   7   0   0   0   0
   7   0  12   6   0   0
   0  12   0   0   0   2
   0   6   0   0  13   0
   0   0   0  13   0   0
   0   0   2   0   0   0
total cost:40


以上。

【忘却のJava#3】sarの出力結果をExcelで扱いやすいように加工するプログラム

 数が少ないうちはいいのですが、複数のファイルを何回も繰り返しExcelに食わせたりしていると、その度に手動で加工するのが大変なので自動化します。
SIerさんはLinuxサーバから取得した情報って、どうせWindows環境でExcelでまとめるのでPowershellで全然問題ないのですが、あくまでJavaのお勉強ということで。

やりたいこと

sarの出力結果を加工して、Excelで扱いやすいようにする

加工対象とするsar出力結果

前回書いた「sarコマンドによるLinuxのリソース情報取得方法」のコマンドで取得できる情報を加工対象とします。

例:CPU情報(「sar -u 1 30 | egrep -v 'Average:'」の出力結果)

Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     04/06/2019      _x86_64_        (2 CPU)

05:28:12 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:28:13 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:28:14 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
(~省略~)

05:28:34 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:28:35 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
05:28:36 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:28:37 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
(~省略~)

05:28:56 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:28:57 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:28:58 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:28:59 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
(~省略~)

これをtxt形式でプログラムから読み込むこととします。

どのように加工するのか

(1) 時刻のヘッダが時刻(「04:56:45 AM」みたいに)
 ⇒ヘッダの文字列を時刻ではなく"TIME"に置換する

(2)ヘッダ行(CPU %user ~)のとスペース行が繰り返し表示される
 ⇒先頭行のヘッダだけ残し、それ以外は削除する

上記はメモリもディスクもネットワークも共通的に加工可能です。

Javaコード

久しぶりにまともにコードを書きました。
ファイルを読み込んで上記の加工処理をして、別のファイルに書き込む感じです。
ひとまず動けばいいのでえいやーです。
Emacsで書いてみましたが、キーバインドを忘れまくっててショック・・・

import java.io.*;
import java.nio.file.*;
import java.util.regex.*;
import java.io.IOException;

class EditSar {
    public static void main(String args[]) {
        try {
            // 書き込み用のファイルを作成
            File outputfile = new File("./sar-cpu_after.txt");
            outputfile.createNewFile();
            Path outputpath = outputfile.toPath();

            // 読み込み用のファイルをオープン
            Path inputpath = Path.of("./sar-cpu_before.txt");
            String text = Files.readString(inputpath);
            BufferedReader reader = new BufferedReader(new StringReader(text));

            //処理開始メッセージ
            System.out.println("加工処理を開始しました。");

            // 読み込んだtxtファイルを1行ずつ処理
            int emptycnt = 0;
            Boolean nextempty = false;
            String line = null;
            while ((line = reader.readLine()) != null) {

                // 空行だったら、書き込まない
                if (line.isEmpty()) {
                    emptycnt++;
                    nextempty = true;
                } else {
                    // 空行の次の行(ヘッダ行)だったら、書き込まない
                    if (nextempty) {
                        nextempty = false;
                        // 最初のヘッダ行だけは書き込む
                        if (emptycnt == 1) {
                            String newline = null;
                            // 時刻を"TIME"という文字列に置換
                            newline = line.replaceAll("[0-9][0-9]:[0-9][0-9]:[0-9][0-9] [A-Z][A-Z]","TIME");
                            Files.writeString(outputpath, newline + "\r\n", StandardOpenOption.APPEND);
                        }
                    } else {
                        // 除外対象となっていない行を書き込む
                        Files.writeString(outputpath, line + "\r\n", StandardOpenOption.APPEND);
                    }
                }
            }
            reader.close();
            //処理終了メッセージ
            System.out.println("加工処理が終了しました。");

        } catch (IOException e) {
            System.out.println(e);
        }
    }
}

実行結果

[root@localhost 20190409_sar]# java EditSar
加工処理を開始しました。
加工処理が終了しました。

加工した結果 (sar-cpu_after.txt)

ヘッダを"TIME"に置換できました。
長くなるので省略していますが、繰り返しのヘッダも除外できてます。
あとはこれをExcelにtxtファイルとして読み込んで区切り位置を指定すればキレイに表にできます。
その後はピボットにするなり煮るなり焼くなり何なりと。

[root@localhost 20190409_sar]# cat sar-cpu_after.txt
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     04/06/2019      _x86_64_        (2 CPU)
TIME     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:28:13 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:28:14 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
05:28:15 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
(~省略~)
05:29:08 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:29:09 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
05:29:10 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:29:11 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
05:29:12 AM     all      0.00      0.00      0.00      0.00      0.00    100.00


以上。

sarコマンドによるLinuxのリソース情報取得方法

sarコマンドとは?

LinuxのHWリソース情報を取得できるコマンドです。
CPU、メモリ、ディスク、ネットワークといった一通りの情報が取得できます。
(以下、CentOS7.6前提で書きます。)

事前準備

sarがインストールされていない場合は下記のパッケージをインストールしましょう。

# yum install sysstat

情報取得方法

基本的にはLinux上でsarコマンドを実行するだけです。
その際、オプションを使うことで取得したいリソースを指定できますが、私が現場で良く使うオプションを紹介します。

CPU情報

"-u"を指定するとCPU使用率の情報を取得できます。

コマンド
# sar -u [取得間隔(秒)] [取得回数]
実行例
# sar -u 1 3 | egrep -v 'Average:'
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     04/06/2019      _x86_64_        (2 CPU)

03:43:42 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
03:43:43 AM     all      0.50      0.00      0.00      0.00      0.00     99.50
03:43:44 AM     all      0.00      0.00      0.50      0.00      0.00     99.50
03:43:45 AM     all      0.00      0.00      0.00      0.00      0.00    100.00

メモリ情報

コマンド

"-r"を指定するとメモリ使用率の情報を取得できます。

# sar -r [取得間隔(秒)] [取得回数]
実行例
# sar -r 1 3 | egrep -v 'Average:'
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     04/06/2019      _x86_64_        (2 CPU)

03:42:46 AM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
03:42:47 AM   1157720    712744     38.11      2244    502232    242848      6.12    277684    291216         0
03:42:48 AM   1157720    712744     38.11      2244    502232    242848      6.12    277688    291216         0
03:42:49 AM   1157720    712744     38.11      2244    502232    242848      6.12    277688    291216         0

ディスク情報

"-d -p"を指定するとディスクごとのIO情報を取得できます。

コマンド
# sar -d -p [取得間隔(秒)] [取得回数]
実行例
# sar -d -p 1 3 | egrep -v 'Average:'
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     04/06/2019      _x86_64_        (2 CPU)

03:41:21 AM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:41:22 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:22 AM centos-root      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:22 AM centos-swap      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

03:41:22 AM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:41:23 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:23 AM centos-root      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:23 AM centos-swap      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

03:41:23 AM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:41:24 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:24 AM centos-root      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03:41:24 AM centos-swap      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

ネットワーク情報

"-n DEV"を指定するとネットワークインタフェースごとのIO情報を取得できます。

コマンド
# sar -n DEV [取得間隔(秒)] [取得回数]
実行例
# sar -n DEV 1 3 | egrep -v 'lo|Average:'

03:51:57 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
03:51:58 AM      eth0      3.00      0.00      1.12      0.00      0.00      0.00      2.00

03:51:58 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
03:51:59 AM      eth0      6.00      1.00      2.58      0.32      0.00      0.00      5.00

03:51:59 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
03:52:00 AM      eth0      2.00      1.00      0.57      0.32      0.00      0.00      1.00

取得項目の詳細

詳しく紹介してくれている方がいるので、以下を参照してみてください。
sarコマンドの使い方
sarコマンドでLinuxサーバのシステムモニタリングを行う方法

なぜ実行例ではegrepをかけているのか

普通にコマンド出力するとAverage(平均値)の行が入るのですが、それを除外するためです。sarの出力結果をExcelで加工する際に邪魔になるので出力の段階で省いてます。私の場合は最終的にピボット表で合計や平均を出すことが多いので尚更不要です。
あとネットワークのコマンドでloを除外しているのはループバックは見る必要がないことが多いためです。


以上。

【忘却のJava#2】CentOS7.6@Hyper-VでJava11の実行環境をつくる

環境づくり

久しぶりにHyper-Vを触って、環境構築しました。

Windows10上にサーバを建てましたが、小さめスペックのIntel NUCを使ってる関係でディスクが少ないのでCentOSはminimal版でインストール。

構成

仮想マシンHyper-V on Windows10 (CPU=2core / MEM=2GB)

仮想OS: CentOS7.6 (minimal)

Javajava-11-openjdk

 

仮想マシン構築

Hyper-Vマネージから仮想マシン1台を作成しました。

f:id:wingfair:20190406005726p:plain

Hyper-Vマネージャ(サンプル)

VM起動時に以下のようなエラーが出て、VMが動かず1時間くらいハマりました。

ハイパーバイザが実行されていないため、仮想マシンが起動できません

基本的にはCPUの仮想化支援が有効化されていないと出るやつです。

 

以下の流れで動くようになりました。

サービスからVMMが正常に動いてるか確認 ⇒ OK

BIOS設定画面からIntel-VTが有効化されている確認 ⇒ OK

手詰まったのでHyper-Vサービスを入れ直す

エラーが解消し、VMが正常に起動 うーんナゾ

 

CentOSインストール

公式サイトからダウンロードしたMinimal ISOからブートしてGUIでポチポチ進めます。

インストール先のディスク、タイムゾーン、rootパスワードを設定するくらいで後はインストーラが勝手に進めてくれます。

それにしてもしばらく見ないうちにグラフィカルになってますね。

minimalでも。

f:id:wingfair:20190406011538p:plain

 

f:id:wingfair:20190406005421p:plain

インストール画面(サンプル)

 

ネットワーク設定

インストールが完了してlogin表示までいったら、まずはネットワーク設定しときます。インターネット使ってyumで入れたいので、IPアドレスを設定します。

VMはあらかじめHyper-V側でNWアダプターを外部スイッチに接続しています。

# nmcli c m eth0 ipv4.address 192.168.10.4/24 ipv4.gateway 192.168.10.1
# systemctl restart network

 

JDKインストール

# yum install java-11-openjdk
# yum install java-11-openjdk-devel

 

動作確認

動作確認用にviでHelloWorldを書きました。

Javaコード

class HelloWorld {
	public static void main (String[] args) {
		System.out.println("Hello World!");
	}
}

 

実行結果

# javac HelloWorld.java

# java HelloWorld
Hello World!

 

以上。

【忘却のJava#1】30代インフラおじさんがJavaを思い出すために絵本を読みました

 これからSpring Bootを触ってみたいと思ってるので、その前にプログラミング言語の概念とか書き方とか初歩的な所をおさらいです。

 

 会社でJavaの研修を受けたのが、かれこれ7年前くらいですが、その後はひたすらインフラ領域のお仕事だったので、プログラミングとしてのJavaは一切触れていませんでした。なのでクラス、オブジェクト、継承、インタフェースって何となく覚えてるけど、どう違ってどう使えるんだっけ?オーバーロードとオーバーライドって何が違うんだっけ?ポリモーフィズムって何だっけ?などなど、恥ずかしながらそんなレベル・・・

 

 で、読んだ本はこちらです。

 「Javaの絵本

 非常に優しいで本なので、さくっと一通り思い出すにはちょうど良かったです。

持っていたのが、Java5.0を対象とした版なので古いですが、あくまで雰囲気を掴むということで、だいたい2時間くらいで読み切りました。

 

 次回は何かコードを書いてみたいと思います。

 

以上。