Base-admin笔记

令牌桶

令牌桶限流,在SpringCloud分布式下限流,需要把令牌桶放到一个公共的地方,如Zuul路由,guava里有现成的基于令牌桶的限流实现。

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 当桶满时,新添加的令牌被丢弃或拒绝。

令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。令牌桶算法基本可以用下面的几个概念来描述:

令牌将按照固定的速率被放入令牌桶中。比如每秒放10个。
桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝。
当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上。
如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

令牌算法是根据放令牌的速率去控制输出的速率,也就是上图的to network的速率。to network我们可以理解为消息的处理程序,执行某段业务或者调用某个RPC。

​通俗的理解,令牌桶是一个水桶,而令牌是通过一根水管流到水桶中的水

​令牌桶的填满时间,是由桶的自身容量、令牌漏出速率(桶下面的水管)、超过平均速率的突发流量持续的时间三个方面共同决定的。如果突发流量的时间比较短,令牌桶不会溢出,在通信流上不会受到影响,如果突发流量比较大,时间比较长,那令牌桶就会溢出,多余的通信流就会被限制。

令牌桶算法和漏桶算法的区别:

  主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输速率外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。

/*
 * RateLimiter
 */
@Slf4j
@Dependson("asyncTaskExecutor")
@Component
public class RateLimiter{
    private Integer limit=10;
    private Integer speed=3;
    private static volatile Integer tokens=0;
    public RateLimiter(){
        RateLimiter.tokens=this.limit;
    }
    @Async("asyncTaskExecutor")
    public void asyncTask(){
        log.info("RateLimiter Starting...");
        while(true){
            try{
                Thread.sleep(1000L);
                int newTokens=tokens+speed;
                if(newTokens>limit){
                    tokens=limit;
                }else{
                    tokens=newTokens;
                }
            }catch(Exception e){
                log.error(ErrorUtil.errorInfoToString(e));
            }
        }
    }
    public synchronized boolean execute(){
        if(tokens>0){
            tokens=tokens-1;
            return true;
        }
        return false;
    }
}

/*
 * ErrorUtil Class
 */
public class ErrorUtil {

    /**
     * Exception出错的栈信息转成字符串
     * 用于打印到日志中
     */
    public static String errorInfoToString(Throwable e) {
        //try-with-resource语法糖 处理机制
        try(StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw)){
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            return sw.toString();
        }catch (Exception ignored){
            throw new RuntimeException(ignored.getMessage(),ignored);
        }
    }
}

获取本机IP地址

    InetAddress.getLocalHost().getHostAddress()

@Configuration

注明这个类为配置类,将各个@Bean组件交给Spring管理

@Value

    @Value("${server.servlet.context-path:}")
    private String contextPath;

    @Value("${server.port}")
    private String port;
/*
 * application.yml
 */
server:
  port: 8888
  servlet:
    #应用路径,配置应用路径,可方便进行反向代理
    context-path:
#    context-path: /baseadmin

Optional

从数据库拉取的对象可以为Null

    OptionalE.isPresent()

Result 类

/*
 *这段代码定义了一个泛型类Result<T>,用于封装通信结果。该类包含以下成员变量: 
- data:通信数据 
- flag:通信状态,默认为true 
- msg:通信描述,默认为"操作成功" 
 
代码中还定义了一些静态方法,用于创建Result对象的实例。这些方法分别接受不同的参数,包括data、flag和msg,用于设置Result对象的成员变量。 
 
代码中还包含一个被弃用的构造方法,没有任何参数。 
 
总体来说,这段代码定义了一个通用的通信结果类Result,可以根据需要设置通信数据、通信状态和通信描述。
 * 
 */
@Data
public class Result<T> implements Serializable {
    /**
     * 通信数据
     */
    private T data;
    /**
     * 通信状态
     */
    private boolean flag = true;
    /**
     * 通信描述
     */
    private String msg = "操作成功";

    /**
     * 通过静态方法获取实例
     */
    public static <T> Result<T> of(T data) {
        return new Result<>(data);
    }

    public static <T> Result<T> of(T data, boolean flag) {
        return new Result<>(data, flag);
    }

    public static <T> Result<T> of(T data, boolean flag, String msg) {
        return new Result<>(data, flag, msg);
    }

    @Deprecated
    public Result() {

    }

    private Result(T data) {
        this.data = data;
    }

    private Result(T data, boolean flag) {
        this.data = data;
        this.flag = flag;
    }

    private Result(T data, boolean flag, String msg) {
        this.data = data;
        this.flag = flag;
        this.msg = msg;
    }

}

CopyUtil

实体转换工具类,对象复制

/*
 *
该方法用于将一个源对象的属性值复制到目标对象中,返回复制后的目标对象。
名称:<T> T copy(Object src, Class<T> targetType,Integer count)
参数:
    源对象Object src
    目标对象Class<T> targetType
    整数Integer count

方法首先将count减1。

然后创建一个空的目标对象target,并通过targetType的newInstance方法实例化该对象。

接着使用BeanWrapperImpl类创建一个BeanWrapper代理器targetBean,用于对目标对象的属性进行填充。
(BeanWrapperImpl类在内部使用Spring的BeanUtils工具类对目标对象进行反射操作,设置属性。)
 
接下来,使用BeanMap类获取源对象src的属性和属性值,并将其转换为Map的key-value形式。

然后遍历源对象的属性,对每个属性进行处理。
for(Object key:srcBean.keySet()){}
 
对于每个属性,获取
源对象属性名称srcPropertyName=key+""
属性值srcPropertyVal=srcBean.get(key)
属性类型srcPropertyType=srcBean.getType(srcPropertyName)。
然后获取目标对象属性类型
targetPropertyType=targetBean.getPropertyType(srcPropertyName)。 

接着进行判断,如果源对象属性名称为"class"或目标对象属性类型为空,则跳过该属性,继续处理下一个属性。 

如果源对象属性类型和目标对象属性类型相等,则直接将属性值复制到目标对象中。
targetBean.setPropertyValue(srcPropertyName, srcPropertyVal);。 

如果源对象属性类型和目标对象属性类型不相等,则进行递归调用。

如果count小于等于-1,则满足跳出递归的条件,直接返回目标对象。 

如果源对象的复杂属性为null,则跳过该属性,不进行复制。 

通过targetBean的setPropertyValue方法将目标对象的属性值设置为复制后的值。
targetBean.setPropertyValue(srcPropertyName, CopyUtil.copy(srcPropertyVal, targetPropertyType, count));

如果在复制过程中出现异常,则将异常信息输出到日志文件中,并返回null。
log.error(ErrorUtil.errorInfoToString(e));
 */
public static <T> T copy(Object src, Class<T> targetType,Integer count) {
        //执行一层,自减1
        count--;

        T target = null;
        try {
            //创建一个空目标对象,并获取一个BeanWrapper代理器,用于属性填充,BeanWrapperImpl在内部使用Spring的BeanUtils工具类对Bean进行反射操作,设置属性。
            target = targetType.newInstance();
            BeanWrapper targetBean = new BeanWrapperImpl(target);

            //获取源对象的BeanMap,属性和属性值直接转换为Map的key-value 形式
            BeanMap srcBean = new BeanMap(src);
            for (Object key : srcBean.keySet()) {
                //源对象属性名称
                String srcPropertyName = key + "";
                //源对象属性值
                Object srcPropertyVal = srcBean.get(key);
                //源对象属性类型
                Class srcPropertyType = srcBean.getType(srcPropertyName);
                //目标对象属性类型
                Class targetPropertyType = targetBean.getPropertyType(srcPropertyName);

                //源对象属性值非空判断、目标对象属性类型非空判断,如果为空跳出,继续操作下一个属性
                if ("class".equals(srcPropertyName) || targetPropertyType == null) {
                    continue;
                }

                //类型相等,可直接设置值,比如:String与String 或者 User与User
                if (srcPropertyType == targetPropertyType) {
                    targetBean.setPropertyValue(srcPropertyName, srcPropertyVal);
                }
                //类型不相等,比如:User与UserVo
                else {
                    //满足条件,跳出递归
                    if(count <= -1){
                        return target;
                    }

                    //如果源复杂对象为null,直接跳过,不需要复制
                    if(srcPropertyVal == null){
                        continue;
                    }

                    //设置目标对象属性值
                    targetBean.setPropertyValue(srcPropertyName, CopyUtil.copy(srcPropertyVal, targetPropertyType, count));
                }
            }
        } catch (Exception e) {
            //输出到日志文件中
            log.error(ErrorUtil.errorInfoToString(e));
        }
        return target;
    }
/*
这段代码实现了一个通用的方法用于将一个列表复制到另一个列表中,并且可以将复制的元素类型转换为指定的目标类型。

1. 首先定义了一个公共的静态方法copyList,该方法接受两个参数:srcList表示源列表,targetType表示目标类型。
2. 在方法内部,创建了一个新的ArrayList对象作为目标列表,命名为newList。
3. 使用for循环遍历源列表srcList中的每个元素,循环变量src表示当前遍历到的元素。
4. 在循环中,调用CopyUtil.copy方法将当前元素src复制到目标类型targetType,并将复制后的元素添加到目标列表newList中。
5. 循环结束后,返回目标列表newList。
 * 
 */
    public static <T> List<T> copyList(List srcList, Class<T> targetType) {
        List<T> newList = new ArrayList<>();
        for (Object src : srcList) {
            newList.add(CopyUtil.copy(src, targetType));
        }
        return newList;
    }
/*
 *
这段代码是一个泛型方法,它接受一个Object数组和一个目标类型作为参数,并返回一个目标类型的对象。

代码首先创建一个目标类型的对象targetVo,并通过targetType.newInstance()方法实例化它。

然后,通过targetType.getDeclaredFields()方法获取目标类型的所有字段,并将其存储在一个Field数组中。

接下来,代码使用一个循环来遍历Object数组和Field数组。在每次循环中,它取出Object数组中的元素作为字段的值,然后判断该值的类型是否是Character或BigDecimal。如果是,就将其转换为字符串。

接下来,代码调用field.setAccessible(true)方法来获取对字段的访问权限,并使用field.set(targetVo, fieldVal)方法将字段的值设置为目标对象的相应字段的值。

最后,代码通过try-catch块捕获可能抛出的InstantiationException和IllegalAccessException异常,并调用ErrorUtil.errorInfoToString(e)方法将异常信息转换为字符串。

最后,代码返回目标对象targetVo。
 */
    public static <T> T copyByObject(Object[] src, Class<T> targetType){
        T targetVo = null;
        try {
            //遍历Object[]转换为Field[]
            targetVo  = targetType.newInstance();
            Field[] fields = targetType.getDeclaredFields();
            int length = src.length < fields.length ? src.length : fields.length;
            for (int i = 0; i < length; i++) {
                Field field = fields[i];
                Object fieldVal = src[i];
                if (fieldVal instanceof Character || fieldVal instanceof BigDecimal) {
                    fieldVal = String.valueOf(fieldVal);
                }

                field.setAccessible(true);//获取授权
                field.set(targetVo, fieldVal);//赋值
            }
        } catch (InstantiationException | IllegalAccessException e) {
            ErrorUtil.errorInfoToString(e);
        }
        return targetVo;
    }
Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
/*
这段代码的功能是获取当前类的泛型参数类型。

代码解释如下:

1.  `this.getClass()` :获取当前类的Class对象。
2.  `getGenericSuperclass()` :获取当前类的直接超类的Type对象,即获取当前类的父类的泛型类型。
3.  `(ParameterizedType) this.getClass().getGenericSuperclass()` :将获取到的父类的Type对象强制转换为ParameterizedType类型,以便获取泛型参数类型。
4.  `getActualTypeArguments()` :获取ParameterizedType对象中的实际类型参数,即获取泛型参数类型的数组。
5.  `Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();` :将获取到的泛型参数类型的数组赋值给类型为Type[]的变量types。
**/

本文作者:酒宴花窗

本文链接:https://www.cnblogs.com/garbslife/p/17704016.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   酒宴花窗  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起