您当前的位置:首页 > 互联网教程

JVM client模式和Server模式的区别

发布时间:2025-05-24 14:30:40    发布人:远客网络

JVM client模式和Server模式的区别

一、JVM client模式和Server模式的区别

这里向大家描述一下JVM client模式和Server模式两者的区别和联系,JVM如果不显式指定是-Server模式还是-client模式,JVM能够根据下列原则进行自动判断(适用于Java5版本或者Java以上版本)。

JVM Server模式与client模式启动,最主要的差别在于:-Server模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。JVM如果不显式指定是-Server模式还是-client模式,JVM能够根据下列原则进行自动判断(适用于Java5版本或者Java以上版本)。

前段时间有个同事给我发了个java跟c++性能比较的文章,其中有个对比图引起了我的兴趣,意外的是,我感兴趣的不是java和c++的对比,而是java-Server模式和java-client模式的对比。从来没想到两者间的性能有如此巨大的差别。而在后来自己的亲身测试中发现确实如此。

图中最显著的就是JVM client模式和Server模式关于method call的对比,那个差别不是一般的大,在后来的测试中发现,相差至少有10倍。

JVM工作在Server模式可以大大提高性能,但应用的启动会比client模式慢大概10%。当该参数不指定时,虚拟机启动检测主机是否为服务器,如果是,则以Server模式启动,否则以client模式启动,J2SE5.0检测的根据是至少2个CPU和最低2GB内存。

当JVM用于启动GUI界面的交互应用时适合于使用client模式,当JVM用于运行服务器后台程序时建议用Server模式。

JVM在client模式默认-Xms是1M,-Xmx是64M;JVM在Server模式默认-Xms是128M,-Xmx是1024M。我们可以通过运行:java-version来查看jvm默认工作在什么模式。

二、Prometheus java-client Collector 解析

1、Prometheus是一款开源监控系统,用于采集时序数据。其架构及生态系统由多个组件组成,包括客户端库、服务器等。Prometheus提供了四个官方客户端库:go-client, java-client, python-client, ruby-client,以及第三方提供的各语言客户端库。本文主要分析java版本的客户端库。

2、Prometheus从根本上将所有数据存储为时间序列,即属于同一指标和同一组标记维度的带时间戳的值的流。样本表示在某一时刻,某一指标的采样,由类Collector.MetricFamilySamples.Sample表示。样本由时序名称、时序标签和值组成,此外,prometheus还附带exemplar字段用于存放样本点的额外信息,用于表示每一步的瞬时操作,这是指标的trace数据。注意,exemplar和timestampMs两个字段的信息是可选的,这里的timestampMs并不表示触发采集的时间戳,但exemplar中的timestampMs表示指标数据变动时的时间戳。

3、数据模型方面,MetricFamily表示一个指标族,这些指标具有相同的名称(或类似的名称),相同的标签名,但本身的标签值不同,分布在不同的时序流中。Collector.MetricFamilySamples结构用于打包时序样本点,根据OpenMetrics的定义,一个MetricFamily可能包含零个或多个Metric。每个Metric具有唯一LabelSet。

4、对于指标类型,Prometheus提供了Counter、Gauge、StateSet、INFO、Histogram和GaugeHistogram等类型。Counter是一个计数器,用于测量离散事件,如HTTP请求的接收数量或CPU时间的消耗等。Gauge是一个可增加和减少的仪表盘,表示当前的测量值,如内存使用量或队列中的项目数。StateSet用于表示一系列相关的布尔值,可以用来表示枚举类型的数据。INFO指标用于暴露在流程生命周期内不应更改的文本信息。Histogram用于统计离散事件的分布,如HTTP请求的延迟或函数运行时间。GAUGE_HISTOGRAM测量当前分布,如队列中等待时间的分布或请求大小的分布。SUMMARY指标也是用于测量离散事件的分布,引入了分位数的概念。

5、Collector的实现分为SimpleCollector,内置Collector的基类,实现了内置Collector的基本功能,以及Counter、Gauge、Summary、Histogram和Enumeration等具体的指标类型实现。Counter是一个递增计数器,用于统计事件的数量,如请求的数量或内存使用量等。Gauge是一个数值可增减的仪表盘,用于表示当前的信息,如请求处理中队列的长度或剩余内存等。Summary用于历史数据的分位数统计,如响应延迟或请求数据大小的统计。Histogram用于统计历史数据落在具体区间内的数量,如响应延迟或请求数据大小的统计。Enumeration是一个表示状态的指标,与OpenMetrics中定义的StateSet相对应。

6、关于注册操作,Collector通过CollectorRegistry进行注册,CollectorRegistry将Collector缓存在两个Map对象中。使用HTTPServer发送数据时,通过Collector采集数据,但Collector中的collect()方法一般是由HTTPServer被动调用。创建HTTP服务时,从HTTPServer.Builder中配置相关信息,创建服务后,可以通过三种资源路径对Prometheus数据进行访问。若请求不带请求参数,则获取所有当前时刻所有指标时序采样点;若带有请求参数,则获取过滤后的时序采样点。请求内容格式可以通过设置Accept请求头参数来指定,若不带或解析错误,则返回CONTENT_TYPE_004格式的数据;若带有application/openmetrics-text,则返回CONTENT_TYPE_OPENMETRICS_100格式的数据。

7、在某些情况下,Prometheus仅支持pull方式采集数据,但对于某些短生命周期的任务,指标变化的关键点可能由于pull的时间间隔太大而未被采集,此时可以使用PushGateway进行指标数据的推送。启动PushGateway,应用程序向其推送指标数据,PushGateway缓存数据后,Prometheus server定期拉取指标数据。向PushGateway推送数据时,需要使用job标签,并可选地附上groupingKey。指标的推送和删除通过向PushGateway发送POST, PUT, DELETE请求完成。推送数据格式为Prometheus通用的004格式。

三、java服务器接收客户端请求怎样实现的

import java.io.BufferedReader;

import java.io.InputStreamReader;

//构造服务器ServerSocket对象,参数为服务器端开放的端口号

ServerSocket ss= new ServerSocket(30102);

System.out.println("服务器准备就绪!");

//死循环可以使服务器持续处于接收客户端状态

//该方法使程序阻塞,等待客户端的链接,当监听到客户端的链接,创建一个Socket对象与客户端单独会话

//为了不影响服务器监听其它客户端,这里开启了一个线程,由线程处理与这个客户端的会话

public static void main(String[] args){

class ServerThread extends Thread{

private BufferedReader read= null;

private PrintStream print= null;

public ServerThread(Socket s){

//从Socket中获取输入流和输出流,由于我们只做一个简单的字符串通讯,所以采用BufferedRead和PrintStream来封装输入、输出流

read= new BufferedReader(new InputStreamReader(s.getInputStream()));

print= new PrintStream(s.getOutputStream());

//这里循环可以使服务器持续的接收客户端信息。read.readLine()通过输入流读取一段字符串,赋值给message变量,如果message字符串不为“exit”则循环,否则结束循环

while(!(message= read.readLine()).equals("exit")){

//将字符串前面添加“返回:”,再发回客户端

print.println("返回:"+ message);

//在 finally代码块中无论如何都会执行下面代码:

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.net.UnknownHostException;

//构造与服务器通讯的Socket对象,参数为服务器IP地址(String)和端口号(int),端口号需要和服务器端开放的端口号对应

Socket s= new Socket("192.168.1.100", 30102);

//启动一个线程与服务器通讯,并把链接服务器的Socket对象传递过去

} catch(UnknownHostException e){

public static void main(String[] args){

class LinkThread extends Thread{

private PrintStream out= null;

private BufferedReader in= null;

private Scanner scanner= null;

//将Socket对象实例保存在全局变量中,因为run方法中我们还要用它断开链接

//从Socket中获取输入流和输出流,由于我们只做一个简单的字符串通讯,所以采用BufferedRead和PrintStream来封装输入、输出流

out= new PrintStream(s.getOutputStream());

in= new BufferedReader(new InputStreamReader(s.getInputStream()));

} catch(UnknownHostException e){

scanner= new Scanner(System.in);

System.out.println("提示:如果要结束本次会话,请输入“exit”指令!");

//死循环可以使客户端不断的向服务器发送信息,不用担心循环无法结束,后面的return语句可以结束整个线程。

System.out.print("请输入:");

//将用户输入的字符串保存在message变量中

String message= scanner.nextLine();

System.out.println("本次会话结束!");

//在 finally代码块中无论如何都会执行下面代码: