分享
 
 
 

Java后台服务程序设计

王朝java/jsp·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

内容:

为什么需要后台服务程序?

通用服务器框架

服务与监听

服务控制

服务器实现和运行

参考资源

作者简介

何千军 (heqianjun@163.net)

软件工程师,独立顾问和自由撰稿人

在很多大型软件项目中,都有一些极为重要的后台服务程序,它们并不处理具体的系统业务逻辑,但对整个系统资源和服务的协调治理却是不可或缺。本文讨论如何完整地编写一个后台服务治理程序,并通过一个具体的后台服务治理例子来说明这一技术实现的技巧。

为什么需要后台服务程序?

在许多大型软件项目中,后台服务程序都扮演着极为重要的角色。它们无处不在,例如操作系统的内核程序处理各种对操作系统的内部调用;数据库系统的核心治理进程处理各种对数据库的读写操作和进程、资源的治理;大型ERP软件的内核治理程序要完成各种应用模块的资源、通讯治理等等。它们使系统的各种服务、资源与应用的表示之间形成了一个松耦合关系,这样就极大地增加了软件系统的稳定性和伸缩性。后台服务程序也就是相当于软件系统的治理调度中心,它是软件系统的中心处理器,是保证应用高效运行的内核程序。

在不同的软件系统中,由于软件的复杂程度和功能的不同使得各种软件系统的后台服务程序都有存在较大的差异。但是后台服务程序还是有很多共同的特点,一个基本的后台服务程序大概可以由四个部分构成:通用服务器框架、服务与监听、服务控制、服务器实现。下面我们就使用具体的代码来实现一个基本的后台服务器程序。

通用服务器框架

在开发后台服务程序中,我们首先实现一个通用服务器框架类,它能在多个端口提供多线程的服务(由多个Service对象定义),并且能够在系统运行时动态地调用和实例化Service类并加载新的服务或卸除已加载的服务。

清单 1显示了如何编制一个通用服务器框架类文件。

【清单 1:通用服务器框架类文件Server.Java】

import java.util.*;

import java.io.*;

import java.net.*;

public class Server {

protected Map services;

Set connections;

int maxConnections;

int freeConn;

ThreadGroup threadGroup;

private int currentConn;

private PrintWriter log = new PrintWriter(System.out, true);

public boolean connected = false;

public Properties proPort, proNum;

public synchronized void setControlFlag() {

connected = true;

}

public synchronized void removeControlFlag() {

connected = false;

}

public void setProperty(Properties proPort, Properties proNum) {

this.proPort = proPort;

this.proNum = proNum;

}

public Server(int maxConn) {

this.maxConnections = maxConn;

this.freeConn=maxConnections;

this.threadGroup = new ThreadGroup(Server.class.getName());

currentConn = 0;

this.services = new HashMap();

this.connections = new HashSet(maxConnections);

}

public synchronized void addService(Service service,int port, int maxConn) throws IOException {

String servicename = service.getClass().getName();

Integer key = new Integer(port);

if (services.get(key) != null) throw new IllegalArgumentException("端口:" + port + " 已经被占用!");

if (getfreeConnections(maxConn)>=0) {

Listener listener = new Listener(this, port, service, maxConn);

services.put(key,listener);

log.println("启动" + servicename + "服务在" + port +"端口上");

listener.start();

} else {

System.err.println("系统并发连接限制已经达到最大值!");

System.err.println("服务" + servicename + " 启动失败!");

}

}

public synchronized void addService(Service service,int port) throws IOException {

this.addService(service,port,10);

}

public synchronized boolean removeService(int port) {

Integer key = new Integer(port);

int maxConn =10;

final Listener listener = (Listener) services.get(key);

if (listener == null) {

log.println("Service " + " isn′t started on port " + port);

return false;

}

services.remove(key);

listener.pleaseStop();

freeConn+=listener.maxConn;

log.println("Close " + listener.service + " on port " + port);

return true;

}

public synchronized void displayStatus(PrintWriter out) {

Iterator keys = services.keySet().iterator();

while (keys.hasNext()) {

Integer port = (Integer) keys.next();

Listener listener = (Listener) services.get(port);

out.println("服务" + listener.service + "运行" + port + " ");

}

out.println("连接限制为" + maxConnections);

Iterator conns = connections.iterator();

while (conns.hasNext()) {

Socket s = (Socket) conns.next();

int sport = s.getLocalPort();

Listener listen = (Listener) services.get(new Integer(sport));

String servicename = listen.service;

out.println(servicename + "响应请求在" + s.getInetAddress().getHostAddress() + "的" + sport + "端口上");

}

out.println("当前连接数为" + currentConn);

out.println("当前系统空闲连接数为" + freeConn);

}

private synchronized int getfreeConnections(int maxConn) {

int num = -1;

if (freeConn >= maxConn) {

freeConn-=maxConn;

num = freeConn;

}

return num;

}

public synchronized int getConnections() {

return currentConn;

}

public synchronized int addConnections(Socket s) {

connections.add(s);

return currentConn++;

}

public synchronized int removeConnections(Socket s) {

connections.remove(s);

try {

s.close();

} catch (Exception e) {

System.out.println(e.getMessage());

}

return currentConn--;

}

public synchronized int getConnections(int connections) {

int num = 0;

if ((num=freeConn-connections) >= 0) {

freeConn = num;

} else num = -1;

return num;

}

private synchronized int getFreeConn() {

return freeConn;

}

}

如上所述可知,服务器框架类Server主要是通过端口到监听器影射的散列表services来治理服务对象,Server类的几个主要方法说明如下:

addService方法:此方法能够在特定的端口上创建新的服务,即在指定端口上运行指定的Service对象。

removeService方法:此方法使服务器停止指定端口上的服务,并不终止连接,仅使服务器停止接受新的连接。

displayStatus方法:此方法用于打印指定流上服务器的状态信息。

服务与监听

每个服务都对应着一个监听对象,监听指定端口的连接并在获得连接请求时调用addConnection(Socket s, Service service)方法来取得(释放)一个连接。清单 2显示了如何编制一个监听类文件。

【清单 2:Listener.java的一个简单实现】

import java.util.*;

import java.io.*;

import java.net.*;

public class Listener extends Thread {

private ServerSocket listener;

private int port;

String service;

Set connections;

private Service runService;

private boolean stop_flag = false;

int maxConn;

private PrintWriter log = new PrintWriter(System.err, true);

private ThreadGroup group;

private int currentConnections = 0;

Server server;

Socket client = null;

public Listener(Server server, int port, Service service, int maxConn, boolean bl) throws IOException {

this.server = server;

this.port = port;

this.service = service.getClass().getName();

this.maxConn = maxConn;

this.group = server.threadGroup;

this.connections = server.connections;

listener = new ServerSocket(this.port);

if (bl == false) listener.setSoTimeout(600000);

this.runService = service;

if (!stop_flag) {

for (int i=0;i<this.maxConn;i++) {

ConnectionHandler currentThread = new ConnectionHandler(server,logStream);

new Thread(this.group, currentThread, this.service+this.port+i).start();

this.logStream.realLog("向线程组" + group.toString() + "添加一个线程" + this.service+this.port+i);

}

} else throw new IllegalArgumentException("系统并发连接限制已经超过最大值!!");

}

public Listener(Server server, int port, Service service, int maxConn) throws IOException {

this(server, port, service, maxConn, false);

}

public void pleaseStop() {

this.stop_flag = true;

try {

listener.close();

} catch (Exception e) {

}

this.interrupt();

}

public void run() {

while(!stop_flag) {

try {

client = listener.accept();

addConnection(client,runService);

} catch (Exception e) {}

}

try {

client.close();

} catch (IOException e) {}

}

private synchronized void addConnection(Socket s, Service service) {

ConnectionHandler.requestToHandler(s, service);

}

}

在实际处理过程中,Listener对象通过在指定端口上与指定服务的绑定实现监听过程,主要的几个方法说明如下:

pleaseStop:以礼貌的方法停止接受连接。

addConnection:把指定要处理的服务放到线程池中,以等待空闲的线程处理服务。

监听对象通过传递一个Service对象并唤醒它的serve()方法才真正提供了服务。下面我们就来实现一个具体的服务:

服务接口Service只有一个抽象方法serve(InputStream in, OutputStream out),它所有服务实现所必须重写的一个方法,Listing 3显示了一个Service接口的定义。 【清单 3:定义一个Service接口】

import java.io.*;

import java.net.*;

public interface Service {

public void serve(InputStream in, OutputStream out) throws IOException;

}

编写一个简单的显示时间服务类:见清单 4。 【清单 4:一个显示系统当前时间的服务类Timer.java】

import java.util.*;

import java.text.*;

import java.io.*;

import java.net.*;

public class Timer implements Service {

public Timer() {

}

public void serve(InputStream in, OutputStream out) throws IOException {

String timeFormat = "yyyy-MM-dd hh:mm:ss";

SimpleDateFormat timeFormatter = new SimpleDateFormat(timeFormat);

BufferedReader from_client = new BufferedReader(new InputStreamReader(in));

PrintWriter outPrint = new PrintWriter(out);

String sDate = timeFormatter.format(new Date());

outPrint.println("当前时间是:" + sDate);

outPrint.flush();

try {

from_client.close();

outPrint.close();

}catch (Exception e){}

}

}

通过实现Service接口可以编写很多的服务实现提供各种不同的服务,读者有爱好也可自己编写一个服务来测试一下。

服务控制

服务控制是在服务器运行时动态地操作控制服务器,如系统运行时,动态地装载(卸载)服务,显示服务器的状态信息等等。为了简化基本后台服务系统的复杂程度,我们采用创建一个ControlService服务实例来在运行时治理服务器。ControlService实现了基于命令的协议,可用密码保护操纵服务器,代码如清单 5所示:

【清单 5:服务控制类文件ControlService.java】

import java.io.*;

import java.util.*;

import java.net.*;

import dvb.kuanshi.kssms.util.*;

import dvb.kuanshi.kssms.server.Server;

public class ControlService implements Service {

Server server;

String passWord;

public ControlService(Server server, String password) {

this.server = server;

this.password = password;

}

public void serve(InputStream in, OutputStream out) throws IOException {

boolean authorized = false;

BufferedReader from_client = new BufferedReader(new InputStreamReader(in));

PrintWriter to_console = new PrintWriter(System.out, true);

to_console.println("后台治理服务响应请求! ");

PrintWriter to_client = new PrintWriter(out);

synchronized (this) {

if(server.connected) {

to_client.println("已经有用户连接,本服务仅答应一个连接! ");

to_client.close();

return;

} else server.setControlFlag();

}

to_client.println("Remote Console>");

to_client.flush();

String line;

while ((line=from_client.readLine())!=null) {

int len = line.indexOf("Remote Console>");

line = line.substring(len+1,line.length());

String printStr;

try {

StringTokenizer st = new StringTokenizer(line);

int count = st.countTokens();

if (!st.hasMoreElements()) continue;

String first = st.nextToken().toLowerCase();

if (first.equals("password")) {

String pwd = st.nextToken();

if (pwd.equals(this.password)) {

to_client.println("OK");

authorized = true;

} else to_client.println("密码无效,请重试! ");

} else if (first.equals("add")) {

if(!authorized) to_client.println("请登陆! ");

else {

count--;

String servicename;

int Port;

boolean flag = true;

if (count>0) {

servicename = st.nextToken();

Port = Integer.parseInt(st.nextToken());

server.addService(loadClass(servicename1), Port);

to_client.println("服务" + servicename + "已经加载 ");

flag = false;

}

if (flag) to_client.println("系统不能启动非法服务:" + servicename);

else {to_client.println("请输入服务名! ");}

}

} else if (first.equals("remove")) {

if(!authorized) to_client.println("请登陆! ");

else {

count--;

if (count>0) {

int port = Integer.parseInt(st.nextToken());

boolean bl = server.removeService(port);

if (bl) to_client.println("端口: " + port +"上的服务已经卸载 ");

else to_client.println("端口: "+ port +"上无任何服务运行,卸载操作失败! ");

} else to_client.println("请输入端口名! ");

}

} else if(first.equals("status")) {

if(!authorized) to_client.println("请登陆! ");

else server.displayStatus(to_client);

} else if(first.equals("help")) {

if(!authorized) to_client.println("请登陆! ");

else printHelp(to_client);

} else if(first.equals("quit")) break;

else to_client.println("命令不能识别! ");

} catch(Exception e) {to_client.println("系统后台出错" + e.getMessage() +" ");

printHelp(to_client);

}

to_client.println("Remote Console>");

to_client.flush();

}

to_client.flush();

authorized = false;

server.removeControlFlag();

to_client.close();

from_client.close();

}

private void printHelp(PrintWriter out) {

out.println("COMMANDS:" +

" password <password> " +

" add <servicename> <port> " +

" remove <port> " +

" status " +

" help " +

" quit ");

}

protected Service loadClass(String servicename) {

Service s = null;

try {

Class serviceClass = Class.forName(servicename);

s = (Service) serviceClass.newInstance();

} catch (Exception e) {

}

return s;

}

}

服务器实现和运行

服务器实现主要完成服务器的初始化,启动服务控制实例等工作,代码如清单 6所示:

【清单 6:runServer.java的一个简单实现】

import java.util.*;

public class runServer {

public runServer() {

}

public static void main(String[] args) {

try {

int argLen = args.length;

System.out.println("正在初始化系统请等待......");

System.out.println("");

int maxConn = 30;

Server server = new Server(maxConn);

System.out.println("################################################################");

System.out.println("# #");

System.out.println("# 后台服务治理系统 #");

System.out.println("# #");

System.out.println("################################################################");

System.out.println();

if (argLen>2) {

for (int i = 0;i<argLen;i++) {

if (args[i].equals("-s")) {

i++;

String password = args[i];

i++;

int port = Integer.parseInt(args[i]);

server.addService(new ControlService(server,password), port, 2);

} else {

String servicename = args[i];

i++;

int port = Integer.parseInt(args[i]);

server.addService(loadClass(servicename), port);

}

}

} else throw new IllegalArgumentException("参数数目不正确!");

System.out.println("系统启动,进入监听服务模式......");

} catch (Exception e) {

throw new IllegalArgumentException(e.getMessage());

}

}

protected static Service loadClass(String servicename) {

Service s = null;

try {

Class serviceClass = Class.forName(servicename);

s = (Service) serviceClass.newInstance();

} catch (Exception e) {

}

return s;

}

}

下面我们就可以启动示例程序来测试一下了。

如清单 7所示,以密码保护方式(密码为test)启动后台服务,在6809启动服务控制实例,在6810端口启动。

【清单 7:启动后台服务程序】

% java runServer -s test 6809 Timer 6810

在另外一个窗口,执行如下命令,将显示当前系统的时间。

% java clientConnect 6810

在另外一个窗口,执行如下命令,你将能查看系统服务状态信息,并动态地装载你写的服务对象,你可以测试一下。

% java clientConnect 6809

现在,一个基本的后台服务程序即编制完成了。实际上,一个大型软件的后台服务程序是非常复杂的,上面的例子希望能起到抛砖引玉的效果。要写出性能良好的后台服务程序还有很多工作要做。

参考资源

要了解更多的 Java信息,请阅读 java.sun.com的 主页。

下载后台服务程序示例全部代码:code.zip

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有