Hystrix的配置
Hystrix配置参数实例
具体配置可以看之前的文章,这里只是对项目中使用到的配置进行理解
@Service
//线程垄断配置
//hystrix配置
@DefaultProperties(threadPoolKey = "jedisFuse",
//10个核心线程池
threadPoolProperties = {@HystrixProperty(name = "coreSize", value = "10"),
//最大线程池
@HystrixProperty(name = "maxQueueSize", value = "50"),
//拒绝阈值
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "50")},
//Hystrix服务降级处理的超时时间
commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5"),
//用来设置Hystrix降级并行的线程数量
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "1000")})
public class JedisFuse {
@Autowired
private JedisUtil jedisUtil;
//设置降级方法
@HystrixCommand(fallbackMethod = "getFallback")
public byte[] get(byte[] key){
return jedisUtil.get(key);
}
//回退方法
private byte[] getFallback(byte[] key){
return jedisUtil.get(key);
}
}
MaxConcurrentRequests降级线程并行数限制
这个属性主要是来控制Hystrix降级并行的线程数,如果需要降级的线程并行数超过了这个属性所设置的值,则会直接报HystrixRuntimeException异常,因为Hystrix对于普通RuntimeException异常报错请求都会进行降级处理,但是对于HystrixRuntimeException不会降级,这个异常是Hystrix自己抛出来的,用来提示请求方请求失败。
设置不妥当可能会导致创建大量线程时,部分线程成功生成了;部分线程降级成功;部分线程竟然没有降级
Hystrix设置这个属性并且给与默认值主要还是为了对高并发下请求数量的控制,毕竟如果走了降级处理逻辑也还是会增加服务器的请求线程数,对服务器可能造成压力
@PostConstruct注解
@PostConstruct注解被用来修饰一个非静态的void()方法,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且智慧被服务器执行一次,PostConstruct在构造函数之后执行,init方法之前执行
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
项目中@PostConstruct注解的应用
private static Cache<String,Object> cache=null;
//用来修饰一个非静态的void()方法,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次
//PostConstruc在构造函数之后执行,init方法之前执行
@PostConstruct
//初始化缓存
public void init(){
cache= newCache();
}
//覆盖之前的缓存
public void replaceCache(Cache<String,Object> newCache){
cache = newCache;
}
这个相当于Spring中的BeanPostProcessor这个接口
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
*
* 任何Bean实例化,并且Bean已经populated(填充属性) 就会回调这个方法
*
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
*
* 任何Bean实例化,并且Bean已经populated(填充属性) 就会回调这个方法
*
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
有个实现类CommonAnnotationBeanPostProcessor,就是专门处理@PostConstruct,@PreDestroy注解
CommonAnnotationBeanPostProcessor的父类InitDestroyAnnotationBeanPostProcessor()
InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()
InitDestroyAnnotationBeanPostProcessor.findLifecycleMetadata()
// 组装生命周期元数据
InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata()
// 查找@PostConstruct注释的方法
InitDestroyAnnotationBeanPostProcessor.initAnnotationType
// 查找@PreDestroy注释方法
InitDestroyAnnotationBeanPostProcessor.destroyAnnotationType
// 反射调用
metadata.invokeInitMethods(bean, beanName);
@Async注解的使用
@Async注解会在调用方的当前线程之外的独立线程中执行,相当于自己new Thread(()->System.out.println(“hello world!”))这样在另一个线程中去执行相应的业务逻辑
@Async注解使用条件
- @Async注解一般用在类的方法上,如果用在类上,那么这个类的所有方法都是异步执行的
- 所使用的@Async注解方法的类对象应该是Spring容器管理的bean对象
- 调用异步方法类上需要配置上注解@EnableAsync
底层是一个线程池,修饰的方法可以异步调用
一般都会加一个线程池配置
项目中的async线程池配置
package com.xm4399.recallserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class AsyncConfiguration {
/**
* 线程池配置
* @param threadPoolProperties
* @return
*/
//异步执行的线程池配置
@Bean
public ExecutorService threadPool(ThreadPoolProperties threadPoolProperties){
return new ThreadPoolExecutor(
threadPoolProperties.getCorePoolSize(),threadPoolProperties.getMaximumPoolSize(),threadPoolProperties.getKeepAliveTime(), TimeUnit.SECONDS
,threadPoolProperties.getWorkQueue(),threadPoolProperties.getThreadFactory(),threadPoolProperties.getHandler());
}
}
package com.xm4399.recallserver.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.concurrent.*;
@ConfigurationProperties(prefix = "myserver.threadpool")
@Data
@Component
//线程池配置参数
public class ThreadPoolProperties {
/**
* 核心线程数
*/
private int corePoolSize = 6;
/**
* 最大线程数
*/
private int maximumPoolSize = 12;
/**
* 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
*/
private long keepAliveTime = 10L;
/**
* 时间单位,默认秒
*/
private TimeUnit timeUnit = TimeUnit.SECONDS;
/**
* 等待队列,默认使用{@link LinkedBlockingQueue}
*/
private BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
/**
* 线程工厂
*/
private ThreadFactory threadFactory = Executors.defaultThreadFactory();
/**
* 拒绝策略
*/
private RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
}
如果修饰的方法带有返回值则需要用到AsyncResult
例子
@Service
public class DeviceProcessServiceImpl implements DeviceProcessService {
@Autowired
private DeviceRpcService deviceRpcService;
@Async("taskExecutor")
@Override
public Future<Map<Long, List<ProcessDTO>>> queryDeviceProcessAbilities(List<BindDeviceDO> bindDevices) {
if (CollectionUtils.isEmpty(bindDevices)) {
return new AsyncResult<>(Maps.newHashMap());
}
List<Long> deviceIds = bindDevices.stream().map(BindDeviceDO::getDeviceId).collect(Collectors.toList());
List<DeviceInstanceWithProcessResp> devices = deviceRpcService.getDeviceProcessAbility(deviceIds);
Map<Long, List<ProcessDTO>> deviceAbilityMap = Maps.newHashMap();
...
return new AsyncResult<>(deviceAbilityMap);
}
}
使用Cache进行缓存
Java中的Cache缓存
Java Caching 定义了5个接口,分别是CachingProvider,CacheManager,Cache,Entry和Expiry
- CachingProvider:定义了创建,配置,获取,管理和控制多个CacheManager,一个应用可以在运行期间访问多个CachingProvider
- CacheManager:定义了创建,配置,获取,管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CachingProvider所拥有
- Cache:是一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有
- Entry:是一个存储在Cache中的key-value对
- Expiry:过期时间
缓存的整体结构
SpringBoot的缓存抽象
几个重要的概念以及缓存注解
开启缓存的步骤
作为Spring框架的核心功能之缓存注解,该功能也继承了Spring这个优良特性,使它生效只需要轻松两步:
1.配置类上开启缓存注解支持:@EnableCaching
2.向容器内至少放置一个CacheManager类型的Bean
项目中Cache的使用
public class RecallCache {
private static Cache<String,Object> cache=null;
//用来修饰一个非静态的void()方法,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次
//PostConstruc在构造函数之后执行,init方法之前执行
@PostConstruct
//初始化缓存
public void init(){
cache= newCache();
}
//覆盖之前的缓存
public void replaceCache(Cache<String,Object> newCache){
cache = newCache;
}
/**
* 创建新cache
* @return
*/
public Cache<String,Object> newCache(){
return CacheBuilder.newBuilder()
//缓存初始容量1000
.initialCapacity(1000)
//最多多少个key,超过按LRU策略移除
.maximumSize(300000)
//写入后3小时过期
.expireAfterWrite(4, TimeUnit.HOURS)
.build();
}
//设置缓存的key和value
public void set(String key, Object value) {
cache.put(key,value);
}
//获取缓存的key
public Object get(String key) {
return cache.getIfPresent(key);
}
/**
* 清空缓存
*/
public void invalidateAll(){
cache.invalidateAll();
}
/**
* 清空过期的缓存
*/
public void cleanUp(){
cache.cleanUp();
}
/**
* 获取缓存数量
* @return
*/
public long size(){
return cache.size();
}
}
参考:https://blog.csdn.net/weixin_44701606/article/details/107291808
埋点的作用
不是很理解,后期跟进
简单的通过百度了解埋点的作用
数据埋点是数据产品经理、数据运营以及数据分析师,基于业务需求(例如:CPC点击付费广告中统计每一个广告位的点击次数),产品需求(例如:推荐系统中推荐商品的曝光次数以及点击的人数)对用户行为的每一个事件对应的位置进行开发埋点,并通过SDK上报埋点的数据结果,记录数据汇总后进行分析,推动产品优化或指导运营。
埋点分析,是网站分析的一种常用的数据采集方法。数据埋点分为初级、中级、高级三种方式。
初级的数据埋点:在产品流程关键部位植相关统计代码,用来追踪每次用户的行为,统计关键流程的使用程度。
中级的数据埋点:在产品中植入多段代码追踪用户连续行为,建立用户模型来具体化用户在使用产品中的操作行为。
高级的数据埋点:与研发及数据分析师团队合作,通过数据埋点还原出用户画像及用户行为,建立数据分析后台,通过数据分析、优化产品。
项目中埋点上报的代码
public class PushKeyService {
@Value("${spring.application.name}")
//应用名称
private String applicationName;
@Value("${spring.application.env}")
//环境名称
private String env;
//kafka主题名称
public final static String KAFKA_TOPIC_UCMETRICS = "ucmetrics_event_origin";
@Autowired
//kafka消息处理
private KafkaTemplate<String, Object> metricsKafkaTemplate;
@Autowired
//召回配置信息
private RecallSettingService recallSettingService;
@Autowired
//jedis工具
private JedisUtil jedisUtil;
//异步执行
@Async
public void pushRecallKey(Integer recallId, String key, boolean isHitCache, boolean isCacheNull) {
//判断是否为实时召回,若是则不上报热点key
if (isRealTimeRecall(recallId)) {
return;
}
//继承了gprp_util中的data,填充data数据
RequestPushKeyInfo pushKeyInfo = buildData(key, isHitCache, isCacheNull);
//kafka发送消息,使用gprp_util工具定义的DataList存储
metricsKafkaTemplate.send(KAFKA_TOPIC_UCMETRICS, key, new DataList().setDatas(pushKeyInfo).java2Json2GZIP2BASE2Url());
}
//填充数据
private RequestPushKeyInfo buildData(String key,boolean isHitCache, boolean isCacheNull){
RequestPushKeyInfo pushKeyInfo = new RequestPushKeyInfo();
pushKeyInfo.setClient_timestamp(System.currentTimeMillis());
pushKeyInfo.setAppinfo("application", applicationName);
pushKeyInfo.setEnv("env", env);
pushKeyInfo.setProperties("recall_key", key);
pushKeyInfo.setProperties("is_hit_cache", isHitCache);
pushKeyInfo.setProperties("is_cache_null", isCacheNull);
return pushKeyInfo;
}
/**
* 判断是否实时召回
* @param recallId
* @return
*/
private boolean isRealTimeRecall(Integer recallId){
Integer recallType = null;
//从redis中获取指定key值
Object obj = jedisUtil.get(CacheConstant.CACHE_RECALL_TYPE_PRE+recallId);
//如果key不为空,将数据转换为整型数据
if (Objects.nonNull(obj)) {
recallType = Integer.parseInt(obj.toString());
}
//如果为空,则调用召回配置信息的方法,根据召回id获取召回类型
if (Objects.isNull(recallType)) {
recallType = recallSettingService.getRecallType(recallId);
if (Objects.nonNull(recallType)) {
//将召回类型加入到redis缓存当中,并设置过期时间
jedisUtil.set(CacheConstant.CACHE_RECALL_TYPE_PRE + recallId, String.valueOf(recallType), CacheConstant.EXPIRE_TIME_ONE_DAY);
}
}
//判断是否有数据且实时召回
if (Objects.nonNull(recallType) && recallType == RecallTypeEnum.REALTIME.getValue()) {
return true;
}
return false;
}
}
小知识点
List.subList
List list = new Arraylist<>();
List subList = list.subList(0, 5);
其中subList(0, 5)取得的是下标为0到4的元素,不包含下标为5的元素.
Collections.sort
Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,有两种形式
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
项目中使用的Collecitons.sort
/**
* 排序
* @param list
*/
//向下排序
public static void sortDataDesc(List<RecallElement> list){
Collections.sort(list, new Comparator<RecallElement>() {
@Override
public int compare(RecallElement o1, RecallElement o2) {
float delta = o1.getSumScore() - o2.getSumScore();
//小于0返回正序,大于0逆序
if(delta < 0f){
return 1;
}else if(delta > 0f){
return -1;
}
return 0;
}
});
}
//向上排序
public static void sortDataAsc(List<RecallElement> list){
Collections.sort(list, new Comparator<RecallElement>() {
@Override
public int compare(RecallElement o1, RecallElement o2) {
float delta = o1.getSumScore() - o2.getSumScore();
//大于0正序,小于0逆序
if(delta > 0f){
return 1;
}else if(delta < 0f){
return -1;
}
return 0;
}
});