题目:

乱序输出N个数,可使用数组实现。

解题思路:

需要考虑两点:

  • 随机取
    想到乱序就想到了随机,随机就可以使用Random,使用Random的取值范围为:[0,arr.length-1],得到的数就是数组的下标。
  • 不重复
    随机取已经通过Random解决,那么如何保证不重复? 每取出一个数后,就把当前Random的取值范围最后一个下标所对应的元素覆盖上去,然后下一轮Random的取值范围-1

- 阅读剩余部分 -

给两个字符串,求两个字符串的最长子串
(例如:“abc”“xyz”的最长子串为空字符串,“abcde”和“bcde”的最长子串为“bcde”)

解题思路:

  1. 把两个字符串分成一个行列的二维矩阵
  2. 比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。
  3. 通过查找出值为1的最长对角线就能找到最长公共子串。

微信图片_20190408175443.png
从图中我们可以看到,等于1的那个对角线就是我们要求的最长公共子串,同时我们还可以再优化一下:
刚才我们说“比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。” ,那么我们并不需要一直等于1,即:两个值相等时(a[i]==b[j]),我们判断对角线前一个值是否相等(a[i-1]==b[j-1]), 如果相等,那么我们只需要加上前一个的值即可。
微信图片_20190408175459.png

我们可以通过一个二维数组实现:

int[][] arr = new int [a.length()][b.length()];
if (a.substring(i,i+1).equals(b.substring(j,j+1))){
  arr[i][j] = 1+ arr[i-1][j-1] ;
}

完整代码为:

public class Lcs {

    public static String lCs(String a,String b){
        int[][] arr = new int [a.length()][b.length()];
        int leng = 0;
        int index = 0;

        for (int i=0;i<a.length();i++){
            for (int j=0;j<b.length();j++){
                if (a.substring(i,i+1).equals(b.substring(j,j+1))){
                    if (i>0 && j>0){
                        arr[i][j] = 1+ arr[i-1][j-1] ;
                    }else{
                        arr[i][j] = 1;
                    }
                }else {
                    arr[i][j] = 0;
                }
                if (arr[i][j]>leng){

                    leng=arr[i][j];
                    index = i ;
                }
            }
        }
        return a.substring(index-leng+1,index+1);
    }


    public static void main(String[] args) {
        System.out.println(Lcs.lCs("abcdefghij","asdabchij"));
    }
}

[post url="https://github.com/CoderXiaohui/LeetCode/blob/master/src/String/Lcs.java" title="GitHub" intro="GitHub" cover="https://github.com/fluidicon.png" /]

参考链接

客户端负载均衡器:Ribbon

Ribbon实现软负载均衡核心:

  1. 服务发现 :依据服务的名字,把该服务下所有的实例都找出来
  2. 服务选择规则:依据规则策略,如果从多个实例中,选出有效的服务
  3. 服务监听:检测失效的服务,做到高效剔除

Ribbon主要组件:

  1. ServerList (获取所有的服务列表)
  2. IRule(根据规则选择出有效的)
  3. ServerListFilter(过滤掉失效的)

流程:

1.首先通过ServerList获取所有的服务列表
2.然后通过ServerListFilter过滤掉一部分地址
3.最后通过IRule选择一个实例,作为最终目标结果

默认负载均衡策略

  • 轮询

如何修改负载均衡策略

  • 去SpringCloud官网搜索Ribbon策略的配置(搜索:Customizing the Ribbon Client),可以找到从配置文件配置的方法

    需要在客户端配置如下:

PRODUCT:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

PRODUCT : 应用名

RandomRule :负载均衡策略为随机 (Ribbon的默认策略默认是RoundRobinRule

所有的负载均衡策略在IRule接口中可以看到:

1554275423045.png

应用间通信

HTTP vs RPC

  • Spring Cloud (HTTP)
  • Dubbo (RPC)

1.SpringCloud中服务间两种restful调用方式

  • RestTemplate
  • Feign

方式一、RestTemplate:是一个http客户端

RestTemplate有三种方式

1.直接写url :http://localhost:8080/msg

使用restTemplate.getForObject("http://localhost:8080/msg", String.class);方法

@RestController
@Slf4j
public class ClientController {
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //1. 第一种方式
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject("http://localhost:8080/msg", String.class);
        log.info("response={}",response);
        return response;
    }
}

缺点:地址是写死的,如果对方服务有多台,这样很不好。

2.通过 ServiceInstance serviceInstance = loadBalancerClient.choose("ServerId"); 获得服务的信息。
ServerId :服务的名字

  @Autowired
  private LoadBalancerClient loadBalancerClient;
@RestController
@Slf4j
public class ClientController {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    
    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //2.第二种
        ServiceInstance serviceInstance= loadBalancerClient.choose("PRODUCT");
        String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort())+"/msg";
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);
        log.info("response={}",response);
        return response;
    }
  1. 先增加@Autowired LoadBalancerClient
  2. 通过loadBalancerClient.choose("ServerId");返回一个ServiceInstance
  3. serviceInstance.getHost()serviceInstance.getPort()可以分别获得主机以及端口号
  4. 拼接主机以及端口
  5. 通过第一种方法的restTemplate.getForObject(url, String.class)请求数据
方法说明返回例子
serviceInstance.getUri()路径http://localhost:8080
serviceInstance.getHost()主机localhost
serviceInstance.getServiceId()服务idPRODUCT
serviceInstance.getPort()端口号8080

优点:我们可以不知道服务的路径,通过服务Id或者服务路径信息
缺点:编码比较繁琐

3.用@LoadBalanced 可在restTemplate里使用应用名字来访问

@Component
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
@RestController
@Slf4j
public class ClientController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        //3.第三种方式 利用@LoadBalanced 可在restTemplate里使用应用名字
        String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
        log.info("response={}",response);
        return response;
    }
}
总结

RestTemplate有三种方式:

  1. url写死,用new RestTemplate () 访问url来获取消息
  2. 使用loadBalancerClient获取到url,然后使用new RestTemplate () 访问url 获取信息
  3. 利用@LoadBalanced注解restTemplate,然后restTemplate就可以使用应用名称来访问(通过ribbon依据某种规则,如简单轮询、随机连接去连接目标服务来实现负载均衡)

方式二、Feign

  • 本质还是http客户端
  • 声明式REST客户端(伪RPC)
  • 才用了基于接口的注解
  • 内部也使用Ribbon做负载均衡

通过feign我们能把http远程调用对开发者完全透明,得到与调用本地方法一样的编码体验

使用方法:
1.在pom中增加Feign依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>2.0.0.M1</version>
        </dependency>
2.在启动类增加注解@EnableFeignClients 开启Feign功能 (org.springframework.cloud.netflix.feign.EnableFeignClients;)
package com.imooc.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
3.在客户端定义好要调用的服务和接口
  • 接口使用@FeignClient(name="需要调用的应用名称")
  • 方法的注解是需要调用接口的地址(也可以使用@RequestMapping、@PostMapping)
    如我们想调用的接口地址为“/order/addOrder”,且为GET请求, 那就写@GetMapping("/order/addOrder")
package com.imooc.order.client;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name="product")
public interface ProductClient {
    @GetMapping("/msg")
    String productMsg();
}
4.客户端依赖注入③定义好的接口,然后直接调用即可。
package com.imooc.order.controller;

import com.imooc.order.client.ProductClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Slf4j
public class ClientController {
    @Autowired
    private ProductClient productClient;

    @GetMapping("/getProductMsg")
    public String getProductMsg(){
        String response = productClient.productMsg();
        log.info("response={}",response);
        return response;
    }
}

1.两个注解:

  • @EnableEurekaServer——在启动类上添加
  • @EnableDiscoveryClient或@EnableEurekaClient——启动类加
因为Eureka支持多种注册中心,

EnableDiscoveryClient注解在使用任何Eureka支持的注册中心时都可以使用,是一个通用的注解.

而EnableEurekaClient注解只有在使用Eureka作为注册中心时才可以使用,是Eureka注册中心的专用注解,在使用其他注册中心的时候不管用.

所以,在使用Eureka作为注册中心的时候,推荐使用EnableEurekaClient注解,在使用其他注册中心的时候,使用

EnableDiscoveryClient注解

1.@EnableEurekaServer——在启动类上添加

提供服务注册的功能,各个服务节点启动后,会在EurekaServer注册,这样EurekaServer就有了所有服务节点的信息

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

2.@EnableDiscoveryClient或@EnableEurekaClient——启动类加

表示这个是一个Eureka客户端

@SpringBootApplication
@EnableDiscoveryClient
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}

2.功能

  • 心跳检测
  • 健康检查
  • 负载均衡

3.Eureka的高可用(生产建议2台以上)

配置原理:将注册中心分别指向其他的注册中心

4.分布式系统中,服务注册中心是最重要的基础部分