OpenFire源码学习之二十二:openfie对用户的优化(下)

用户名片

在预初始化中,贴出来用户名片的程序。这里也一样不在重复。

首先同样先修改系统属性:


provider.vcard.className


org.jivesoftware.util.redis.expand.RedisVCardProvider

然后需要修改VCardManager名片管理这个类。

RedisVCardProvider:

import redis.clients.jedis.Jedis;
public class RedisVCardProvider implements VCardProvider {

	private static final Logger Log = LoggerFactory.getLogger(RedisVCardProvider.class);

    private static final String DELETE_PROPERTIES =
        "DELETE FROM ofVCard WHERE username=?";
    private static final String UPDATE_PROPERTIES =
        "UPDATE ofVCard SET vcard=? WHERE username=?";
    private static final String INSERT_PROPERTY =
        "INSERT INTO ofVCard (username, vcard) VALUES (?, ?)";

	private static final int POOL_SIZE = 10;

	private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>(POOL_SIZE);

	public RedisVCardProvider() {
        super();
        // Initialize the pool of sax readers
        for (int i=0; i<POOL_SIZE; i++) {
            SAXReader xmlReader = new SAXReader();
            xmlReader.setEncoding("UTF-8");
            xmlReaders.add(xmlReader);
        }
    }

	@Override
	public Element loadVCard(String username) {
		synchronized (username.intern()) {
			Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
            Element vCardElement = null;
            SAXReader xmlReader = null;
            try {
                // Get a sax reader from the pool
                xmlReader = xmlReaders.take();
                String usercard = jedis.get("OFVCARD:" + username);
                if (usercard == null || "".equals(usercard)) {
                	return XMPPServer.getInstance().getJedisConfDao().getVCardProvider().loadVCard(username);
                }
                vCardElement = xmlReader.read(new StringReader(usercard)).getRootElement();
            }
            catch (Exception e) {
                Log.error("Error loading vCard of username: " + username, e);
            }
            finally {
            	if (xmlReader != null) {
                    xmlReaders.add(xmlReader);
                }
            	XMPPServer.getInstance().getUserJedis().returnRes(jedis);
            }
            return vCardElement;
        }
	}

	@Override
	public Element createVCard(String username, Element vCardElement)
			throws AlreadyExistsException {
		......
	}

	@Override
	public Element updateVCard(String username, Element vCardElement)
			throws NotFoundException {
		......
	}

	@Override
	public void deleteVCard(String username) {
		......
	}
	@Override
	public boolean isReadOnly() {
		// TODO Auto-generated method stub
		return false;
	}
}

VCardManager

/**
 * Manages VCard information for users.
 *
 * @author Matt Tucker
 */
public class VCardManager extends BasicModule implements ServerFeaturesProvider {

	private static final Logger Log = LoggerFactory.getLogger(VCardManager.class);

	private VCardProvider provider;

    private static VCardManager instance;

    private EventHandler eventHandler;

    private static HmThreadPool threadPool = new HmThreadPool(5);

    public static VCardManager getInstance() {
        return instance;
    }

    public static VCardProvider getProvider() {
        return instance.provider;
    }

	public VCardManager() {
		super("VCard Manager");
        //String cacheName = "VCard";
        //vcardCache = CacheFactory.createCache(cacheName);
        this.eventHandler = new EventHandler();

        // Keeps the cache updated in case the vCard action was not performed by VCardManager
        VCardEventDispatcher.addListener(new VCardListener() {
            public void vCardCreated(String username, Element vCard) {
                // Since the vCard could be created by the provider, add it to the cache.
                //vcardCache.put(username, vCard);
            	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
				try {
					jedis.set("OFVCARD:" + username, vCard.asXML());
				} finally {
					XMPPServer.getInstance().getUserJedis().returnRes(jedis);
				}
            }

            public void vCardUpdated(String username, Element vCard) {
                // Since the vCard could be updated by the provider, update it to the cache.
                //vcardCache.put(username, vCard);
            	vCardCreated(username, vCard);
            }

            public void vCardDeleted(String username, Element vCard) {
                // Since the vCard could be delated by the provider, remove it to the cache.
                //vcardCache.remove(username);
            	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();

            	try {
            		jedis.del("OFVCARD:" + username);
				} finally {
					XMPPServer.getInstance().getUserJedis().returnRes(jedis);
				}
            }
        });
	}

    public String getVCardProperty(String username, String name) {
        ......
    }

    public void setVCard(String username, Element vCardElement) throws Exception {
        boolean created = false;
        boolean updated = false;

        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }

        //Element newvCard = null;
    	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
    	try {
    		boolean exists = jedis.exists("OFVCARD:" + username);

        	if (exists) {
        		threadPool.execute(createTaskByUpdateVCard(provider, username, vCardElement));
        		updated = true;
        	}
        	else {
        		threadPool.execute(createTaskByCreateVCard(provider, username, vCardElement));
        		created = true;
        	}
		}finally {
			XMPPServer.getInstance().getUserJedis().returnRes(jedis);
		}
        // Dispatch vCard events
        if (created) {
            // Alert listeners that a new vCard has been created
            VCardEventDispatcher.dispatchVCardCreated(username, vCardElement);
        } else if (updated) {
            // Alert listeners that a vCard has been updated
            VCardEventDispatcher.dispatchVCardUpdated(username, vCardElement);
        }
    }

	private Runnable createTaskByCreateVCard(final VCardProvider provider, final String username,
			final Element vCardElement) {
		return new Runnable() {
			public void run() {
				try {
					provider.createVCard(username, vCardElement);
				} catch (AlreadyExistsException e) {
					Log.error("AlreadyExistsException: username=" + username + ", vCardElement=" + vCardElement);
				}
			}
		};
	} 

	private Runnable createTaskByUpdateVCard(final VCardProvider provider, final String username,
			final Element vCardElement) {
		return new Runnable() {
			public void run() {
				try {
					provider.updateVCard(username, vCardElement);
				} catch (NotFoundException e) {
					Log.error("NotFoundException: username=" + username + ", vCardElement=" + vCardElement);
				}
			}
		};
	}

	public void deleteVCard(String username) {
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }

        final String vusername = username;
        threadPool.execute(new Runnable() {
			@Override
			public void run() {
				provider.deleteVCard(vusername);
			}
		});
        VCardEventDispatcher.dispatchVCardDeleted(username, null);
    }

	public Element getVCard(String username) {
        Element vCardElement = getOrLoadVCard(username);
        return vCardElement == null ? null : vCardElement.createCopy();
    }

	private Element getOrLoadVCard(String username) {
		return provider.loadVCard(username);
    }

    @Override
	public void initialize(XMPPServer server) {
        ......
    }

    @Override
	public void start() {
        ......
    }

    @Override
	public void stop() {
        // Remove this module as a user event listener
        UserEventDispatcher.removeListener(eventHandler);
    }

    public void reset() {
        //vcardCache.clear();
    }

	@Override
	public Iterator<String> getFeatures() {
		ArrayList<String> features = new ArrayList<String>();
        features.add("redis-vcard-temp");
        return features.iterator();
	}

	private class EventHandler extends UserEventAdapter {
        @Override
		public void userDeleting(User user, Map params) {
            try {
                deleteVCard(user.getUsername());
            } catch (UnsupportedOperationException ue) { /* Do Nothing */ }
        }
    }

    public UserCardEnity getUserCardByUserName (String username) {
    	Element element = getVCard(username);
    	UserCardEnity uce = new UserCardEnity();

    	if (element != null) {
    		String myName = element.elementText("MYNAME");
    		String sex = element.elementText("SEX");
    		String oname = element.elementText("ONAME");
    		String moblie = element.elementText("MOBILE");
    		String landline = element.elementText("LANDLINE");
    		String address = element.elementText("ADDRESS");
    		String workUnit = element.elementText("WORKUNIT");
    		String birthday = element.elementText("BIRTHDAY");
    		String photo = element.elementText("PHOTO");
    		String userType = element.elementText("USERTYPE");

    		uce.setMyName( myName);
    		uce.setSex(sex);
    		uce.setOname(oname);
    		uce.setMoblie( moblie) ;
    		uce.setLandline(landline);
    		uce.setAddress(address);
    		uce.setWorkUnit(workUnit) ;
    		uce.setBirthday(birthday);
    		uce.setPhoto(photo);

    		if (userType == null) {
    			uce.setUserType("");
    		}
    		else if (1 == Integer.valueOf(userType)) {
    			uce.setUserType("student");
    		}
    		else if (2 == Integer.valueOf(userType)) {
    			uce.setUserType("teacher");
    		}
    		else if (3 == Integer.valueOf(userType)) {
    			uce.setUserType("Guardian");
    		}
    		else if (4 == Integer.valueOf(userType)) {
    			uce.setUserType("edusun admin");
    		}
    		else if (5 == Integer.valueOf(userType)) {
    			uce.setUserType("Agents");
    		}
    		else {
    			uce.setUserType("Other locations");
    		}
    	}
    	return uce;
    }
}

用户名片就到这了。

用户搜索

搜索用户的时候,在openfire中都是重新查找关系数据库的。因为我们已经将用户预加载到了redis中。那么这里只需要对用户做一些分词存储检索即可。

本人在这里做了比较简单的分词处理。比如用户名,手机号码等。

首先要做的就是制作用户分词了。然后要做的就是需要修修改搜索的handler处理类。Openfire上提供了一个search搜索插件。查询消息最后提交给SearchPlugin这个类的handler方法。本人这里就不做描述。重点要说的就是如何在redis中分词存储。

客户端发送用户查找请求如下:

<iq id="E6l1b-43" to="[email protected]" type="get"><query xmlns="jabber:iq:search"></query></iq>

请看代码清单:

RedisSearchManager:

public class RedisSearchManager extends BasicModule{

	private static final Logger LOG = LoggerFactory.getLogger(RedisSearchManager.class);

	private static final Integer timeout = 1000*10;
	private static final int maxActive = 5000 * 10;
	private static final int maxIdle = 5;
	private static final long maxWait = (1000 * 100);

	private static JedisPool pool;
	private static XMPPServer loaclserver;
	private static JedisPoolConfig config;

	public RedisSearchManager() {
		super("Redis search manager");
	}

	private static JedisPoolConfig createConfig() {
           ......
	}

	private static void createJedisPool() {
		......//创建连接池
	}

	private static synchronized void poolInit() {
		boolean enabled = JiveGlobals.getBooleanProperty("plugin.userservice.enabled");
		if (enabled) {
			if (pool == null)
				createJedisPool();
		}
	}

	public Jedis getSearchJedis() {
		if (pool == null)
			poolInit();
		Jedis jedis = pool.getResource();
		jedis.select(3);
		return jedis;
	}

	public void returnSearchJedis(Jedis jedis) {
		pool.returnResource(jedis);
	}

	@Override
	public void initialize(XMPPServer server) {
.....
    }

	//创建用户的搜索关键词数据
	public void createSearchAllUserData() {
		Collection<User> userList = XMPPServer.getInstance().getUserJedis().getAllUser();
		for (User user : userList) {
			createSearchUserData(user);
		}
		System.out.println("Initialize the user search data is completed...(the end)");
	}

   //这里根据string长度切分。按照“|”分割
	public void createSearchUserData(User user) {
		Jedis jedis = getSearchJedis();
		IndexWriter iw = new IndexWriter(jedis);
		String username = user.getUsername();
		String keyword = "";
		if (username.length() >= 4) {
			int index = 4;
			for (int i=0; i<=username.length()-4; i++) {
				String usernameKey = username.substring(0, index++) + "|";
				keyword += usernameKey;
			}
		}
		else {
			keyword = username + "|";
		}

		if (user.getName() != null && !"".equals(user.getName())) {
			keyword += user.getName() + "|";
		}
		if (user.getEmail() != null && !"".equals(user.getEmail())) {
			keyword += user.getEmail() + "|";
		}
		if (user.getMoblie() != null && !"".equals(user.getMoblie())) {
			keyword += user.getMoblie();
		}

		if ("|".equals(keyword.substring(keyword.length()-1))) {
			keyword = keyword.substring(0, keyword.length()-1);
		}

		iw.addIdAndIndexItem(username, keyword);
		iw.addNeedSortItem("USERNAME",username);
        iw.addNeedSortItem("CREATIONDATE",user.getCreationDate() != null ? user.getCreationDate() : StringUtils.dateToMillis(new Date()));
        iw.addNeedSortItem("MODIFICATIONDATE",user.getModificationDate() != null ? user.getModificationDate() : StringUtils.dateToMillis(new Date()));
        iw.writer();
        System.out.println("create user search data, id:" + username + ", keyword:" + keyword);
        LOG.info("create user search data, id:" + username + ", keyword:" + keyword);
        returnSearchJedis(jedis);
	}
}

IndexWriter写入索引

public class IndexWriter {
	private Jedis jedis;
	private String id;
	private Map<String, String> items = new HashMap<String, String>();
	private String contentItems[];

	public IndexWriter(Jedis jedis) {
		if (!jedis.isConnected()) {
			jedis.connect();
		}
		this.jedis = jedis;
	}
	/**
	 * @param id
	 *            必须有
	 * @param content
	 *            是分词程序切分后的内容,每个词中间必须用用“|”分隔,如:中国|中国人|2012
	 */
	public void addIdAndIndexItem(String id, String content) {
		this.id = id;
		contentItems = content.split("\\|");
	}
	public void addNeedSortItem(String name, String value) {
		items.put(name, value);
	}
	public void writer() {
		indexWriter();
		itemWriter();
	}
	private void indexWriter() {
		if (!id.equals("") && contentItems.length != 0) {
			for (int i = 0; i < contentItems.length; i++) {
				jedis.sadd(contentItems[i].trim(), id);
			}
		}
	}
	private void itemWriter() {
		if (items.size() != 0) {
			Iterator<Entry<String, String>> it = items.entrySet().iterator();
			while (it.hasNext()) {
				Entry<String, String> entry = (Entry<String, String>) it.next();
				jedis.set(entry.getKey().toString() + ":" + id, entry
						.getValue().toString());
			}
		}
	}
}

IndexSearch:查找索引

public class IndexSearch {
	public static int ALPHA = 0;
	public static int DESC = 1;
	public static int ASC = 2;

	private Jedis jedis;
	private int limit = 100;
	private String itemName = null;
	private int pager = 0;

	public IndexSearch(Jedis jedis) {
		if (!jedis.isConnected()) {
			jedis.connect();
		}
		this.jedis = jedis;
	}

	private SortingParams getSP(String item, int sort) {
		SortingParams sp = new SortingParams();
		sp.limit(pager, limit);

		if (null == item || "".equals(item)) {
			switch (sort) {
			case 1:
				sp.desc();
				break;
			case 2:
				sp.asc();
				break;
			case 0:
			default:
				sp.alpha();
				break;
			}
		} else {
			switch (sort) {
			case 1:
				sp.by(itemName + ":*").desc();
				break;
			case 2:
				sp.by(itemName + ":*").asc();
				break;
			case 0:
			default:
				sp.by(itemName + ":*").alpha();
				break;
			}
		}
		return sp;
	}

	private List<String> isearch(int sort, String... query) {
		jedis.sinterstore("tempKey", query);
		return jedis.sort("tempKey", this.getSP(itemName, sort));
	}

	public List<String> search(String... query) {
		return this.isearch(0, query);
	}

	public List<String> search(int sort, String... query) {
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, String... query) {
		this.itemName = itemName;
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, int limit,
			String... query) {
		this.itemName = itemName;
		this.limit = limit;
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, int pager, int limit,
			String... query) {
		this.itemName = itemName;
		this.limit = limit;
		this.pager = pager;
		return this.isearch(sort, query);
	}
}

Ok用户搜索这就只贴出一些比较关键性的代码。提供思路。代码贴多了搞了篇幅很长。

时间: 2024-07-29 13:58:37

OpenFire源码学习之二十二:openfie对用户的优化(下)的相关文章

Swoole源码学习记录(十二)——ReactorThread模块

ReactorThread 这一章将分析Swoole的ReactorThread模块.虽然叫Thread,但是实际上使用的是swFactoryProcess也就是多进程模式.但是,在ReactorThread中,所有的事件监听是在线程中运行的(Rango只是简单提到了PHP不支持多线程安全,具体原因还有待请教--),比如在UDP模式下,是针对每一个监听的host开辟一个线程运行reactor,在TCP模式下,则是开启指定的reactor_num个线程用于运行reactor. 那么OK,先上swR

手机自动化测试:appium源码分析之bootstrap十二

手机自动化测试:appium源码分析之bootstrap十二 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478. ScrollTo package io.appium.android.bootstrap.handler; import com.android.uiautomator.core.UiObject; import com.android.uiautomator.c

struts2源码学习之初始化(二)

在上一篇struts2源码学习之初始化(一)中,详细描述了StrutsPrepareAndExecuteFilter的init()的主要工作,这一篇就详细说说Dispatcher.从上一篇文章中,我们知道了Dispatcher在Filter的init()方法中被创建出来,那么,它的功能是什么呢?Dispatcher类的功能正如它的名字所示,是派发,派发请求. PrepareOperations类预处理请求,比如找到findActionMapping(),找到之后就要交给Dispatcher,让D

OpenFire源码学习之二十九:集群

集群 Openfire的给集群提供了多种方案.一种是基于Hazelcast插件,还有基于Oracle的coherence插件. Linux集群配置 一.修改配置文件 /etc/hosts文件 openfire1 192.168.2.104 openfire2192.168.2.240 每台主机都需要配置 二.添加jar包 将coherence.jar.coherence-work.jar.tangosol.jar添加到lib目录 将clustering.jar放到plugins下面 Hazelc

OpenFire源码学习之二十:在openfire中使用redis插件(下)

Redis插件实现 首先来看下插件目录: RedisServicePlugin 源码清单: import java.io.File; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.jivesoftware.database.DbConnecti

OpenFire源码学习之二十一:openfie对用户的优化(上)

用户类 优化用户主要是要解决用户的连接量.已经对用户的访问速度和吞吐量. 预初始化 在前面的带面中提出来了用户的预初始化.这里就不在贴出来了.下面将redis用户库连接池处理贴出来UserJedisPoolManager public class UserJedisPoolManager extends BasicModule{ private static final Logger log = LoggerFactory.getLogger(UserJedisPoolManager.class

OpenFire源码学习之二十三:关于消息的优化处理

消息处理 之前有说过,openfire的消息处理策略本人并不是很喜欢.先看下openfire上脱机消息策略. 个人认为消息关于会话的消息,用户的存储量应该无限大.服务器不应该被消息吃撑了.所谓聊天通讯,这一关很重要. Openfire的消息是什么流程呢. 1.当用户登陆连接的时候.握手.认证.绑定资源.获取花名册.获取离线消息. 2.服务端会查找关系型数据库.经本人测试离线消息在数据库表中达到100万条以上的时候,查询速度非常慢,甚至会导致openfire奔溃. ..... 那么openfire

OpenFire源码学习之二十七:Smack源码解析

Smack Smack是一个用于和XMPP服务器通信的类库,由此可以实现即时通讯和聊天.Android中开发通讯APP也可以使用这个包.关于smack的中文开发文档,目前网上也有很多. 下面本,将从源码中分析smack的几个案例. 连接 关于smack的Connection是连接XMPP服务器的默认实现.他有两个构造函数,一个是XMPPConecttion(String) 接收服务器地址名的参数.这个是默认的比较常用的构造方法. l 查找一个DNS的SRC,首先需要找到它精确的服务器地址和端口默

(转)OpenFire源码学习之十:连接管理(上)

转:http://blog.csdn.net/huwenfeng_2011/article/details/43415827 关于连接管理分为上下两部分 连接管理 在大并发环境下,连接资源 需要随着用户并发访问量的增加而增加,所以可伸缩的连接资源就是支持大访问量的关键技术.openfire系统通过增加独立部署的连接管理器程序提高并发的能力,连接管理的前端是一台负载均衡设备,它负责把用户访问分配到指定连接管理器,多台连接管理器在访问服务器.使用连接管理器后,服务器的连接池是提供给连接管理器连 接,

three.js 源码注释(八十二)extras/geometries/RingGeometry.js

商域无疆 (http://blog.csdn.net/omni360/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:商域无疆 -  本博客专注于 敏捷开发及移动和物联设备研究:数据可视化.GOLANG.Html5.WEBGL.THREE.JS,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 俺也是刚开始学,好多地儿肯定不对还请见谅. 以下代码是THREE.JS 源码文件中extras/geometries/RingGeometry.js文件的注释. 更多更