Hazelcast (www.hazelcast.com])是一种内存数据网格 in-memory data grid,提供Java程序员关键任务交易和万亿级内存应用。
Hazelcast的集群属于“无主节点”,这意味着它不是一个客户端 - 服务器系统。有一个集群的领导者,默认是最老的成员,管理数据是如何在系统间分布,但是,如果该节点当机,那么是下面一个旧的节点接管。
你所用的数据结构Maps List和队列都是保存在内存中。如果集群中的一个节点死亡,数据不会丢失,但如果多个节点同时当机,那么你就麻烦了
下面以BigWideWorl为案例说明Hazelcast的使用,首先依赖包导入:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.1</version>
</dependency>
public class BigWideWorld {
private static Random rand = new Random(System.currentTimeMillis());
private final Users users = new Users();
private final int totalNumUsers = users.size();
public String nextUser() {
User user = users.get(rand.nextInt(totalNumUsers));
String name = user.getUsername();
return name;
}
}
nextUser()方法可以随机获得一个用户的名称,用户集合由Users类管理。
public class Users {
/** The users in the database */
private final User[] users = { new User("fred123", "Fred", "Jones", "[email protected]"),
new User("jim", "Jim", "Jones", "[email protected]"),
new User("bill", "Bill", "Jones", "[email protected]"),
new User("ted111", "Edward", "Jones", "[email protected]"),
new User("annie", "Annette", "Jones", "[email protected]"),
new User("lucy", "Lucy", "Jones", "[email protected]"),
new User("jimj", "James", "Jones", "[email protected]"),
new User("jez", "Jerry", "Jones", "[email protected]"),
new User("will", "William", "Jones", "[email protected]"),
new User("shaz", "Sharon", "Jones", "[email protected]"),
new User("paula", "Paula", "Jones", "[email protected]"),
new User("leo", "Leonardo", "Jones", "[email protected]"), };
private final Map<String, User> userMap;
public Users() {
userMap = new HashMap<String, User>();
for (User user : users) {
userMap.put(user.getUsername(), user);
}
}
/**
* The number of users in the database
*/
public int size() {
return userMap.size();
}
/**
* Given a number, return the user
*/
public User get(int index) {
return users[index];
}
/**
* Given the user's name return the User details
*/
public User get(String username) {
return userMap.get(username);
}
/**
* Return the user names.
*/
public Set<String> getUserNames() {
return userMap.keySet();
}
}
Users其实类似一个Map集合。下面是用户类:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private final String username;
private final String firstName;
private final String lastName;
private final String email;
public User(String username, String firstName, String lastName, String email) {
super();
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getUsername() {
return username;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("User: ");
sb.append(username);
sb.append(" ");
sb.append(firstName);
sb.append(" ");
sb.append(lastName);
sb.append(" ");
sb.append(email);
return sb.toString();
}
}
用户User必须实现序列化接口。
客户端调用代码如下:
public static void main(String[] args) throws InterruptedException {
MyApplication application = new MyApplication();
String username = theWorld.nextUser();
if (application.isLoggedOn(username)) {
application.logout(username);
} else {
application.logon(username);
}
application.displayUsers();
TimeUnit.SECONDS.sleep(2);
}
}
此代码创建BigWideWorld和所有MyApplication的实例。然后,它无限循环抓住抓取下一个随机的用户名。如果是已经登录的用户,那么注销该用户。如果没有登录的用户,然后登录用户后显示。
运行该代码:
java -cp /your path to the/hazelcast-3.1/lib/hazelcast-1.jar:. com.captaindebug.hazelcast.gettingstarted.Main
得到如下结果:
Logged on users:
User: fred123 Fred Jones [email protected]
User: jimj James Jones [email protected]
User: shaz Sharon Jones [email protected]
User: paula Paula Jones [email protected]
User: lucy Lucy Jones [email protected]
User: jez Jerry Jones [email protected]
User: jim Jim Jones [email protected]
7 -- 14:54:16-17
可以多开几个终端运行这个代码。
你会看到用户不断在登录推出,用户Map集合每次显示出改变,关键是:一个应用的Map大小变化会影响其他窗口应用内的大小,好像大家共用一个Users的Map集合。
发布者和订阅者实现
假设有一个模型:
public class StockPrice implements Serializable {
private final BigDecimal bid;
private final String code;
private final long timestamp;
/**
* Create a StockPrice for the given stock at a given moment
*/
public StockPrice(BigDecimal bid, BigDecimal ask, String code, String description,
long timestamp) {
super();
this.bid = bid;
this.ask = ask;
this.code = code;
this.description = description;
this.timestamp = timestamp;
}
public BigDecimal getBid() {
return bid;
}
public BigDecimal getAsk() {
return ask;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public long getTimestamp() {
return timestamp;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Stock - ");
sb.append(code);
sb.append(" - ");
sb.append(description);
sb.append(" - ");
sb.append(description);
sb.append(" - Bid: ");
sb.append(bid);
sb.append(" - Ask: ");
sb.append(ask);
sb.append(" - ");
SimpleDateFormat df = new SimpleDateFormat("HH:MM:SS");
sb.append(df.format(new Date(timestamp)));
return sb.toString();
}
}
要求将股票的买入卖出价格在任何时间发布给做市商。
发布者代码:
private static Random random = new Random();
private final String description;
private volatile boolean running;
public MarketMaker(String topicName, String stockCode, String description) {
this.stockCode = stockCode;
this.description = description;
this.topic = createTopic(topicName);
running = true;
}
@VisibleForTesting
ITopic createTopic(String topicName) {
HazelcastInstance hzInstance = Hazelcast.newHazelcastInstance();
return hzInstance.getTopic(topicName);
}
Thread thread = new Thread(this);
thread.start();
}
@Override
public void run() {
do {
publish();
sleep();
} while (running);
}
StockPrice price = createStockPrice();
System.out.println(price.toString());
topic.publish(price);
}
@VisibleForTesting
StockPrice createStockPrice() {
double price = createPrice();
DecimalFormat df = new DecimalFormat("#.##");
BigDecimal bid = new BigDecimal(df.format(price - variance(price)));
BigDecimal ask = new BigDecimal(df.format(price + variance(price)));
StockPrice stockPrice = new StockPrice(bid, ask, stockCode, description,
System.currentTimeMillis());
return stockPrice;
}
int val = random.nextInt(2010 - 1520) + 1520;
double retVal = (double) val / 100;
return retVal;
}
private double variance(double price) {
return (price * 0.01);
}
private void sleep() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void stop() {
running = false;
}
MarketMaker bt = new MarketMaker("STOCKS", "BT.L", "British Telecom");
MarketMaker cbry = new MarketMaker("STOCKS", "CBRY.L", "Cadburys");
MarketMaker bp = new MarketMaker("STOCKS", "BP.L", "British Petrolium");
bt.publishPrices();
cbry.publishPrices();
bp.publishPrices();
}
其中代码关键是:
ITopic createTopic(String topicName) {
HazelcastInstance hzInstance = Hazelcast.newHazelcastInstance();
return hzInstance.getTopic(topicName);
}
这是设置Hazelcast,创建一个主题topic用于股票发布。真正发布是在run方法中的topic.publish(price);
订阅者代码如下:
public Client(String topicName) {
HazelcastInstance hzInstance = Hazelcast.newHazelcastInstance();
ITopic topic = hzInstance.getTopic(topicName);
topic.addMessageListener(this);
}
/**
* @see com.hazelcast.core.MessageListener#onMessage(com.hazelcast.core.Message)
*/
@Override
public void onMessage(Message arg0) {
System.out.println("Received: " + arg0.getMessageObject().toString());
}
new Client('STOCKS");
}
下面是运行,需要配合两个包:hazel cast-3.1.jar and guava-13.0.1.jar.
下面是订阅者运行:
java -cp ./:/Users/Roger/tmp/mm/guava-13.0.1.jar:/Users/Roger/tmp/mm/hazelcast-3.1.jar com.captaindebug.hazelcast.pubsub.Client