Coder 爱翻译 How Tomcat Works 第九章 第二部分

本文探讨了Tomcat中的会话管理机制,介绍了ManagerBase类及其子类StandardManager和PersistentManagerBase的功能,包括如何创建、存储和回收Session对象。还讨论了DistributedManager在集群环境中的作用。
[size=large]The ManagerBase Class[/size]

ManagerBase类是一个从所有Manager抽取出来到的抽象类。这个类给它的子类提供了常见的方法。ManagerBase有createSession方法来创建一个Session对象。每一个session都有一个唯一的标识符,ManagerBase类的protected方法generateSessionId返回一个唯一的标识符。

注意:一个活动状态下的session是一个session仍然是valid对象,而不是expired的。这些活动的session被存储在一个叫做sessions的HashMap中的:

protected HashMap sessions = new HashMap();


add方法把一个Session对象添加到sessions中:

public void add(Session session) {
synchronized (sessions) {
sessions.put(session.getId(), session);
}
}

remove方法从sessions中移除一个Session对象。

public void remove(Session session) {
synchronized (sessions) {
sessions.remove(session.getId());
}
}

没有参数的findSession方法返回在sessions中的活动的所有session对象的实例数组。findSession方法接收一个session标识符作为一个参数返回给出的标识符的Session实例。

public Session[] findSessions() {
Session results[] = null;
synchronized (sessions) {
results = new Session[sessions.size()];
results = (Session[]) sessions.values().toArray(results);
}
return (results);
}
public Session findSession(String id) throws IOException {
if (id == null)
return (null);
synchronized (sessions) {

Session session = (Session) sessions.get(id);
return (session);
}
}

[size=large]StandardManager[/size]

StandardManager类是Manager的标准实现,它用来把Session对象存储到内存中。它实现了Lifecycle接口,所以它可以被启动和停止。stop方法的实现调用unload方法,为每一个context把合法的Session实例以串行化方式存入一个叫做SESSION.ser的文件中。SESSION.ser文件可以在CATALINA_HOME目录下找到。当StandardManager再次启动的时候,使用load方法把这些Session对象被重新加载到内存中去。

一个manager也负责销毁那些不再合法的session对象。Tomcat 4中的StandardManager,它拥有一个指定的线程来实现。所以,StandardManager它实现了java.lang.Runnable接口。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}

ThreadSleep方法让这个线程处于睡眠等待checkInterval变量指定的秒数。默认是60秒。你可以调用setCheckInterval方法来设置这个变量值。

ProcessExpire方法循环地把所有的被StandardManager管理的Session对象中的每一个Session对象的lastAccessedTime值和当前的时间值比较。如果这两个时间的差值超过了maxInactiveInterval值,这个方法就调用Session接口的expire方法把这个Session实例设置为超时。maxInactiveInterval值可以通过调用setMaxInactiveInterval方法来改变。StandardManager下默认的maxInactiveInterval变量值是60。不要简单地认为这个值是在Tomcat部署时用到的值,setContainer方法org.apache.catalina.core.ContainerBase 类的setManager方法被调用,重写这个值:

setMaxInactiveInterval(((Context)
this.container).getSessionTimeout()*60 );

注意:在org.apache.catalina.core.StandardContext类的sessionTimeOut变量的默认值是30。

在Tomcat 5中,StandardManager类没有实现java.lang.Runnable.。在Tomcat 5中的StandardManager中的processExpires方法被backgroundprocess方法直接调用。

public void backgroundProcess() {
processExpires();
}

在StandardManager中的backgroundProcess方法通过org.apache.catalina.core.StandardContext实例的backgroundProcess方法调用,这个容器和这个manager相关联。每隔一段时间StandardContext调用它的backgroundProcess方法。

[size=large]PersistentManagerBase[/size]

PersistentManagerBase类是所有持久化管理器的父类。StandardManager和一个持久化manager主要的不同是:后面有一个store。一个store代表一个辅助存储器用来管理session对象。PersistentManagerBase类使用一个private的叫做store的对象引用。

private Store store = null;

在一个持久化manager中,session对象可以被备份和被清理。当一个session对象被备份,这个session对象被复制到一个store中,原来的session对象还在内存中。如果服务器down掉了,活动的session对象可以在store中重新获取。当一个session对象它被因为活动的session对象的数量超过了指定的数量或者session对象已经空闲了很长一段时间,清理的目的是为了节省内存空间。

在Tomcat 4中PersistentManagerBase实现了java.lang.Runnable来持有一个单独的线程来定期备份和清理活动的session。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
processPersistenceChecks();
}
}

processExpired方法,就像在StandardManager中的一样,是检查session对象是否超时。processPersistenceChecks方法调用了其他三个方法:

public void processPersistenceChecks() {
processMaxIdleSwaps();
processMaxActiveSwaps();
processMaxIdleBackups();
}

在Tomcat 5中,PersistentManagerBase没有实现java.lang.Runnable接口。备份和清理工作是由backgroundProcess manager处理的。它由与它相关联的StandardContext实例定期地调用。
[size=x-large]Swap Out[/size]

PersistentManagerBase类采用一系列规则来清理session对象。一个session对象在超过最大活动session的maxActiveSessions变量值或者session已经空闲了一段很长时间时被清理。

这种有太多的session对象的情况下,一个PersistentManagerBase实例简单地清理任何一个session知道活动session对象的值等于maxActiveSessions。

在一个session对象空闲了很长一段时间这种情况,PersistentManagerBase类使用两个变量来决定是否这个session对象被清理:minIdleSwap和maxIdleSwap。一个session对象在它的lastAccessedTime超过minIdleSwap和maxIdleSwap时,会被清理掉。为了保护被清理掉,你可以设置maxIdleSwap为负数。

因为一个活动的session可以被清理,它可以被重新加载到内存和存储到一个store中。findSession (String id)方法首先在内存中寻找Session实例,如果没有,就在store中搜索。下面是PersistentManagerBase类方法的实现。

public Session findSession(String id) throws IOException {
Session session = super.findSession(id);
if (session != null)
return (session);
// not found in memory, see if the Session is in the Store
session = swapIn(id); // swapIn returns an active session in the Store
return (session);
}

[size=large]Back-up[/size]

不是所有活动的session对象会被备份。一个PersistentManagerBase实例只会备份空闲时间比maxIdleBackup的值大的session对象。processMaxIdleBackups方法负责备份session对象。

[size=large]PersistentManager[/size]

PersistentManager继承了PersistentManagerBase:

Listing 9.5: The PersistentManager class

package org.apache.catalina.session;
public final class PersistentManager extends PersistentManagerBase {
// The descriptive information about this implementation.
private static final String info = "PersistentManager/1.0";
// The descriptive name of this Manager implementation (for logging).
protected static String name = "PersistentManager";
public String getInfo() {
return (this.info);
}
public String getName() {
return (name);
}
}


[size=large]DistributedManager[/size]

Tomcat 4中提供了DistrubutedManager类。是PersistentManagerBase的一个子类,DistributedManager被用在一个集群环境,有两个或更多节点。一个节点代表一个Tomcat的部署。在一个集群中的节点可以存在于不同的计算机中或在同一台计算机中。在集群环境中,
每一个节点必须使用一个DistributedManager实例作为它的Manager来支持session的复制。这个是DistributedManager的主要工作。

为了达到复制的目的,DistributedManager发送通知给其它节点。不管说么么时候一个session对象被创建或销毁。此外,一个节点必须能够接收到其它节点的通知。一个HTTP请求可以在集群中的任何一个节点得到服务。

发送和接收其它节点的DistributedManager实例的通知,Catalina提供org.apache.catalina.cluster包的类。ClusterSender类用来发送通知给其它节点,ClusterReceiver类用来接收通知。

DistrbutedManager的createSession方法必须创建一个session对象存储到当前实例,使用ClusterSender实例发送通知给其它节点。

Listing 9.6: The createSession method

public Session createSession() {
Session session = super.createSession();
ObjectOutputStream oos = null;
ByteArrayOutputStream bos = null;
ByteArraylnputStream bis = null;

try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(new BufferedOutputStream(bos));

((StandardSession)session).writeObjectData(oos);
oos.close();
byte[] obs = bos.toByteArray();
clusterSender.send(obs);
if(debug > 0)
log("Replicating Session: "+session.getId());
}
catch (IOException e) {
log("An error occurred when replicating Session: " + session.getId());
}

retun (session);
}

首先createSession方法调用父类的createSession方法来创建一个session对象。它发送session对象作为一个字节数组。

DistribubedManager类也实现了java.lang.Runnable,它有一个独立的线程把session对象超时,接收其它节点来的通知。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processClusterReceiver();
processExpires();
processPersistenceChecks();
}
}


[size=large]Stores[/size]

一个store由一个org.apache.catalina.Store接口表示,它是一个提供永久存储session的组件。

Listing 9.7: The Store interface

package org.apache.catalina;
import java.beans.PropertyChangeListener;
import java.io.IOException;

public interface Store {
public String getInfo();
public Manager getManager();
public void setManager(Manager manager);
public int getSize() throws IOException;
public void addPropertyChangeListener(PropertyChangeListener listener);
public String[] keys() throws IOException;
public Session load(String id) throws ClassNotFoundException, IOException;
public void remove(String id) throws IOException;
public void clear() throws IOException;
pubiic void removePropertyChangeListener(PropertyChangeListener listener);
public void save(Session session) throws IOException;
}

Store接口中两个最重要的方法:save和load。save方法存储指定的session对象要一个永久存储器。load方法用给出的指定标识符从存储器中加载session对象。keys方法返回一个字符串数组,这个数组包含所有的session标识符。

类图:
[img]http://dl.iteye.com/upload/attachment/403877/06f7bea7-e06d-36f2-8e63-a272d13e0644.jpg[/img]

[size=large]StoreBase[/size]

StoreBase类是一个抽象类,它为它的两个子类提供了常用的方法:FileStore和JDBCStore。StoreBase类没有实现Store接口的save和load方法,因为这个方法的实现依赖存储类型来存储session。

Tomcat 4中的StoreBase类持有一个单独的线程来定期地检查超时的session和移除活动session集合中超时的session。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}

processExpires方法获取所有的活动的session和检查每一个lastAccessedTime的值,移除session对象空闲了很长时间。

Listing 9.7: the processExpires method

protected void processExpires() {
long timeNow = System.currentTimeMillis();
String[] keys = null;
if (!started)
return;
try {
keys = keys();
}
catch (IOException e) {
log (e.toString());
e.printStackTrace();
return;
}
for (int i = 0; i < keys.length; i++) {
try {
StandardSession session = (StandardSession) load(keys[i]);
if (!session.isValid())
continue;
int maxInactiveInterval = session.getMaxInactiveInterval();
if (maxInactiveInterval < 0)
continue;
int timeIdle = // Truncate, do not round up
(int) ((timeNow - session.getLastAccessedTime()) / 1000L);
if (timeIdle >= maxInactiveInterval) {
if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
// recycle old backup session
session.recycle();
}
else {
// expire swapped out session
session.expire();
}
remove(session.getId());
}
}
catch (IOException e) {
log (e.toString());
e.printStackTrace();

}
catch (ClassNotFoundException e) {
log (e.toString());
e.printStackTrace();
}
}
}


在Tomcat 5中没有特别的线程来调用processExpires方法。这个方法定期地被与它相关联的PersistentManagerBase实例的backgroundProcess方法调用。

[size=large]FileStore[/size]

FileStore方法把session对象存储到文件中。文件是以session对象的标识符为名字,以.session为后缀名。这个文件位于临时的工作目录中。你可以调用Filestore类的setDirectory方法来更改临时目录。

java.io.ObjectOutputStream类是在sava方法中用来串行化session对象。存储在一个Session实例的所有对象都必须实现java.lang.Serializable。反序列化一个session对象是在load方法,java.io.ObjectInputStream类调用。

[size=large]JDBCStore[/size]

JDBCStore类存储session对象到一个数据库中,是通过JDBC来传输的,使用JDBCStore你需要设置驱动名字和链接URL,通过调用各自的setDriverName和setConnectionURL方法。

[size=large]The Application[/size]

本章的应用程序,使用默认连接器,它有一个context作为它的主容器。
这个应用程序有两个包:ex09.pyrmont.core和ex09.pyrmont.startup,在Catalina中使用不同的类。在ex09.pyrmont.core包下有四个类:SimpleContextConfig, SimplePipeline, SimpleWrapper和SimpleWrapperValve.
ex09.pyrmont.startup包下有一个类:Bootstrap。


[size=large]The Bootstrap Class[/size]

main方法开始是设置catalina.base系统属性和初始化默认连接器。

System.setProperty("catalina.base",
System.getProperty("user.dir"));
Connector connector = new HttpConnector();

SessionServlet,它创建一个wrapper,命名为: wrapper1.

Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Session");
wrapper1.setServletclass("SessionServlet");

然后创建一个StandardContext对象,设置它的路径和docBase属性,添加wrapper到context。

Context context = new StandardContext();
context.setPath("/myApp");
context.setDocBase("myApp");
context.addChild(wrapper1);

接下来start方法添加一个servlet映射。这个映射和第八章的不同。/Session,我们使用myApp/Session作为。

context.addServletMapping("/myApp/Session", "Session")

请求的url:http://localhost:8080/myApp/Session.

context需要一个监听器和一个加载器。

LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
// here is our loader
Loader loader = new WebappLoader();
// associate the loader with the Context
context.setLoader(loader);
connector.setContainer (context);

现在我们使用一个StandardManager实例,并把它传递给context。

Manager manager = new StandardManager();
context.setManager(manager);


最后,我们初始化和启动链接诶器和启动context。

connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();


[size=large]The SimpleWrapperValve Class[/size]


Listing 9.8: The invoke method of the SimpleWrapperValve class

public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {

SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
// pass the Context to the Request object so that
// the Request object can call the Manager
Context context = (context) wrapper.getparent();
request.setcontext(context);

// Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
}


你要访问wrapper,你可以调用Container接口的getParent方法获取这个context。注意:wrapper被添加到context中了。一旦你有一个context,你可以调用Request接口的setContext方法。

org.apache.catalina.connector.HttpRequestBase类的private的doGetSession方法调用Context接口的getManager方法来获取manager。

// Return the requested session if it exists and is valid
Manager manager = null;
if (context != null)
manager = context.getManager();

当有了一个manager,就可以直接获取一个session对象或创建一个新的session对象。

总结:本章讨论manager,它是在session管理里面的一个管理session的组件。组件类型和一个manager怎么持久化session对象到store中。
单向双向V2G 环境下分布式电源与电动汽车充电站联合配置方法(Matlab代码实现)内容概要:本文介绍了在单向和双向V2G(Vehicle-to-Grid)环境下,分布式电源与电动汽车充电站的联合配置方法,并提供了基于Matlab的代码实现。研究涵盖电力系统优化、可再生能源接入、电动汽车充放电调度、储能配置及微电网经济调度等多个关键技术领域,重点探讨了在不同电价机制和需求响应策略下,如何通过智能优化算法实现充电站与分布式电源的协同规划与运行优化。文中还展示了多种应用场景,如有序充电调度、鲁棒优化模型、多目标优化算法(如NSGA-II、粒子群算法)在电力系统中的实际应用,体现了较强的工程实践价值和技术综合性。; 适合人群:具备电力系统、新能源、智能优化算法等相关背景的科研人员、研究生及从事能源系统规划与优化的工程技术人员;熟悉Matlab/Simulink仿真工具者更佳。; 使用场景及目标:①用于科研项目中关于电动汽车与分布式电源协同配置的模型构建与仿真验证;②支持毕业论文、期刊投稿中的案例分析与算法对比;③指导实际电力系统中充电站布局与能源调度的优化设计。; 阅读建议:建议结合文中提供的Matlab代码与具体案例进行同步实践,重点关注优化模型的数学建模过程与算法实现细节,同时可参考文末网盘资源获取完整代码与数据集以提升学习效率。
【电动车】【超级棒】基于蒙特卡洛模拟法的电动汽车充电负荷研究(Matlab代码实现)内容概要:本文围绕基于蒙特卡洛模拟法的电动汽车充电负荷研究展开,利用Matlab代码实现对不同类型电动汽车(如常规充电、快速充电、换电模式)在不同场景下的充电负荷进行建模与仿真。通过蒙特卡洛方法模拟大量电动汽车的充电行为,结合用户出行规律、充电时间、电量需求等随机因素,分析电动汽车规模化接入电网后对电力系统负荷的影响,并探讨分时电价策略对充电负荷的引导作用,进而优化电网运行。研究涵盖充电负荷的空间分布特性、时间分布特征及对电网峰谷差的影响,旨在为电力系统规划和电动汽车有序充电管理提供理论支持和技术工具。; 适合人群:具备一定电力系统、交通工程或新能源汽车背景的研究生、科研人员及从事智能电网、电动汽车相关领域的工程技术人员。; 使用场景及目标:①用于研究大规模电动汽车接入对配电网负荷曲线的影响;②支撑分时电价、需求响应等政策制定与优化;③为充电站规划、电网调度、储能配置等提供数据支持和仿真平台;④适用于学术研究、课题复现及工程项目前期分析。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注蒙特卡洛模拟的参数设置、充电行为的概率建模过程,并尝试调整输入变量以观察负荷变化趋势,加深对电动汽车充电负荷不确定性和聚合效应的理解。
<think>好的,我现在需要帮助用户解决如何再次运行Ollama的Qwen2.5-Coder模型(7B版本)的问题。用户提到了“reopen”这个词,可能是指重新启动或重新运行模型。首先,我需要回顾Ollama的基本命令和操作流程。 用户之前的引用内容都是关于nginx的reopen操作,这可能让用户误以为Ollama也有类似的命令。需要明确告知用户Ollama的使用方式不同,不需要nginx那样的日志处理步骤。接下来,我需要分步骤说明如何运行模型,包括安装、拉取模型、运行模型以及可能的重启方法。 然后,要检查用户提到的模型名称是否正确。Qwen2.5-Coder可能有不同的版本命名,比如是否在Ollama的模型库中存在。如果用户遇到问题,可能需要建议他们检查模型名称是否正确或者尝试其他相关模型。 此外,用户可能遇到的问题可能包括模型未找到、运行失败或如何保持服务运行。需要提前预判这些问题并提供解决方案,比如检查网络连接、更新Ollama版本、使用后台运行模式等。 最后,确保回答结构清晰,步骤明确,并生成相关问题引导用户进一步学习。同时,引用之前提供的参考资料时要恰当,但注意用户的问题与nginx无关,所以可能需要指出两者的区别,避免混淆。</think>### 如何再次运行 Ollama 的 Qwen2.5-Coder 模型(7B 版本) #### 步骤说明 1. **确认模型名称** 首先需要确认模型在Ollama模型库中的完整名称。根据Ollama官方模型库的命名规则,模型名称通常为`<开发者>/<模型名>:<版本>`格式。例如:`qwen2.5-coder:7b`或`qwen/qwen2.5-coder-7b`(需查询Ollama文档确认)。 2. **拉取模型(若未下载)** 使用以下命令拉取模型(假设模型名为`qwen2.5-coder:7b`): ```bash ollama pull qwen2.5-coder:7b ``` 若已下载,此步骤可跳过。 3. **运行模型** 直接执行启动命令: ```bash ollama run qwen2.5-coder:7b ``` 4. **重启服务的特殊场景** 若需要**重新加载模型配置**(类似nginx的`reopen`操作),Ollama提供了服务控制命令: ```bash ollama serve # 先停止服务(Ctrl+C终止) ollama run qwen2.5-coder:7b # 重新运行 ``` #### 常见问题解决 - **模型未找到** 检查模型名称是否在Ollama库中存在: ```bash ollama list # 查看本地已下载模型 ollama search qwen # 搜索相关模型 ``` - **运行失败** 1. 确保Ollama版本已更新: ```bash ollama --version # 检查版本 ``` 2. 尝试重启Ollama后台服务: ```bash systemctl restart ollama # Linux系统 ``` - **保持服务运行** 使用`nohup`或`screen`实现后台运行: ```bash nohup ollama run qwen2.5-coder:7b & ``` #### 与Nginx日志管理的区别 虽然用户提到`nginx -s reopen`的日志轮转操作[^3][^4],但Ollama的运行机制不同: - Ollama无需手动管理日志文件 - 模型运行状态通过命令行直接控制 - 持久化配置通过`ollama serve`实现[^1][^2]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值