zookeeper+kafka集群的安装

时效性要求很高的数据,库存,采取的是数据库+缓存双写的技术方案,也解决了双写的一致性的问题
缓存数据生产服务,监听一个消息队列,然后数据源服务(商品信息管理服务)发生了数据变更之后,就将数据变更的消息推送到消息队列中
缓存数据生产服务可以去消费到这个数据变更的消息,然后根据消息的指示提取一些参数,然后调用对应的数据源服务的接口,拉去数据,这个时候一般是从mysql库中拉去的
1、zookeeper集群搭建
zookeeper-3.4.5.tar.gz使用WinSCP拷贝到/usr/local目录下。
对zookeeper-3.4.5.tar.gz进行解压缩:tar -zxvf zookeeper-3.4.5.tar.gz。
对zookeeper目录进行重命名:mv zookeeper-3.4.5 zk
配置zookeeper相关的环境变量
vi ~/.bashrc
export ZOOKEEPER_HOME=/usr/local/zk
export PATH=$ZOOKEEPER_HOME/bin
source ~/.bashrc

cd zk/conf
cp zoo_sample.cfg zoo.cfg

vi zoo.cfg

  修改:

dataDir=/usr/local/zk/data

  新增:

server.0=eshop-cache01:2888:3888	
server.1=eshop-cache02:2888:3888
server.2=eshop-cache03:2888:3888

  

cd zk
mkdir data
cd data

vi myid
0

在另外两个节点上按照上述步骤配置ZooKeeper,使用scp将zk和.bashrc拷贝到eshop-cache02和eshop-cache03上即可。唯一的区别是标识号分别设置为1和2。

分别在三台机器上执行:zkServer.sh start。
检查ZooKeeper状态:zkServer.sh status,应该是一个leader,两个follower
jps:检查三个节点是否都有QuromPeerMain进程
 
2、kafka集群搭建
scala,就是一门编程语言,现在比较火,很多比如大数据领域里面的spark(计算引擎)就是用scala编写的

scala-2.11.4.tgz使用WinSCP拷贝到/usr/local目录下。
对scala-2.11.4.tgz进行解压缩:tar -zxvf scala-2.11.4.tgz。
对scala目录进行重命名:mv scala-2.11.4 scala

配置scala相关的环境变量
vi ~/.bashrc
export SCALA_HOME=/usr/local/scala
export PATH=$SCALA_HOME/bin
source ~/.bashrc

查看scala是否安装成功:scala -version

按照上述步骤在其他机器上都安装好scala。使用scp将scala和.bashrc拷贝到另外两台机器上即可。
kafka_2.9.2-0.8.1.tgz使用WinSCP拷贝到/usr/local目录下。
对kafka_2.9.2-0.8.1.tgz进行解压缩:tar -zxvf kafka_2.9.2-0.8.1.tgz。
对kafka目录进行改名:mv kafka_2.9.2-0.8.1 kafka

配置kafka
vi /usr/local/kafka/config/server.properties
broker.id:依次增长的整数,0、1、2,集群中Broker的唯一id
zookeeper.connect=192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181

安装slf4j
slf4j-1.7.6.zip上传到/usr/local目录下
unzip slf4j-1.7.6.zip
把slf4j中的slf4j-nop-1.7.6.jar复制到kafka的libs目录下面

解决kafka Unrecognized VM option 'UseCompressedOops'问题

vi /usr/local/kafka/bin/kafka-run-class.sh

if [ -z "$KAFKA_JVM_PERFORMANCE_OPTS" ]; then
KAFKA_JVM_PERFORMANCE_OPTS="-server -XX:+UseCompressedOops -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true"
fi

去掉-XX:+UseCompressedOops即可

按照上述步骤在另外两台机器分别安装kafka。用scp把kafka拷贝到其他机器即可。
唯一区别的,就是server.properties中的broker.id,要设置为1和2

在三台机器上的kafka目录下,分别执行以下命令:nohup bin/kafka-server-start.sh config/server.properties &

使用jps检查启动是否成功

使用基本命令检查kafka是否搭建成功

bin/kafka-topics.sh --zookeeper 192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181 --topic test --replication-factor 1 --partitions 1 --create

bin/kafka-console-producer.sh --broker-list 192.168.31.181:9092,192.168.31.19:9092,192.168.31.227:9092 --topic test

bin/kafka-console-consumer.sh --zookeeper 192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181 --topic test --from-beginning
 
kafka+ehcache+redis完成缓存数据生产服务的开发:
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.roncoo.eshop.cache.listener.InitListener;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

@EnableAutoConfiguration
@SpringBootApplication
@ComponentScan
@MapperScan("com.roncoo.eshop.cache.mapper")
public class Application {
 
    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource dataSource() {
        return new org.apache.tomcat.jdbc.pool.DataSource();
    }
    
    @Bean
    public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
        return sqlSessionFactoryBean.getObject();
    }
 
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
    
    @Bean
    public JedisCluster JedisClusterFactory() {
        Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
        jedisClusterNodes.add(new HostAndPort("192.168.31.19", 7003));
        jedisClusterNodes.add(new HostAndPort("192.168.31.19", 7004));
        jedisClusterNodes.add(new HostAndPort("192.168.31.227", 7006));
        JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
        return jedisCluster;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
   	@Bean
       public ServletListenerRegistrationBean servletListenerRegistrationBean() {
       	ServletListenerRegistrationBean servletListenerRegistrationBean = 
       			new ServletListenerRegistrationBean();
       	servletListenerRegistrationBean.setListener(new InitListener());  
       	return servletListenerRegistrationBean;
     }
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

  

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.roncoo.eshop.kafka.KafkaConsumer;
import com.roncoo.eshop.spring.SpringContext;


/**
 * 系统初始化的监听器
 */
public class InitListener implements ServletContextListener {
	
	public void contextInitialized(ServletContextEvent sce) {
		ServletContext sc = sce.getServletContext();
		ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(sc);
		SpringContext.setApplicationContext(context);  
		
		new Thread(new KafkaConsumer("cache-message")).start();
	}
	
	public void contextDestroyed(ServletContextEvent sce) {
		
	}

}

  

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

//缓存配置管理类
@Configuration
@EnableCaching
public class CacheConfiguration {
	@Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
		EhCacheManagerFactoryBean ehCacheManagerFactoryBean=
				new EhCacheManagerFactoryBean();
		ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); 
		ehCacheManagerFactoryBean.setShared(true);
		return ehCacheManagerFactoryBean;
	}
	@Bean
	public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean){
		return new EhCacheCacheManager(ehCacheManagerFactoryBean.getObject());
	}
}

  

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;

// kafka消费者
public class KafkaConsumer implements Runnable   {
	private ConsumerConnector consumerConnector;
	private String topic;
	
	public KafkaConsumer(String topic){
		this.consumerConnector=Consumer.createJavaConsumerConnector(
				createConsumerConfig());
		this.topic=topic;
	}
	
	@SuppressWarnings("rawtypes")
	public void run(){
		Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
        topicCountMap.put(topic, 1);
        
        Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = 
        		consumerConnector.createMessageStreams(topicCountMap);
        List<KafkaStream<byte[], byte[]>> streams = consumerMap.get(topic);
        
        for (KafkaStream stream : streams) {
            new Thread(new KafkaMessageProcessor(stream)).start();
        }
	}
	
	//创建kafka cosumer config
	private static ConsumerConfig createConsumerConfig() {
        Properties props = new Properties();
        props.put("zookeeper.connect", "192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181");
        props.put("group.id", "eshop-cache-group");
        props.put("zookeeper.session.timeout.ms", "40000");
        props.put("zookeeper.sync.time.ms", "200");
        props.put("auto.commit.interval.ms", "1000");
        return new ConsumerConfig(props);
    }
}

  

import com.alibaba.fastjson.JSONObject;
import com.roncoo.eshop.model.ProductInfo;
import com.roncoo.eshop.model.ShopInfo;
import com.roncoo.eshop.service.CacheService;
import com.roncoo.eshop.spring.SpringContext;

import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;

//kafka消息处理线程
@SuppressWarnings("rawtypes")
public class KafkaMessageProcessor implements Runnable {

	private KafkaStream  kafkaStream;
	private CacheService cacheService;
	
	public KafkaMessageProcessor(KafkaStream kafkaStream) {
		this.kafkaStream = kafkaStream;
		this.cacheService = (CacheService) SpringContext.getApplicationContext()
				.getBean("cacheService"); 
	}
	
	@SuppressWarnings("unchecked")
	public void run(){
		ConsumerIterator<byte[],byte[]> it=	kafkaStream.iterator();
		while(it.hasNext()){
			String message =new String(it.next().message());
			// 首先将message转换成json对象
			JSONObject messageJSONObject =JSONObject.parseObject(message);
			
			// 从这里提取出消息对应的服务的标识
			String serviceId =messageJSONObject.getString("serviceId");  
			
			// 如果是商品信息服务
			if("productInfoService".equals(serviceId)){
				processProductInfoChangeMessage(messageJSONObject);
			}else if("shopInfoService".equals(serviceId)) {
        		processShopInfoChangeMessage(messageJSONObject);  
        	}
		}
	}

	//处理商品信息变更的消息
	private void processProductInfoChangeMessage(JSONObject messageJSONObject){
		// 提取出商品id
		Long productId = messageJSONObject.getLong("productId");
		
		// 调用商品信息服务的接口
		// 直接用注释模拟:getProductInfo?productId=1,传递过去
		// 商品信息服务,一般来说就会去查询数据库,去获取productId=1的商品信息,然后返回回来
		
		String productInfoJSON = "{\"id\": 1, \"name\": \"iphone7手机\", \"price\": 5599, \"pictureList\":\"a.jpg,b.jpg\", \"specification\": \"iphone7的规格\", \"service\": \"iphone7的售后服务\", \"color\": \"红色,白色,黑色\", \"size\": \"5.5\", \"shopId\": 1}";
		ProductInfo productInfo = JSONObject.parseObject(productInfoJSON, ProductInfo.class);
		cacheService.saveProductInfo2LocalCache(productInfo);
		System.out.println("===================获取刚保存到本地缓存的商品信息:" + cacheService.getProductInfoFromLocalCache(productId));  
		cacheService.saveProductInfo2ReidsCache(productInfo);  
	}
	
	//处理店铺信息变更的消息
	private void processShopInfoChangeMessage(JSONObject messageJSONObject){
		// 提取出商品id
		Long productId = messageJSONObject.getLong("productId");
		Long shopId = messageJSONObject.getLong("shopId"); 
		
		String shopInfoJSON = "{\"id\": 1, \"name\": \"小王的手机店\", \"level\": 5, \"goodCommentRate\":0.99}";
		ShopInfo shopInfo = JSONObject.parseObject(shopInfoJSON, ShopInfo.class);
		cacheService.saveShopInfo2LocalCache(shopInfo);
		System.out.println("===================获取刚保存到本地缓存的店铺信息:" + cacheService.getShopInfoFromLocalCache(shopId));   
		cacheService.saveShopInfo2ReidsCache(shopInfo);  
	}
}

  

public class ProductInfo {
	private Long id;
	private String name;
	private Double price;
	
	public ProductInfo() {
		
	}
	
	public ProductInfo(Long id, String name, Double price) {
		this.id = id;
		this.name = name;
		this.price = price;
	}
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getPrice() {
		return price;
	}
	public void setPrice(Double price) {
		this.price = price;
	}
}

  

//店铺信息
public class ShopInfo {
	private Long id;
	private String name;
	private Integer level;
	private Double goodCommentRate;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getLevel() {
		return level;
	}

	public void setLevel(Integer level) {
		this.level = level;
	}

	public Double getGoodCommentRate() {
		return goodCommentRate;
	}

	public void setGoodCommentRate(Double goodCommentRate) {
		this.goodCommentRate = goodCommentRate;
	}

	@Override
	public String toString() {
		return "ShopInfo [id=" + id + ", name=" + name + ", level=" + level
				+ ", goodCommentRate=" + goodCommentRate + "]";
	}
}

  

import com.roncoo.eshop.model.ProductInfo;
import com.roncoo.eshop.model.ShopInfo;

//缓存service接口
public interface CacheService {
   //将商品信息保存到本地缓存中
	public ProductInfo saveLocalCache(ProductInfo productInfo);
	
	//从本地缓存中获取商品信息
	public ProductInfo getLocalCache(Long id);
	
	//将商品信息保存到本地的ehcache缓存中
	public ProductInfo saveProductInfo2LocalCache(ProductInfo productInfo);
	
	//从本地ehcache缓存中获取商品信息
	public ProductInfo getProductInfoFromLocalCache(Long productId);
	
	// 将店铺信息保存到本地的ehcache缓存中
	public ShopInfo saveShopInfo2LocalCache(ShopInfo shopInfo);
	
	//从本地ehcache缓存中获取店铺信息
	public ShopInfo getShopInfoFromLocalCache(Long shopId);
	
	//将商品信息保存到redis中
	public void saveProductInfo2ReidsCache(ProductInfo productInfo);
	
	//将店铺信息保存到redis中
	public void saveShopInfo2ReidsCache(ShopInfo shopInfo);
	
}

  

import javax.annotation.Resource;

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.roncoo.eshop.model.ProductInfo;
import com.roncoo.eshop.model.ShopInfo;
import com.roncoo.eshop.service.CacheService;

import redis.clients.jedis.JedisCluster;

//缓存Service实现类
@Service("cacheService")
public class CacheServiceImpl implements CacheService {
	public static final String CACHE_NAME ="local";
	
	@Resource
	private JedisCluster jedisCluster;
	
	//将商品信息保存到本地缓存中
	@CachePut(value = CACHE_NAME, key = "'key_'+#productInfo.getId()")
	public ProductInfo saveLocalCache(ProductInfo productInfo) {
		return productInfo;
	}
	
	 // 从本地缓存中获取商品信息
	@Cacheable(value = CACHE_NAME, key = "'key_'+#id")
	public ProductInfo getLocalCache(Long id) {
		return null;
	}
	
	//将商品信息保存到本地的ehcache缓存中
	@CachePut(value=CACHE_NAME,key = "'product_info_'+#productInfo.getId()")
	public ProductInfo saveProductInfo2LocalCache(ProductInfo productInfo) {
		return productInfo;
	}
	
	//从本地ehcache缓存中获取商品信息
	@Cacheable(value = CACHE_NAME, key = "'product_info_'+#productId")
	public ProductInfo getProductInfoFromLocalCache(Long productId) {
		return null;
	}
	
	//将店铺信息保存到本地的ehcache缓存中
	@CachePut(value = CACHE_NAME, key = "'shop_info_'+#shopInfo.getId()")
	public ShopInfo saveShopInfo2LocalCache(ShopInfo shopInfo) {
		return shopInfo;
	}
	
	//从本地ehcache缓存中获取店铺信息
	@Cacheable(value = CACHE_NAME, key = "'shop_info_'+#shopId")
	public ShopInfo getShopInfoFromLocalCache(Long shopId) {
		return null;
	}
	
	//将商品信息保存到redis中
	public void saveProductInfo2ReidsCache(ProductInfo productInfo){
		String key="product_info_" + productInfo.getId();
		jedisCluster.set(key, JSONObject.toJSONString(productInfo));
	}
	
	//将店铺信息保存到redis中
	public void saveShopInfo2ReidsCache(ShopInfo shopInfo){
		String key="shop_info_" + shopInfo.getId();
		jedisCluster.set(key, JSONObject.toJSONString(shopInfo));  
	}
}

  

import org.springframework.context.ApplicationContext;

/**
 * spring上下文
 */
public class SpringContext {

	private static ApplicationContext applicationContext;

	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public static void setApplicationContext(ApplicationContext applicationContext) {
		SpringContext.applicationContext = applicationContext;
	}
	
}

  

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.roncoo.eshop.model.ProductInfo;
import com.roncoo.eshop.service.CacheService;

@Controller
public class CacheController {
	@Resource
	private CacheService cacheService;
	
	@RequestMapping("/testPutCache")
	@ResponseBody
	public String testPutCache(ProductInfo productInfo) {
		cacheService.saveLocalCache(productInfo);
		return "success";
	}
	
	@RequestMapping("/testGetCache")
	@ResponseBody
	public ProductInfo testGetCache(Long id) {
		return cacheService.getLocalCache(id);
	}
}

  如何提升缓存命中率:

分发层+应用层,双层nginx

分发层nginx,负责流量分发的逻辑和策略,这个里面它可以根据你自己定义的一些规则,比如根据productId去进行hash,然后对后端的nginx数量取模

将某一个商品的访问的请求,就固定路由到一个nginx后端服务器上去,保证说只会从redis中获取一次缓存数据,后面全都是走nginx本地缓存了

后端的nginx服务器,就称之为应用服务器; 最前端的nginx服务器,被称之为分发服务器

大幅度提升你的nginx本地缓存这一层的命中率,大幅度减少redis后端的压力,提升性能

 缓存命中率低的原因:

 

转载于:https://www.cnblogs.com/sunliyuan/p/11366478.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值