小结1

1、select 0和select 1区别

select 0,返回结果为0
select 1,返回结果为1
select 0 from tableA,返回与tableA同等记录数的0
select 1 from tableA,返回与tableA同等记录数的1

2、springcloud负载均衡与熔断器

负载均衡分类:

1、服务端负载均衡:客户端请求到负载均衡服务器,负载均衡服务器根据自身的算法将该请求转给某台真正提供业务的服务器,该服务器将响应数据给负载均衡服务器,负载均衡服务器最后将数据返回给客服端。(nginx)

2、客服端负载均衡:基于客户端的负载均衡,简单的说就是在客户端程序里面,自己设定一个调度算法,在向服务器发起请求的时候,先执行调度算法计算出向哪台服务器发起请求,然后再发起请求给服务器。

基于客户端负载均衡的特点:

由客户端内部程序实现,不需要额外的负载均衡器软硬件投入。

程序内部需要解决业务服务器不可用的问题,服务器故障对应用程序的透明度小。

程序内部需要解决业务服务器压力过载的问题。
springcloud内部使用客户端负载均衡,有Ribbon和Feign两种方式。

Ribbon的负载均衡策略
1、RoundRobinRule(轮询模式),roundRobin方式轮询选择server 轮询index,选择index对应位置的server,是Ribbon的默认策略。
SpringCloudRibbonApplication.java

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class SpringCloudRibbonApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringCloudRibbonApplication.class, args);
  }
  @Autowired
  private LoadBalancerClient loadBalancer;
  @RequestMapping(value="static")
  public String staticRibbon(){
     ServiceInstance instance = loadBalancer.choose("stores");
     URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
     System.out.println(storesUri);
    return "static";
  }
}

连续请求6次执行结果:

http://www.baidu.com:80
http://www.jalja.org:80
http://www.163.org:80
http://www.baidu.com:80
http://www.jalja.org:80
http://www.163.org:80

2、RandomRule(随机策略), 随机选择一个server 在index上随机,选择index对应位置的server。
3、BestAvailableRule(并发量),选择一个最小的并发请求的server 逐个考察Server,如果Server被tripped(down)了,则忽略,在选择其中ActiveRequestsCount最小的server
4、AvailabilityFilteringRule(服务器状态)

public class AvailabilityFilteringRule extends PredicateBasedRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态

5、WeightedResponseTimeRule(根据响应时间)
public class WeightedResponseTimeRule extends RoundRobinRule 根据响应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择server。

6、RetryRule(根据策略+重试)
public class RetryRule extends AbstractLoadBalancerRule 对选定的负载均衡策略机上重试机制。
在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server

7、ZoneAvoidanceRule(Zone状态+服务状态)
public class ZoneAvoidanceRule extends PredicateBasedRule
复合判断server所在区域的性能和server的可用性选择server
使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。

Feign负载均衡

从实践上看,采用feign的方式更优雅(feign内部也使用了ribbon做负载均衡,使用hystrix做熔断)

在Spring Cloud Netflix中,使用Ribbon实现客户端负载均衡——有点类似远程调用,使用Feign实现声明式HTTP客户端调用——即写得像本地函数调用一样。

3、kafka

这里写图片描述
这张图比较清晰地描述了“分区”的概念,对于某一个topic的消息来说,我们可以把这组消息发送给若干个分区,就相当于一组消息分发一样。

分区、Offset、消费线程、group.id的关系

1)一组(类)消息通常由某个topic来归类,我们可以把这组消息“分发”给若干个分区(partition),每个分区的消息各不相同;

2)每个分区都维护着他自己的偏移量(Offset),记录着该分区的消息此时被消费的位置;

3)一个消费线程可以对应若干个分区,但一个分区只能被具体某一个消费线程消费;

4)group.id用于标记某一个消费组,每一个消费组都会被记录他在某一个分区的Offset,即不同consumer group针对同一个分区,都有“各自”的偏移量

4、分布式限流算法

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流
常用限流算法和实现参考:
https://www.cnblogs.com/haoxinyue/p/6792309.html

5、常见rpc框架

thrift Facebook
grpc google
Hessian
Dubbo

6、MyBatis动态sql之${}和#{}区别

先直接说用法:

一般参数传递用#{},在sql相当于占位符,sql执行时参数带引号(单引或双引)

在进行group by 或order by 的时候,使用${},sql执行的时候不带引号

原则:能用#{}的地方不要用${},避免sql注入,安全

mybatis 在对 sql 语句进行预编译之前,会对 sql 进行动态解析,解析为一个BoundSql 对象,也是在此处对动态 SQL 进行处理的,使得#{}和${}在此处产生了差异。
#{}解析为一个JDBC中prepared statement的参数标记符
即:

select * from order where order_no = #{orderNo}

被解析为:

select * from order where order_no = ?

也即是,#{}被解析为一个参数占位符?
${}则仅仅是一个纯粹的字符串替换
即:

select * from order where order_no = '${orderNo}' 

被解析为:

select * from order where order_no = '2017111695468435844135' 

也就是说,使用${}在预编译之前已经不包含变量name了,而#{}的变量替换是在DBMS中。

安全问题

能使用#{}的地方就别用${}.

原因:
1.使用#{},相同的预编译sql可以重复利用,性能比${}高。

2.${}在预编译之前已经被变量替换了,会存在sql注入问题。

sql预编译
定义:
​ sql 预编译指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。

为什么需要预编译

JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译

1、预编译阶段可以优化 sql 的执行。
预编译之后的 sql 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的sql,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。
2、预编译语句对象可以重复利用。
把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。

7.可重入锁

可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁
下面是使用实例

public class Test implements Runnable{


  public synchronized void get(){

      System.out.println(Thread.currentThread().getId());

      set();

  }



  public synchronized void set(){

      System.out.println(Thread.currentThread().getId());

  }



  @Override

  public void run() {

      get();

  }

  public static void main(String[] args) {

      Test ss=new Test();

      new Thread(ss).start();

      new Thread(ss).start();

      new Thread(ss).start();

  }



public class Test implements Runnable {

  ReentrantLock lock = new ReentrantLock();



  public void get() {

      lock.lock();

      System.out.println(Thread.currentThread().getId());

      set();

      lock.unlock();

  }



  public void set() {

      lock.lock();

      System.out.println(Thread.currentThread().getId());

      lock.unlock();

  }



  @Override

  public void run() {

      get();

  }



  public static void main(String[] args) {

      Test ss = new Test();

      new Thread(ss).start();

      new Thread(ss).start();

      new Thread(ss).start();

  }

}

两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。

结果如下:

Threadid: 8
Threadid: 8
Threadid: 10
Threadid: 10
Threadid: 9
Threadid: 9

可重入锁最大的作用是避免死锁

8.快速排序

/**************************************************************      
* Copyright (c) 2016, 
* All rights reserved.                   
* 版 本 号:v1.0                   
* 题目描述:快速排序算法,从键盘输入一组整数数组,用快速排序输出排序后的结果
* 输入描述:请输入要排序的数组: 
*           48 15 24 59 64 79 97 40
* 程序输出: 48 15 24 59 64 79 97 40
*           输入的数组是:
*           48 15 24 59 64 79 97 40 
*           排序后的数组是:
*           15 24 40 48 59 64 79 97 
*           第二种写法的输出:
*           排序后的数组是:
*           15 24 40 48 59 64 79 97 
* 问题分析:1.从键盘输入一个整数字符串,将字符串转化为整数数组
*           解决方法:先将字符串用str.split()转化为字符串数组,再将字符串数组转化为整数数组
* 算法描述:实现快速排序算法的关键在于现在数组中选择一个数字,接下来把数组中的数字分为两部分,比选择的数字小的数字移到数组左边,
*           比选择的数字大的数字移到数组的右边。
*           1.  设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个元素)作为基准点,然后将所有比它小的数都放到它前面,
*           所有比它大的数都放到它后面,这个过程称为一趟快速排序,然后采用分治策略,分别以同样的方式排序前面和后面的数据。
*           2.一趟快速排序的算法是: 
*           1)设置两个变量i、j,排序开始的时候:i=0,j=N-1; 
            2)以第一个数组元素作为基准点。 
            3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于A[i](此时基准点)的值A[j],将值与A[j]交换; 
            4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于A[j](此时基准点)的A[i],将A[j]与A[i]交换; 
            5)循环执行第3、4步,直到i=j; 
            6)到此找到基准点的下标,作为分治下标。 
            7)重复1-6步骤递归排序前半部分 
            8 )重复1-6步骤递归排序后半部分 
* 完成日期:2016-07-24
***************************************************************/  


package org.marsguo.offerproject;

import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
class Sort_Array{
    //int[] s;
    //int i,j,key;
    /*构造函数,用于输出用户输入的数组*/
    public Sort_Array(int[] s){                 
        System.out.println("输入的数组是:");
        for(int i = 0; i <s.length; i++){
            System.out.print(s[i] + " ");
        }
        System.out.println();
    }
    /*排序函数,参数为要排序的数组、数组的起始,数组的末尾*/
    public void sortfun(int[] arrays,int start, int end){

        if(start>=end){                             //判断数组的起始和终止是否相同,相同表示已经都全部排完,返回
            return;
        }
        int i = start;                              //i指向数组的起始位
        int j = end;                                //j指向数组的末位
        int key = arrays[i];                        //选取数组的第一位为关键字key,基准元素
        boolean flag = true;                        //设置标志位,用于判断是i++还是j--;这个很重要
        //int temp;
        while(i != j){                              //如果i≠j,表示还没有比较完,即即关键字左右两侧还不是最小与最大
            if(flag){                   
                if(key>arrays[j]){                  //从后向前遍历,找到小于key的值,
                    swap(arrays,i,j);               //找到小于key的值后将arrays[i]与此值交换
                    flag = false;
                }else{                              //如果没有找到的话j--,向前遍历
                    j--;
                }
            }else{                              
                if(key<arrays[i]){                  //从前向后遍历,找到大于key的值
                    swap(arrays,i,j);               //将此值与arrays[j]进行交换
                    flag = true;
                }else{                              //如果没有找到话就将i++,向后遍历
                    i++;
                }
            }
        }
        sprint(arrays);                             //打印每次排序后的数组
        sortfun(arrays,start,j-1);                  //递归调用,将基准元素的前半段数组再用此方法进行排序,直到所有都排完为止。
        sortfun(arrays,i+1,end);                    //递归调用,将基准元素的后半段数组再用此方法进行排序,直到所有都排完为止。
//      sortfun(s[0],s[j],s[0]);
//      sortfun(s[j+1],s[s.length-1],s[j+1]);
    }
    /*交换函数,用于交换数组中的两个值,easy,*/
    public void swap(int[] array,int i,int j){          
        int temp;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    /*sprint()函数用于打印每次排序后的结果,非必须,但可以显示每次排序情况*/
    public void sprint(int[] arrays){           
        System.out.println("排序后的数组是:");
        for(int i = 0; i <arrays.length;i++){
            System.out.print(arrays[i] + " ");
        }
        System.out.println(); 
    }

    /*第二种写法:*/
    private int getMiddle(int[] sortArray,int low,int high){
        int key = sortArray[low];
        while(low<high){
            while(low <high && sortArray[high] >= key){
                high--;
            }
            sortArray[low] = sortArray[high];
            while(low < high && sortArray[low] <= key){
                low++;
            }
            sortArray[high] = sortArray[low];
        }
        sortArray[low] = key;
        return low;
    }
    public void quicksort(int[] sortArray, int low,int high){
        if(low<high){
            int middle = getMiddle(sortArray, low, high);
            quicksort(sortArray, low, middle-1);
            quicksort(sortArray, middle+1, high);
        }
        System.out.println("第二种写法的输出:");
        sprint(sortArray);
    }
}
public class Quick_sort {
    public static void main(String args[]){
    Scanner sc = new Scanner(System.in);            //从键盘输入数组
        System.out.println("请输入要排序的数组:");
        String str = sc.nextLine();                     //将键盘输入转化为字符串
        String[] temp = str.split(" ");                 //将字符串用“ ”分开转化为字符串数组
        int[] s = new int[temp.length];                 //定义一个整型数组s
        for(int i = 0; i<temp.length; i++){             //将字符串数组强制转化为整型数组
            s[i] = Integer.parseInt(temp[i]);           //这种方法非常巧妙
        }
        Sort_Array sort_array = new Sort_Array(s);      //创建对象,并传入数组s
        sort_array.sortfun(s, 0, s.length-1);           //调用类的方法,用于排序数组

        Sort_Array quickSort = new Sort_Array(s);
        quickSort.quicksort(s, 0, s.length-1);

        /*用集合的方法输入不定长数组:
        Scanner scanner = new Scanner(System.in);           //创建输入扫描器
        System.out.println("请输入要排序的数组:");
        List  list = new LinkedList();
        while(scanner.hasNext()){                       //循环,当扫描到有下一个元素的时候
            int elements = scanner.nextInt();               //获得下一个元素并作为整数
            if(elements == 0){                              //如果等于0,则输入结束
                break;                                  //退出while循环
            }else{
                list.add(elements);                     //否则放入集合中
            }
        }
        Integer[] intArray =  list.toArray(new Integer[0]);
        for(int i = 0; i<intArray.length;i++){
            System.out.println(intArray[i]);
        }
    */
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值