GRE数学常用単語 まとめ 整数編

GREを勉強する時にまとめていたノートです
因みに数学は一発満点でした

  • Integer

整数

  • consecutive integer

連続した整数

  • positive whole number

正の整数

  • negative whole number

負の整数

  • even/even integer

偶数

  • odd/odd integer

奇数

  • real number

実数

  • divisor

除数

  • multiple

複数

  • remainder

余数

  • composite number

合成数

  • quotient

  • prime number

素数

  • prime factor

素因数

  • successive

連続した

  • spread

普及

  • score

20 <- ここ注意

  • consecutive

連続した

  • constant

定数

ジャーナルノードのRemote JMX portを開く ambari hadoop journal node

Journal nodeのメトリクスモニタリングの為、AmbariのUI上でjournal nodeのリモートjmxを開きます

が、datanode や namenodeと違い、journal nodeはあまり弄った事がないので起動オプションを何処で付け加えたらいいのかわかりませんでした。

hortonworksのHadoopソースコードからjoural nodeの起動コードを探してみる:

elif [ "$COMMAND" = "journalnode" ] ; then
  CLASS='org.apache.hadoop.hdfs.qjournal.server.JournalNode'
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_JOURNALNODE_OPTS"

Ref: github.com

どうやらnamenodeやdatanodeと同じく、hadoop-envからオプションを読み込んでいる様なので、同じ様に起動オプションを追加してあげましょう

// Add the line below in hadoop-env
      export HADOOP_JOURNALNODE_OPTS="${HADOOP_JOURNALNODE_OPTS} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=****"

Enable remote Jmx with port of journal node [ambari]

I wanna monitor the JMX metrics of Journal node, but cannot find a relative option in Ambari UI.

From the Hadoop source code, we can find that:

elif [ "$COMMAND" = "journalnode" ] ; then
  CLASS='org.apache.hadoop.hdfs.qjournal.server.JournalNode'
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_JOURNALNODE_OPTS"

Ref: github.com

So we ca open remote Jmx port of Journal node just like the way of namenode/datanode:

// Add the line below in hadoop-env
      export HADOOP_JOURNALNODE_OPTS="${HADOOP_JOURNALNODE_OPTS} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=****"

jmx exporter ソースコード解析

Jmx_exporterとは?

JMXのMBeansから、Prometheus用の形式に変換し、メトリクスを出力することができるExporterです。

どうやって使うの?

普通のアプリケーションはもちろん、KafkaやHDFSなどのオープンソースでもJMX用のポートを開ける物であれば簡単にアプリケーションの各種パラメーターを指定したポートに出力し、prometheusを使ってモニタリングシステムを組む事ができます

使い方は公式githubにて詳しく説明があるので割愛します。

github.com

ソースコード解析

凄くシンプルなプログラムなので、入り口から順に追って読んでみましょう

アプリケーション起動

入り口はここです: jmx_exporter/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java#agentmain

    public static void agentmain(String agentArgument, Instrumentation instrumentation) throws Exception {
        premain(agentArgument, instrumentation);
    }

    public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception {
        // Bind to all interfaces by default (this includes IPv6).
        String host = "0.0.0.0";

        try {
            Config config = parseConfig(agentArgument, host);

            new BuildInfoCollector().register();
            new JmxCollector(new File(config.file)).register();
            DefaultExports.initialize();
            server = new HTTPServer(config.socket, CollectorRegistry.defaultRegistry, true);
        }
        catch (IllegalArgumentException e) {
            System.err.println("Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration file> " + e.getMessage());
            System.exit(1);
        }
    }
    public static Config parseConfig(String args, String ifc) {
        Pattern pattern = Pattern.compile(
                "^(?:((?:[\\w.]+)|(?:\\[.+])):)?" +  // host name, or ipv4, or ipv6 address in brackets
                        "(\\d{1,5}):" +              // port
                        "(.+)");                     // config file

        Matcher matcher = pattern.matcher(args);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("Malformed arguments - " + args);
        }

        String givenHost = matcher.group(1);
        String givenPort = matcher.group(2);
        String givenConfigFile = matcher.group(3);

        int port = Integer.parseInt(givenPort);

        InetSocketAddress socket;
        if (givenHost != null && !givenHost.isEmpty()) {
            socket = new InetSocketAddress(givenHost, port);
        }
        else {
            socket = new InetSocketAddress(ifc, port);
            givenHost = ifc;
        }

        return new Config(givenHost, port, givenConfigFile, socket);
    }

    static class Config {
        String host;
        int port;
        String file;
        InetSocketAddress socket;

        Config(String host, int port, String file, InetSocketAddress socket) {
            this.host = host;
            this.port = port;
            this.file = file;
            this.socket = socket;
        }
    }
}

起動コマンドからArgumentをparseConfigでパースし、ホストやポート、configファイルなどを割り出しアプリケーションを起動しています

BuildInfoCollector

            new BuildInfoCollector().register();
            new JmxCollector(new File(config.file)).register();

アプリケーションの起動時、collectorsを作り、初期化しています。

BuildInfoCollectorは起動時にその名の通りビルド時のバージョンなどを記録し、MetricFamilySamplesの中に保存しています。

public class BuildInfoCollector extends Collector {
  public List<Collector.MetricFamilySamples> collect() {
    List<Collector.MetricFamilySamples> mfs = new ArrayList<Collector.MetricFamilySamples>();

    GaugeMetricFamily artifactInfo = new GaugeMetricFamily(
            "jmx_exporter_build_info",
            "A metric with a constant '1' value labeled with the version of the JMX exporter.",
            asList("version", "name"));

    Package pkg = this.getClass().getPackage();
    String version = pkg.getImplementationVersion();
    String name = pkg.getImplementationTitle();

    artifactInfo.addMetric(asList(
            version != null ? version : "unknown",
            name != null ? name : "unknown"
    ), 1L);
    mfs.add(artifactInfo);

    return mfs;
  }
}

JmxCollector

起動時に登録したもう一つのコレクターがJmxCollectorです。 こちらがメインにjmx_exporter.yamlに記述されたjmxを記録するクラスになります。

    public JmxCollector(File in) throws IOException, MalformedObjectNameException {
        configFile = in;
        config = loadConfig((Map<String, Object>)new Yaml().load(new FileReader(in)));
        config.lastUpdate = configFile.lastModified();
    }

起動時に指定されたYamlファイルをloadします

    public List<MetricFamilySamples> collect() {
     // Config ファイルの最終編集時間を確認
      if (configFile != null) {
        long mtime = configFile.lastModified();
     // アップデートされていたら、リロードする
        if (mtime > config.lastUpdate) {
          LOGGER.fine("Configuration file changed, reloading...");
          reloadConfig();
        }
      }

      Receiver receiver = new Receiver();
      JmxScraper scraper = new JmxScraper(config.jmxUrl, config.username, config.password, config.ssl,
              config.whitelistObjectNames, config.blacklistObjectNames, receiver, jmxMBeanPropertyCache);
      long start = System.nanoTime();
      double error = 0;
    // startDelaySecondsはコンフィグファイルで指定。アプリケーションの起動をまつ
      if ((config.startDelaySeconds > 0) &&
        ((start - createTimeNanoSecs) / 1000000000L < config.startDelaySeconds)) {
        throw new IllegalStateException("JMXCollector waiting for startDelaySeconds");
      }
      try {
     // メトリクスの収集を始める
        scraper.doScrape();
      } catch (Exception e) {
    ........
    }
  • configファイルが編集されているかチェックをする。
    • アップデートされていたら、リロードする
    • なのでjmx_exporterはホットロードを対応している。更新の際に再起動は必要なし
  • startDelaySecondsはコンフィグファイルで指定。アプリケーションが立ち上がるのを待たせる事ができます。デフォルトは0
  • scraper.doScrape() でmetricsのスクレイプを始めます。

doScrape

    public void doScrape() throws Exception {
        MBeanServerConnection beanConn;
        JMXConnector jmxc = null;
        // 与えられたホストとポートを元にコネクションを張ってみる。
  // SSLがある場合は認証を行う
        if (jmxUrl.isEmpty()) {
          beanConn = ManagementFactory.getPlatformMBeanServer();
        } else {
          Map<String, Object> environment = new HashMap<String, Object>();
          if (username != null && username.length() != 0 && password != null && password.length() != 0) {
            String[] credent = new String[] {username, password};
            environment.put(javax.management.remote.JMXConnector.CREDENTIALS, credent);
          }
          if (ssl) {
              environment.put(Context.SECURITY_PROTOCOL, "ssl");
              SslRMIClientSocketFactory clientSocketFactory = new SslRMIClientSocketFactory();
              environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
              environment.put("com.sun.jndi.rmi.factory.socket", clientSocketFactory);
          }

          jmxc = JMXConnectorFactory.connect(new JMXServiceURL(jmxUrl), environment);
          beanConn = jmxc.getMBeanServerConnection();
        }
        try {
   // configに記載してあるホワイトリストのmbeansをハッシュセットに入れる
            // Query MBean names, see #89 for reasons queryMBeans() is used instead of queryNames()
            Set<ObjectName> mBeanNames = new HashSet<ObjectName>();
            for (ObjectName name : whitelistObjectNames) {
                for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
                    mBeanNames.add(instance.getObjectName());
                }
            }
   // configに記載してあるブラックリストのmbeansをハッシュセットから除く
            for (ObjectName name : blacklistObjectNames) {
                for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
                    mBeanNames.remove(instance.getObjectName());
                }
            }

            // Now that we have *only* the whitelisted mBeans, remove any old ones from the cache:
            jmxMBeanPropertyCache.onlyKeepMBeans(mBeanNames);

            for (ObjectName objectName : mBeanNames) {
                long start = System.nanoTime();
     // 残った有効なmbeansを取りに行く
                scrapeBean(beanConn, objectName);
                logger.fine("TIME: " + (System.nanoTime() - start) + " ns for " + objectName.toString());
            }
        } finally {
          if (jmxc != null) {
            jmxc.close();
          }
        }
    }

ここでやっている事は: - 与えられたホストとポートを元にコネクションを張ってみる   - SSLがある場合は認証を行う - ホワイトリストブラックリストを元に、ユーザーに指定されたmbeansのリストを作る - リストを元に、mbeansを取りに行く

 private void scrapeBean(MBeanServerConnection beanConn, ObjectName mbeanName) {
        MBeanInfo info;
        try {
          // コネクションからbeansの情報を取得
          info = beanConn.getMBeanInfo(mbeanName);
        } catch (IOException e) {
          logScrape(mbeanName.toString(), "getMBeanInfo Fail: " + e);
          return;
        } catch (JMException e) {
          logScrape(mbeanName.toString(), "getMBeanInfo Fail: " + e);
          return;
        }
        MBeanAttributeInfo[] attrInfos = info.getAttributes();

        Map<String, MBeanAttributeInfo> name2AttrInfo = new LinkedHashMap<String, MBeanAttributeInfo>();
        for (int idx = 0; idx < attrInfos.length; ++idx) {
            MBeanAttributeInfo attr = attrInfos[idx];
            if (!attr.isReadable()) {
                logScrape(mbeanName, attr, "not readable");
                continue;
            }
    // 有効な値をLinkedHashMapに入れていく
            name2AttrInfo.put(attr.getName(), attr);
        }
        final AttributeList attributes;
        try {
            attributes = beanConn.getAttributes(mbeanName, name2AttrInfo.keySet().toArray(new String[0]));
        } catch (Exception e) {
            logScrape(mbeanName, name2AttrInfo.keySet(), "Fail: " + e);
            return;
        }
   // ここでは、processBeanValueを使い、recursiveにデータを処理していく
        for (Attribute attribute : attributes.asList()) {
            MBeanAttributeInfo attr = name2AttrInfo.get(attribute.getName());
            logScrape(mbeanName, attr, "process");
            processBeanValue(
                    mbeanName.getDomain(),
                    jmxMBeanPropertyCache.getKeyPropertyList(mbeanName),
                    new LinkedList<String>(),
                    attr.getName(),
                    attr.getType(),
                    attr.getDescription(),
                    attribute.getValue()
            );
        }
    }
  • 主にやっている事は、有効なbeansをリストにprocessBeanValueを使い処理していく事です

processBeanValue

   private void processBeanValue(
            String domain,
            LinkedHashMap<String, String> beanProperties,
            LinkedList<String> attrKeys,
            String attrName,
            String attrType,
            String attrDescription,
            Object value) {
        if (value == null) {
            logScrape(domain + beanProperties + attrName, "null");
   // 基本型だったら記録
        } else if (value instanceof Number || value instanceof String || value instanceof Boolean) {
            logScrape(domain + beanProperties + attrName, value.toString());
            this.receiver.recordBean(
                    domain,
                    beanProperties,
                    attrKeys,
                    attrName,
                    attrType,
                    attrDescription,
                    value);
  // そうでなかったらリカーシブルに基本型になるまでサーチしてく
        } else if (value instanceof CompositeData) {
            logScrape(domain + beanProperties + attrName, "compositedata");
            CompositeData composite = (CompositeData) value;
            CompositeType type = composite.getCompositeType();
            attrKeys = new LinkedList<String>(attrKeys);
            attrKeys.add(attrName);
            for(String key : type.keySet()) {
                String typ = type.getType(key).getTypeName();
                Object valu = composite.get(key);
                processBeanValue(
                        domain,
                        beanProperties,
                        attrKeys,
                        key,
                        typ,
                        type.getDescription(),
                        valu);
            }
  • 前の処理の続きで、processBeanValueに入ったbeans
  • 基本型(数字、文字列、ブーリアン)だったら記録
  • 他の型だったらrecursiveで基本型になるまで掘る
public void recordBean(
          String domain,
          LinkedHashMap<String, String> beanProperties,
          LinkedList<String> attrKeys,
          String attrName,
          String attrType,
          String attrDescription,
          Object beanValue) {

        String beanName = domain + angleBrackets(beanProperties.toString()) + angleBrackets(attrKeys.toString());
        // attrDescription tends not to be useful, so give the fully qualified name too.
        String help = attrDescription + " (" + beanName + attrName + ")";
        String attrNameSnakeCase = toSnakeAndLowerCase(attrName);
       // Configの中で書いたruleに基づき、マッチしたものだけ残す
        for (Rule rule : config.rules) {
          Matcher matcher = null;
          String matchName = beanName + (rule.attrNameSnakeCase ? attrNameSnakeCase : attrName);
          if (rule.pattern != null) {
            matcher = rule.pattern.matcher(matchName + ": " + beanValue);
            if (!matcher.matches()) {
              continue;
            }
          }

          Number value;
          if (rule.value != null && !rule.value.isEmpty()) {
            String val = matcher.replaceAll(rule.value);

            try {
              beanValue = Double.valueOf(val);
            } catch (NumberFormatException e) {
              LOGGER.fine("Unable to parse configured value '" + val + "' to number for bean: " + beanName + attrName + ": " + beanValue);
              return;
            }
          }
          if (beanValue instanceof Number) {
            value = ((Number)beanValue).doubleValue() * rule.valueFactor;
          } else if (beanValue instanceof Boolean) {
            value = (Boolean)beanValue ? 1 : 0;
          } else {
            LOGGER.fine("Ignoring unsupported bean: " + beanName + attrName + ": " + beanValue);
            return;
          }

          // If there's no name provided, use default export format.
          if (rule.name == null) {
            defaultExport(domain, beanProperties, attrKeys, rule.attrNameSnakeCase ? attrNameSnakeCase : attrName, help, value, rule.type);
            return;
          }

          // Matcher is set below here due to validation in the constructor.
          String name = safeName(matcher.replaceAll(rule.name));
          if (name.isEmpty()) {
            return;
          }
          if (config.lowercaseOutputName) {
            name = name.toLowerCase();
          }

          // Set the help.
          if (rule.help != null) {
            help = matcher.replaceAll(rule.help);
          }

          // Set the labels.
          ArrayList<String> labelNames = new ArrayList<String>();
          ArrayList<String> labelValues = new ArrayList<String>();
          if (rule.labelNames != null) {
            for (int i = 0; i < rule.labelNames.size(); i++) {
              final String unsafeLabelName = rule.labelNames.get(i);
              final String labelValReplacement = rule.labelValues.get(i);
              try {
                String labelName = safeName(matcher.replaceAll(unsafeLabelName));
                String labelValue = matcher.replaceAll(labelValReplacement);
                if (config.lowercaseOutputLabelNames) {
                  labelName = labelName.toLowerCase();
                }
                if (!labelName.isEmpty() && !labelValue.isEmpty()) {
                  labelNames.add(labelName);
                  labelValues.add(labelValue);
                }
              } catch (Exception e) {
                throw new RuntimeException(
                  format("Matcher '%s' unable to use: '%s' value: '%s'", matcher, unsafeLabelName, labelValReplacement), e);
              }
            }
          }

          // Add to samples.
          LOGGER.fine("add metric sample: " + name + " " + labelNames + " " + labelValues + " " + value.doubleValue());
          addSample(new MetricFamilySamples.Sample(name, labelNames, labelValues, value.doubleValue()), rule.type, help);
          return;
        }
      }

    }
  • Configで記載したruleに基づき、パターンマッチしたbeansのみ残す
  • Labelなど指定されたものを追加していく

最初のJmxCollector classの続きをみてみましょう

 List<MetricFamilySamples> mfsList = new ArrayList<MetricFamilySamples>();
      mfsList.addAll(receiver.metricFamilySamplesMap.values());
      List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
      samples.add(new MetricFamilySamples.Sample(
          "jmx_scrape_duration_seconds", new ArrayList<String>(), new ArrayList<String>(), (System.nanoTime() - start) / 1.0E9));
      mfsList.add(new MetricFamilySamples("jmx_scrape_duration_seconds", Type.GAUGE, "Time this JMX scrape took, in seconds.", samples));

      samples = new ArrayList<MetricFamilySamples.Sample>();
      samples.add(new MetricFamilySamples.Sample(
          "jmx_scrape_error", new ArrayList<String>(), new ArrayList<String>(), error));
      mfsList.add(new MetricFamilySamples("jmx_scrape_error", Type.GAUGE, "Non-zero if this scrape failed.", samples));
      return mfsList;
    }
  • jmx_exporterのメトリクス、jmx_scrape_errorとjmx_scrape_duration_secondsを追加
  • 実は今回のソースコード解析、この二つのデフォルトメトリクスをrenameできないか調べるのが目的でして、できなさそうですね涙

Registor

ここまででメトリクスの収集が完了しました 収集した物をコレクターと呼ぶ ここからはレジストリのコードです *) この部分はdependencyのなかにあります。jmx_exporterの中にはありません。

 // io.prometheus.client.CollectorRegistry#register()
    public void register(Collector m) {
        List<String> names = this.collectorNames(m);
        synchronized(this.collectorsToNames) {
            Iterator var4 = names.iterator();

            String name;
            while(var4.hasNext()) {
                name = (String)var4.next();
                // 既にレジストされているコレクターだったらエラー
                if (this.namesToCollectors.containsKey(name)) {
                    throw new IllegalArgumentException("Collector already registered that provides name: " + name);
                }
            }

            var4 = names.iterator();

            while(var4.hasNext()) {
                name = (String)var4.next();
                // namesToCollectorsに格納
                this.namesToCollectors.put(name, m);
            }
   // collectorsToNamesに格納
            this.collectorsToNames.put(m, names);
        }
    }
  • コレクターに重複がないかチェックしたら二つのHashmapに格納する
  • namesToCollectorsとcollectorsToNamesはその名の通りコレクターと名前のマップ
  • names と collectors は一対一ではない

サーバーの起動

// io.prometheus.jmx.JavaAgent.agentmain
....
            new BuildInfoCollector().register();
            new JmxCollector(new File(config.file)).register();
            DefaultExports.initialize();
            server = new HTTPServer(config.socket, CollectorRegistry.defaultRegistry, true);

アプリケーション起動の最後の一歩

    public HTTPServer(InetSocketAddress addr, CollectorRegistry registry, boolean daemon) throws IOException {
        this.server = HttpServer.create();
        this.server.bind(addr, 3);
        HttpHandler mHandler = new HTTPServer.HTTPMetricHandler(registry);
        this.server.createContext("/", mHandler);
        this.server.createContext("/metrics", mHandler);
        this.executorService = Executors.newFixedThreadPool(5, HTTPServer.DaemonThreadFactory.defaultThreadFactory(daemon));
        this.server.setExecutor(this.executorService);
        this.start(daemon);
    }
....
  • httpサーバーデーモンを起動しています
  • argumentにあるCollectorRegistryが先程の手順で起動したcollectorregistryです
  • HttpHandler mHandler = new HTTPServer.HTTPMetricHandler(registry)を通してregistryにアクセスします

HTTPMetricHandler

    static class HTTPMetricHandler implements HttpHandler {
        private CollectorRegistry registry;
        private final HTTPServer.LocalByteArray response = new HTTPServer.LocalByteArray();

        HTTPMetricHandler(CollectorRegistry registry) {
            this.registry = registry;
        }

        public void handle(HttpExchange t) throws IOException {
            String query = t.getRequestURI().getRawQuery();
            ByteArrayOutputStream response = (ByteArrayOutputStream)this.response.get();
            response.reset();
            OutputStreamWriter osw = new OutputStreamWriter(response);
            TextFormat.write004(osw, this.registry.filteredMetricFamilySamples(HTTPServer.parseQuery(query)));
            osw.flush();
            osw.close();
            response.flush();
            response.close();
            t.getResponseHeaders().set("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
            if (HTTPServer.shouldUseCompression(t)) {
                t.getResponseHeaders().set("Content-Encoding", "gzip");
                t.sendResponseHeaders(200, 0L);
                GZIPOutputStream os = new GZIPOutputStream(t.getResponseBody());
                response.writeTo(os);
                os.close();
            } else {
                t.getResponseHeaders().set("Content-Length", String.valueOf(response.size()));
                t.sendResponseHeaders(200, (long)response.size());
                response.writeTo(t.getResponseBody());
            }

            t.close();
        }
    }
  • this.registry.filteredMetricFamilySamplesでコレクターのMetricFamilySamplesを呼び出します
    public Enumeration<MetricFamilySamples> filteredMetricFamilySamples(Set<String> includedNames) {
        return new CollectorRegistry.MetricFamilySamplesEnumeration(includedNames);
    }
...
...
    class MetricFamilySamplesEnumeration implements Enumeration<MetricFamilySamples> {
        private final Iterator<Collector> collectorIter;
        private Iterator<MetricFamilySamples> metricFamilySamples;
        private MetricFamilySamples next;
        private Set<String> includedNames;

        MetricFamilySamplesEnumeration(Set<String> includedNames) {
            this.includedNames = includedNames;
            this.collectorIter = this.includedCollectorIterator(includedNames);
            this.findNextElement();
        }
        MetricFamilySamplesEnumeration() {
            this(Collections.emptySet());
        }
...
...
        private void findNextElement() {
            this.next = null;

            while(this.metricFamilySamples != null && this.metricFamilySamples.hasNext()) {
                this.next = this.filter((MetricFamilySamples)this.metricFamilySamples.next());
                if (this.next != null) {
                    return;
                }
            }

            if (this.next == null) {
                while(this.collectorIter.hasNext()) {
                    this.metricFamilySamples = ((Collector)this.collectorIter.next()).collect().iterator();

                    while(this.metricFamilySamples.hasNext()) {
                        this.next = this.filter((MetricFamilySamples)this.metricFamilySamples.next());
                        if (this.next != null) {
                            return;
                        }
                    }
                }
            }

        }

collectorIterの中のコレクターからメトリクスを探します この様な方式を用いて、http方式によりアプリケーション内部のjmx portを通じてメトリクスを読み込む事ができます

以上、jmx exporterのソースコード解析でした!

参考:

https://blog.csdn.net/qqqq0199181/article/details/83792364

Java デフォルトバージョン設定 java12 から 8 mac

新しいデスクトップをセットアップしてる時、ついうっかり最新のJavaをダウンロードしてしまった Gradleで昔のプロジェクトをビルドするとエラーが

Could not determine java version from '12.0.1'.

なんとJava 12が入っていた 普段は8を使っていたので、ダウングレードせねば

まあ12を消せば衝突は起きないだろうけどせっかくだから二つとも置いておこう ちなみにjavaは12 も 8もオラクルからダウンロードしてます

java8:

www.oracle.com

只、アーカイブバージョンをダウンロードするには会員登録しなければいけないのが鬱

ダウンロード完了後、ディフォルトのパスは変わらず12を指している。

$JAVA_HOME

output:
-bash: /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home

これを8に差し替えればおけ

新調したjava homeのパスを確認:

/usr/libexec/java_home -V

output:
Matching Java Virtual Machines (2):
    12.0.1, x86_64: "Java SE 12.0.1"    /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home
    1.8.0_211, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home

新しいPathをエクスポート

export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_211`

ちゃんと設定できてるか確認

$JAVA_HOME

output:
-bash: /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home

OK

React native expo-cli permission denied リアクトインストール出来ない権限問題 EACCES mac

流行りのReact nativeを勉強してみようと思ったのでインストール npm install -g expo-cli

Result:

ERR! sharp EACCES: permission denied, mkdir '/Users/***/.npm/_libvips'
info sharp Attempting to build from source via node-gyp but this may fail due to the above error
info sharp Please see https://sharp.pixelplumbing.com/page/install for required dependencies
gyp WARN EACCES user "root" does not have permission to access the dev dir "/Users/JP23136/.node-gyp/10.16.0"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/expo-cli/node_modules/sharp/.node-gyp"
gyp WARN install got an error, rolling back install
gyp WARN install got an error, rolling back install
gyp ERR! configure error
gyp ERR! stack Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/expo-cli/node_modules/sharp/.node-gyp'
gyp ERR! System Darwin 18.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /usr/local/lib/node_modules/expo-cli/node_modules/sharp
gyp ERR! node -v v10.16.0
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! sharp@0.22.1 install: `(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the sharp@0.22.1 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

なんと初手から失敗

EACCESパーミッション問題はどうやらNodeのパッケージをglobalにインストールしようとすると起こるらしい。 解決方法としては二つ!

  • npmをアンインストールして、node version manager(nvm)でインストールし直す
    • 推奨されているらしい
    • が試していないので省略
    • アンインストールはだるい
  • npmのデフォルトディレクトリを手動で変える
    • コマンドを打つだけなので楽

私は二番目の方法で解決したので記録

  1. global インストール用のディレクトリを作る
 mkdir ~/.npm-global
  1. npmのディフォルトディレクトリをセット
 npm config set prefix '~/.npm-global'
  1. 空のファイルを作る
// 私はvimでそのまま保存。エディタはご自由に
vi ~/.profile
  1. 環境変数を変える
export PATH=~/.npm-global/bin:$PATH
source ~/.profile

今度はインストール出来るはず!

npm install -g expo-cli

無事成功