java仿写string类,仿写spring cloud负载均衡实现

负载均衡实现要点

这里讨论的负载均衡指的是客户端负载均衡,仿照ribbon实现一个简单的负载均衡例子,采用一定的算法来决定调用服务的哪一个实例。

客户端负载均衡实现主要包括以下几点。

服务实例管理(定时更新)

拦截RestTemplate请求,实现负载均衡接入点

负载均衡算法逻辑

仿写实现

具体pom依赖如下

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

org.springframework.cloud

spring-cloud-starter-zookeeper-all

org.apache.zookeeper

zookeeper

org.apache.zookeeper

zookeeper

3.4.12

org.slf4j

slf4j-log4j12

仿写例子中,利用zookeeper作为注册中心。

已有服务

假如有以下一个服务,定义了一个hello rest接口

@SpringBootApplication

@EnableDiscoveryClient

@RestController

public class DiscoverClient {

@Autowired

private DiscoveryClient discoveryClient;

public static void main(String[] args) {

SpringApplication.run(DiscoverClient.class, args);

}

@RequestMapping("/hello")

public String hello(){

return "hello garine";

}

}

客户端请求代码

@SpringBootApplication(scanBasePackages = "garine.learn.custom.loadblance")

@EnableDiscoveryClient

@EnableScheduling

public class LoadblanceSpringApplication {

public static void main(String[] args) {

SpringApplication.run(LoadblanceSpringApplication.class, args);

}

}

@RestController

public class LoadbalceTestController {

@Autowired

private RestTemplate restTemplate;

@GetMapping("/{applicationName}/hello")

public String hello(@PathVariable String applicationName){

//模拟传入应用名称,请求调用服务端hello方法

return restTemplate.getForObject(applicationName + "/hello", String.class);

}

//......

}

服务实例管理实现

首先客户端要实现负载,必须先获取到所有的服务实例,然后才能决定使用哪一个实例。所以客户端需要维护一个服务实例Map,结构Map

//......

private volatile Map> serviveUrlCache = new HashMap<>();

@Autowired

private DiscoveryClient discoveryClient;

//......

/** * 更新地址缓存 */

@Scheduled(fixedRate = 10000)

public void pullServiceUrl(){

Map> oldServiveUrlCache = this.serviveUrlCache;

Map> newServiveUrlCache = new HashMap<>();

discoveryClient.getServices().forEach(dto -> {

Set ins = discoveryClient.getInstances(dto).stream().map(serviceInstance -> {

return (serviceInstance.isSecure() ? "https://":"http://") + serviceInstance.getHost() + ":" + serviceInstance.getPort();

}).collect(Collectors.toSet());

newServiveUrlCache.put(dto, ins);

});

this.serviveUrlCache = newServiveUrlCache;

}

拦截RestTemplate请求,实现负载均衡接入点

我们都知道spring cloud中服务之间的调用是基于RestTemplate进行扩展实现的,结合Feign,提供一个服务名称,实现ClientHttpRequestInterceptor接口的拦截器拦截RestTemplate请求。拦截器进行负载均衡并且发起服务调用,返回调用结果。

所以扩展负载均衡接入点,需要:

定义实现ClientHttpRequestInterceptor的拦截器类

实例化拦截器并且加入到为RestTemplate的拦截器列表中

1.定义拦截器实现,第一步的服务实例列表管理可以一起放到这里面一起管理。代码如下。

/** * RequestTemplate执行请求时可以应用的自定义拦截器,拦截器逻辑包含了http请求逻辑 */

public class LoadblanceInterceptor implements ClientHttpRequestInterceptor{

private volatile Map> serviveUrlCache = new HashMap<>();

@Autowired

private DiscoveryClient discoveryClient;

/** * 更新地址缓存 */

@Scheduled(fixedRate = 10000)

public void pullServiceUrl(){

Map> oldServiveUrlCache = this.serviveUrlCache;

Map> newServiveUrlCache = new HashMap<>();

discoveryClient.getServices().forEach(dto -> {

Set ins = discoveryClient.getInstances(dto).stream().map(serviceInstance -> {

return (serviceInstance.isSecure() ? "https://":"http://") + serviceInstance.getHost() + ":" + serviceInstance.getPort();

}).collect(Collectors.toSet());

newServiveUrlCache.put(dto, ins);

});

this.serviveUrlCache = newServiveUrlCache;

}

@Override

public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

String requestPath = request.getURI().getPath().substring(1);

//获取传入的应用名称

String applicationName = requestPath.split("/")[0];

Set ins = this.serviveUrlCache.get(applicationName);

List insUrls = new ArrayList<>(ins);//重新建立列表,防止并发过程被修改

//负载均衡逻辑-start

int index = new Random().nextInt(ins.size());

String preTargetUrl = insUrls.get(index);

//负载均衡逻辑-end

String targetUrl = preTargetUrl + requestPath.substring(requestPath.indexOf("/"));

URL url = new URL(targetUrl);

URLConnection urlConnection = url.openConnection();

//make a simple response

ClientHttpResponse response = new ClientHttpResponse() {

@Override

public HttpStatus getStatusCode() throws IOException {

return HttpStatus.OK;

}

@Override

public int getRawStatusCode() throws IOException {

return 200;

}

@Override

public String getStatusText() throws IOException {

return "OK";

}

@Override

public void close() {

}

@Override

public InputStream getBody() throws IOException {

return urlConnection.getInputStream();

}

@Override

public HttpHeaders getHeaders() {

return new HttpHeaders();

}

};

return response;

}

}

2.设置拦截器为RestTemplate的拦截器

在RestTemplate初始化时,就需要把拦截器加入到RestTemplate的拦截器中,这样在进行请求时才会拦截。

利用spring注解进行设置,为了方便,直接在定义客户端请求的类中设置。代码如下。

/** * 指定某一种bean,修饰/或者指定注入 */

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

@Qualifier

public @interface LoadblancedRestTemplate {

}

@RestController

public class LoadbalceTestController {

@Autowired

private RestTemplate restTemplate;

@GetMapping("/{applicationName}/hello")

public String hello(@PathVariable String applicationName){

return restTemplate.getForObject(applicationName + "/hello", String.class);

}

@Bean

@LoadblancedRestTemplate

public RestTemplate restTemplate(){

RestTemplate restTemplate = new RestTemplate();

return restTemplate;

}

@Bean

public Object setAndInitRestTamplates(@LoadblancedRestTemplate Collection restTemplates, LoadblanceInterceptor loadblanceInterceptor){

//@LoadblancedRestTemplate指定只能注入被@LoadblancedRestTemplate修饰的RestTemplate,LoadblanceInterceptor默认依赖注入

restTemplates.forEach(val -> {

//添加到restTemplate的拦截器中

val.getInterceptors().add(loadblanceInterceptor);

});

return new Object();

}

@Bean

public LoadblanceInterceptor loadblanceInterceptor(){

LoadblanceInterceptor l1 = new LoadblanceInterceptor();

return l1;

}

}

负载均衡算法

在LoadblanceInterceptor实现中,已经实现了一种最简单的负载方式,随机负载。负载的最终目的是选出一个服务地址,所以只需要再LoadblanceInterceptor中定制负载算法即可。

执行结果

restTemplate.getForObject方法执行时,如果发现RestTemplate具有拦截器,则执行请求逻辑由拦截器进行处理。也就是garine.learn.custom.loadblance.interceptor.LoadblanceInterceptor#intercept方法。根据传递的应用名称参数->查找出服务实例地址集合->负载出一个实例地址->拼接url->http调用->返回结果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值