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

用户类

优化用户主要是要解决用户的连接量。已经对用户的访问速度和吞吐量。

预初始化

在前面的带面中提出来了用户的预初始化。这里就不在贴出来了。下面将redis用户库连接池处理贴出来UserJedisPoolManager

public class UserJedisPoolManager extends BasicModule{

	private static final Logger log = LoggerFactory.getLogger(UserJedisPoolManager.class);

	private static final String OF_ALL_USER = "select username, encryptedPassword, name, email, moblie, creationDate, modificationDate from ofuser";
	private static final String OF_USER_VCARD = "select username, vcard from ofvcard";
	private static final String OF_PRESENCE = "select username, offlinePresence, offlineDate from ofPresence";

	//private static final String REDIS_USER = "REDIS_USER";
	private static final Integer timeout = 1000*10;
	private static final int maxActive = 5000 * 10;
	private static final int maxIdle = 50;
	private static final long maxWait = (1000 * 100);

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

	public UserJedisPoolManager() {
		super("User redis manager");
	}

	private static JedisPoolConfig createConfig() {
        configs = new JedisPoolConfig();
        configs.setMaxActive(maxActive);
        configs.setMaxIdle(maxIdle);
        configs.setMaxWait(maxWait);
        configs.setTestOnBorrow(false);
        return configs;
	}

	private void createJedisPool() {
 		RedisConfig redisConfig = loaclserver.getJedisConfDao().getRedisConfig("REDIS_USER");
		if (redisConfig != null) {
					+ " ,auto:" + redisConfig.getAuto());
			System.out.println(redisConfig.getAuto() .equals("") );
			pool = new JedisPool(createConfig(), redisConfig.getIp(), Integer.valueOf(redisConfig.getPort().trim()),
					timeout, redisConfig.getAuto().equals("")  ? null : redisConfig.getAuto());
			Jedis jedis = pool.getResource();
			jedis.select(0);
			if(!jedis.exists("OFUSER:admin")) {
				DefaultAuthProvider dup = new DefaultAuthProvider();
				try {
					String password = dup.getPassword("admin");
					password = AuthFactory.encryptPassword(password);
					Map<String, String> map = new HashMap<String, String>();
					map.put("NAME", "admin");
					map.put("PASSWORD", password);
					map.put("CREATIONDATE", "0");
					map.put("MODIFICATIONDATE", "0");
					jedis.hmset("OFUSER:admin", map);
				} catch (UserNotFoundException e) {
					e.printStackTrace();
				}finally{
					pool.returnResource(jedis);
				}			}
		}
	}

	private void poolInit() {
		createJedisPool();
	}

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

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

	@Override
	public void initialize(XMPPServer server) {
		super.initialize(server);
		loaclserver = server;
		poolInit();
		log.info("UserManager By Redis: start init....");

    }

	public Collection<User> getAllUser() {
		Collection<User> users = new ArrayList<User>();
		PreparedStatement pstmt = null;
		Connection con = null;
	    ResultSet rs = null;
	    try {
			con = (Connection) DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(OF_ALL_USER);
			rs = pstmt.executeQuery();
			while(rs.next()) {
				User user = new User();
				user.setUsername(rs.getString(1));
				user.setPassword(rs.getString(2));
				user.setName(rs.getString(3));
				user.setEmail(rs.getString(4));
				user.setMoblie(rs.getString(5));
				user.setCreationDate(rs.getString(6));
				user.setModificationDate(rs.getString(7));
				users.add(user);
			}
		}catch (Exception e) {
			 log.info( e.getMessage());
			 e.printStackTrace();
		 }
		 finally {
			 DbConnectionManager.closeConnection(pstmt, con);
		 }
		return users;
	}

	public Collection<UserVcard> getUserVcard() {
		Collection<UserVcard> userVcards = new ArrayList<UserVcard>();
		PreparedStatement pstmt = null;
		Connection con = null;
	    ResultSet rs = null;
	    try {
			con = (Connection) DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(OF_USER_VCARD);
			rs = pstmt.executeQuery();
			while(rs.next()) {
				UserVcard user = new UserVcard();
				user.setUsername(rs.getString(1));
				user.setVcard(rs.getString(2));
				userVcards.add(user);
			}
		}catch (Exception e) {
			 log.info( e.getMessage());
			 e.printStackTrace();
		 }
		 finally {
			 DbConnectionManager.closeConnection(pstmt, con);
		 }
		return userVcards;
	}

	public Collection<Presence> getPresences() {
		......
	}
}

在上面createJedisPool方法中预置了管理员的账号。这是因为我们需要修改openfire的用户认证dao。也就是说web控制台的管理员。在登陆web页面的时候,我们认证也是先走redis验证的。

用户认证

用户认证,首先需要重新实现AuthProvider。Openfire当中默认使用的是DefaultAuthProvider来操作数据层。当然他也提供了其他的方式实现接口,比如:HybridAuthProvider、JDBCAuthProvider、NativeAuthProvider、POP3AuthProvider等。

写完AuthProvider的Redis实现后,接下来需要基于Redis的用户DAO。

下面是两个类的源码清单:

RedisAuthProvider

public class RedisAuthProvider implements AuthProvider{

	private static final Logger log = LoggerFactory.getLogger(RedisAuthProvider.class);
	private static HmThreadPool threadPool = new HmThreadPool(3);
	......

	@Override
	public void authenticate(String username, String password)
			throws UnauthorizedException, ConnectionException,
			InternalUnauthenticatedException {
		......
	}

	@Override
	public void authenticate(String username, String token, String digest)
			throws UnauthorizedException, ConnectionException,
			InternalUnauthenticatedException {
		......
	}

	@Override
	public String getPassword(String username) throws UserNotFoundException,
			UnsupportedOperationException {
		Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
		try {
			String pw = jedis.hmget("OFUSER:" + username, "PASSWORD").get(0);
			if (pw == null) {
				String userid = jedis.get("MOBILE:" + username);
				pw = jedis.hmget("OFUSER:" + userid, "PASSWORD").get(0);
			}
			return AuthFactory.decryptPassword(pw);
		} finally {
			XMPPServer.getInstance().getUserJedis().returnRes(jedis);
		}
	}

	@Override
	public void setPassword(String username, String password)
			throws UserNotFoundException, UnsupportedOperationException {
		Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
		try {
			password = AuthFactory.encryptPassword(password);
			jedis.hset("OFUSER:" + username, "PASSWORD", password);
		} finally {
			XMPPServer.getInstance().getUserJedis().returnRes(jedis);
		}
		threadPool.execute(createTask(XMPPServer.getInstance().getJedisConfDao().getAuthProvider(), username, password));
	}

	@Override
	public boolean supportsPasswordRetrieval() {
		// TODO Auto-generated method stub
		return true;
	}

	private static final String UPDATE_PASSWORD =
            "UPDATE ofUser SET encryptedPassword=? WHERE username=?";

	private Runnable createTask(final AuthProvider edp, final String username,
											final String password) {
        return new Runnable() {
            public void run() {
            	try {
					//edp.setPassword(username, password);

            		Connection con = null;
                    PreparedStatement pstmt = null;
                    try {
                        con = DbConnectionManager.getConnection();
                        pstmt = con.prepareStatement(UPDATE_PASSWORD);
                        if (password == null) {
                            pstmt.setNull(1, Types.VARCHAR);
                        }
                        else {
                            pstmt.setString(1, password);
                        }
                        pstmt.setString(2, username);
                        pstmt.executeUpdate();
                    }
                    catch (SQLException sqle) {
                        throw new UserNotFoundException(sqle);
                    }
                    finally {
                        DbConnectionManager.closeConnection(pstmt, con);
                    }

				} catch (UserNotFoundException e) {
					log.info("UserNotFoundException: " + username);
				}
            }
       };
   }
}

用户认证写完后,要记得修改系统属性表:ofProperty


provider.auth.className


org.jivesoftware.util.redis.expand.RedisAuthProvider

RedisUserProvider:

public class RedisUserProvider implements UserProvider{
......
	public User loadUser(String username) throws UserNotFoundException {
        if(username.contains("@")) {
            if (!XMPPServer.getInstance().isLocal(new JID(username))) {
                throw new UserNotFoundException("Cannot load user of remote server: " + username);
            }
            username = username.substring(0,username.lastIndexOf("@"));
        }

        Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();

        try {
        	Map<String, String> map = jedis.hgetAll("OFUSER:" + username);
    		String usernames = username;
    		if (map.isEmpty()) {
    			String userid = jedis.get("OFUSER:" + username);
    			map = jedis.hgetAll("OFUSER:" + userid);
    			if (map.isEmpty()) {
    				return XMPPServer.getInstance().getJedisConfDao().getUserProvider().loadUser(username);
    			}
    			usernames = userid;
    		}
    		String name = map.get("NAME");
    		String email = map.get("EMAIL");
    		String mobile = map.get("MOBILE");
    		String creationDate = map.get("CREATIONDATE");
    		String modificationDate = map.get("MODIFICATIONDATE");

    		User user = new User(usernames, name, email, mobile, new Date(Long.parseLong(creationDate.equals("0")||creationDate.equals("") ? StringUtils.dateToMillis(new Date()) : creationDate)),
    				new Date(Long.parseLong(modificationDate.equals("0")||modificationDate.equals("") ? StringUtils.dateToMillis(new Date()) : modificationDate)));

    		return user;

		} finally {
			XMPPServer.getInstance().getUserJedis().returnRes(jedis);
		}

    }

    public User createUser(String username, String password, String name, String email)
            throws UserAlreadyExistsException
    {
    	return createUser(username, password, name, email, null);
    }

    public User createUser(String username, String password, String name, String email, String moblie)
            throws UserAlreadyExistsException{
        try {
            loadUser(username);
            // The user already exists since no exception, so:
            throw new UserAlreadyExistsException("Username " + username + " already exists");
        }
        catch (UserNotFoundException unfe) {

            Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();

            Map<String, String> hash = new HashMap<String, String>();

            password = AuthFactory.encryptPassword(password);

            hash.put("PASSWORD", password);
            if (name != null && !"".equals(name))
            	hash.put("NAME", name);
            if (email != null && !"".equals(email))
            	hash.put("EMAIL", email);
            if (moblie != null && !"".equals(moblie))
            	hash.put("MOBILE", moblie);

            Date now = new Date();
            hash.put("CREATIONDATE", StringUtils.dateToMillis(now));
            hash.put("MODIFICATIONDATE", StringUtils.dateToMillis(now));

            try {
            	jedis.hmset("OFUSER:" + username, hash);
			} finally {
				XMPPServer.getInstance().getUserJedis().returnRes(jedis);
			}

            threadPool.execute(createTaskAddUser(username, null, password, name, email, moblie));
            return new User(username, name, email, moblie, now, now);
        }
    }

    private Runnable createTaskAddUser(final String username, final String password, final String encryptedPassword,
    		final String name, final String email, final String moblie) {
    		return new Runnable() {
    			public void run () {
    				.....
    			}
    		};
    }

    public void deleteUser(String username) {
        ......
    }

    public int getUserCount() {
        int count = 0;
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(USER_COUNT);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                count = rs.getInt(1);
            }
        }
        catch (SQLException e) {
            Log.error(e.getMessage(), e);
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        return count;
    }

    public Collection<User> getUsers() {
        Collection<String> usernames = getUsernames(0, Integer.MAX_VALUE);
        return new UserCollection(usernames.toArray(new String[usernames.size()]));
    }

    public Collection<String> getUsernames() {
        return getUsernames(0, Integer.MAX_VALUE);
    }

    private Collection<String> getUsernames(int startIndex, int numResults) {
        ......
    }

    public Collection<User> getUsers(int startIndex, int numResults) {
        Collection<String> usernames = getUsernames(startIndex, numResults);
        return new UserCollection(usernames.toArray(new String[usernames.size()]));
    }

    public void setName(String username, String name) throws UserNotFoundException {
        ......
    }

    public void setEmail(String username, String email) throws UserNotFoundException {
        ......
    }

    public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
        ......
    }

    public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
        ......
    }

    public Set<String> getSearchFields() throws UnsupportedOperationException {
        return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email"));
    }

    public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
        return findUsers(fields, query, 0, 100);
    }

    public Collection<User> findUsers(Set<String> fields, String query, int startIndex,
            int numResults) throws UnsupportedOperationException
    {
        ......
    }
    /**
     * Make sure that Log.isDebugEnabled()==true before calling this method.
     * Twenty elements will be logged in every log line, so for 81-100 elements
     * five log lines will be generated
     * @param listElements a list of Strings which will be logged
     */
    private void LogResults(List<String> listElements) {
      ......
    }

	@Override
	public void setMoblie(String username, String moblie)
			throws UserNotFoundException {
       ......
	}
}

注意:这里有个moblie字段。在原来openfire用户认证表里面是没有这个字段的。这里是本人新加的字段。方便手机登陆。看各自的页面场景啦。

时间: 2024-10-21 03:30:33

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

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

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

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源码学习之二十七:Smack源码解析

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

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

用户名片 在预初始化中,贴出来用户名片的程序.这里也一样不在重复. 首先同样先修改系统属性: provider.vcard.className org.jivesoftware.util.redis.expand.RedisVCardProvider 然后需要修改VCardManager名片管理这个类. RedisVCardProvider: import redis.clients.jedis.Jedis; public class RedisVCardProvider implements

OpenFire源码学习之二十五:消息回执与离线消息(下)

这一篇紧接着上面继续了. 方案二 基于redis的消息回执.主要流程分为下面几个步骤: 1)将消息暂存储与redis中,设置好消息的过期时间 2)客户端回执消息id来消灭暂存的消息 3)开通单独线程论坛在第1)步中的消息.根据消息的时间重新发送消息.如果消息第一次存放的时间大雨有效期(自定义10秒),解析消息中的to查找用户是否还在线.如果在则T掉(因为它长时间不理会服务的重要命令),如果不在线则将消息放置离线表. OK,先来看看消息的存储格式吧. 1.MESSAGE消息 用户集合 SADD 

OpenFire源码学习之二十八:与其他系统的用户整合

与三方系统整合.Openfire提供了一套不错的解决方案.其实openfire的用户插件也做http方式的用户数据同步.关于openfire的用户整合,本人也不是十分赞同(这仅仅只是在我们自己的项目下).我们的办法是做数据共享池.这个解决方案可以用到redis. 下面是opnfire的 解决方案 openfire可以非常方便的整合现有系统用户. 进入openfire管理控制台-服务器-服务管理器-系统属性 可以发现如下配置 : 以下Provider是openfire默认自己管理用户组 属性名 属

jquery源码学习(二)sizzle部分 【转】

一,sizzle的基本原理 sizzle是jquery选择器引擎模块的名称,早在1.3版本就独立出来,并且被许多其他的js库当做默认的选择器引擎.首先,sizzle最大的特点就是快.那么为什么sizzle当时其他引擎都快了,因为当时其他的引擎都是按照从左到右逐个匹配的方式来进行查找的,而sizzle刚好相反是从右到左找的. 举个简单的例子 “.a .b .c”来说明为什么sizzle比较快.这个例子如果按照从左到右的顺序查找,很明显需要三次遍历过程才能结束,即先在document中查找.a,然后

Spring 源码学习(二) IOC容器启动过程

这一节主要是记录一下Spring Ioc 容器的启动过程. Spring 的 Ioc 容器是怎么被加载和使用的? web容器为它提供了宿主环境 ServlectContext,  Tomcat 启动时会读取web.xml. 并且实例化web.xml中配置的ContextLoaderListener ,下面看一下ContextLoaderListener的创建过程: 在实例化ContextLoaderListener 之后,通过接口回调执行ContextLoaderListener 类中的cont