微服务调⽤

RestTemplate简介

RestTemplate是Spring Resources中⼀个访问第三⽅RESTful API接⼝的⽹络请求框架。
RestTemplate的设计原则和其他的Spring Template(例如JdbcTemplate)类似,都是为了执⾏复杂
任务提供了⼀个具有默认⾏为的简单⽅法。
RestTemplate是⽤来消费REST服务的,所以RestTemplate的主要⽅法都与REST的HTTP协议的
⼀些⽅法紧密相连,例如HEAD、GET、POST、PUT、DELETE、OPTIONS等⽅法,这些⽅法在
RestTemplate类对应的⽅法为headForHeaders(),getForObject()、postForObject()、put()、delete()
等。
举例说明,在订单服务通过RestTemplate的getForObject⽅法调⽤⽀付服务,并且将调⽤结果反
序列化成Payment对象,代码如下。

@GetMapping("/payment/{id}")
public ResponseEntity<Payment> getPaymentById(@PathVariable("id") Integer
	id) {
	String url = "http://localhost:9001/payment/" + id;
	List<ServiceInstance> serviceInstances =
	discoveryClient.getInstances("cloud-payment-service");
	ServiceInstance serviceInstance = serviceInstances.get(0);
	url = "http://" + serviceInstance.getHost() + ":" +
	serviceInstance.getPort() + "/payment/" + id;
	Payment payment = restTemplate.getForObject(url, Payment.class);
	return ResponseEntity.ok(payment);
	}

RestTemplate⽀持常⻅的Http协议请求⽅法,例如post, get, delete等,所以⽤RestTemplate很
容易构建RESTfule API。上述案例结果返回json对象,使⽤jackson框架完成。

LoadBalancer负载均衡

负载均衡是指将负载分摊到多个执⾏单元上,常⻅的负载均衡有两种⽅式。⼀种独⽴进程单元,通
过负载均衡策略,将请求转发到不同的执⾏单元上,例如Nginx。另⼀种是将负载均衡逻辑以代码的形
式封装到服务消费者的客户端上,服务消费者客户端维护了⼀份服务提供者的信息列表,有了信息表,
通过负载均衡策略将请求分摊给多个服务提供者,从⽽达到负载均衡的⽬的。
SpringCloud原有的客户端负载均衡⽅案Ribbon已经被废弃,取⽽代之的是SpringCloud
LoadBalancer,LoadBalancer是Spring Cloud Commons的⼀个⼦项⽬,他属于上述的第⼆种
⽅式,是将负载均衡逻辑封装到客户端中,并且运⾏在客户端的进程⾥。
在Spring Cloud构件微服务系统中,LoadBalancer作为服务消费者的负载均衡器,有两种使⽤⽅
式,⼀种是和RestTemplate相结合,另⼀种是和Feign相结合,Feign已经默认集成了LoadBalancer,
关于Feign下⼀章讲解。

LoadBalancer整合RestTemplate

在⽀付微服务⼯程中进⾏如下更改。

  1. 配置⽂件
    在application.yml配置⽂件中,使⽤spel指定端⼝,表示存在port参数使⽤port参数,不存在使⽤
    默认9001端⼝, 启动⽀付服务时,可以通过指定-Dport=9000,指定⽀付服务使⽤不同端⼝启动,具体参考后⾯内容。
server:
port: ${port:9001}
  1. PaymentController
    在提供⽀付服务时,把端⼝打印出来,⽅便查看测试效果。代码如下。
@RestController
@RequestMapping("/payment")
public class PaymentController {
	@Value("${server.port}")
	private String serverPort;
	@GetMapping("/{id}")
	public ResponseEntity<Payment> payment(@PathVariable("id") Integer id) {
		Payment payment = new Payment(id, "⽀付成功,服务端⼝=" + serverPort);
		return ResponseEntity.ok(payment);
	}
}
  1. OrderApplication
    在产⽣RestTemplate实例时,使⽤@LoadBalanced注解,开启负载均衡。
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
	return new RestTemplate();
}
  1. OrderController
    在OrderController中,使⽤serviceId调⽤⽀付服务,此时LoadBalancer负载均衡⽣效,从多个服
    务提供者节点轮询选择⼀个使⽤。
@GetMapping("/payment/{id}")
public ResponseEntity<Payment> getPaymentById(@PathVariable("id") Integer
id) {
	String url = "http://cloud-payment-service/payment/" + id;
	Payment payment = restTemplate.getForObject(url, Payment.class);
	return ResponseEntity.ok(payment);
}
  1. 启动并测试
    启动两个⽀付微服务⼯程,端⼝分别是9000和9001,因为application.yml配置⽂件中使⽤
    ${port:9001}配置端⼝,其中9000节点启动配置如图3-1所示。
    在这里插入图片描述
    另⼀个⽀付节点,不指定-Dport,使⽤默认9001端⼝启动,这时准备了2个⽀付微服务节点。
    Eureka注册效果如图3-2所示。
    在这里插入图片描述
    接下来在浏览器多次访问http://localhost:9002/order/payment/123456时,负载均衡器起了作
    ⽤,结果9000,9001轮流出现。
    ⽀付成功,服务端⼝=9000
    ⽀付成功,服务端⼝=9001

LoadBlancerClient简介

负载均衡的核⼼类为LoadBalancerClient,LoadBalancerClient可以获取负载均衡的服务提供者实
例信息。在OrderController增加演示代码如下。

@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/test-load-balancer")
public String testLoadBalancer() {
	ServiceInstance instance = loadBalancerClient.choose("cloud-paymentservice");
	return instance.getHost() + ":" + instance.getPort();
}

重启⼯程,浏览器访问http://localhost:9002/order/test-load-balancer,发现浏览器轮流显示如下内

localhost:9000
localhost:9001

LoadBalancer源码解析

类的调⽤顺序

当时使⽤有@LoadBalanced注解的RestTemplate时,设计的类的调⽤关系如图3-3所示
在这里插入图片描述
关键类解析
●LoadBalancerRequestFactory: ⼀个⼯⼚, 包装⼀个为HttpRequest对象,回调对象
LoadBalancerRequest。
●LoadBalancerClient: ⽤于根据 serviceId 选取⼀个 ServiceInstance, 执⾏从
LoadBalancerRequestFactory 获得的那个回调。
●LoadBalancerInterceptor:RestTemplate 的拦截器, 拦截后调⽤ LoadBalancerClient 修改
HttpRequest 对象(主要是 url), 且传⼊调⽤ LoadBalancerRequestFactory ⽣成的回调给
LoadBalancerClient。
● RestTemplateCustomizer:为 restTemplate 加上⼀个拦截器(也可以⼲点别的, 默认就这⼀个⽤
处) 。
● SmartInitializingSingleton:容器初始化是,调⽤ RestTemplateCustomizer 为容器中所有加了。
@LoadBalanced 的 RestTemplate 加上⼀个拦截器。
●ReactiveLoadBalancer:定义了 choose ⽅法, 即如何选取⼀个 ServiceInstance, 如轮播, 随机。

源码部分

  1. LoadBalancerInterceptor
    RestTemplate 的拦截器, 拦截后调⽤ LoadBalancerClient 修改 HttpRequest 对象(主要是 url),
    且传⼊调⽤ LoadBalancerRequestFactory ⽣成的回调给 LoadBalancerClient。
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[]
body,
	final ClientHttpRequestExecution execution) throws IOException {
	final URI originalUri = request.getURI();
	String serviceName = originalUri.getHost();
	Assert.state(serviceName != null, "Request URI does not contain a valid
	hostname: " + originalUri);
	return this.loadBalancer.execute(serviceName,
	this.requestFactory.createRequest(request, body, execution));
}
  1. BlockingLoadBalancerClient
    ⽤于根据 serviceId 选取⼀个 ServiceInstance, 执⾏从 LoadBalancerRequestFactory 获得的那
    个回调,发送HTTP请求,远程调⽤REST ful API。代码如下所示。
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
	String hint = getHint(serviceId);
	LoadBalancerRequestAdapter<T, DefaultRequestContext> lbRequest = new
	LoadBalancerRequestAdapter<>(request,
	new DefaultRequestContext(request, hint));
	Set<LoadBalancerLifecycle> supportedLifecycleProcessors =
	getSupportedLifecycleProcessors(serviceId);
	supportedLifecycleProcessors.forEach(lifecycle ->
	lifecycle.onStart(lbRequest));
	ServiceInstance serviceInstance = choose(serviceId, lbRequest);
	if (serviceInstance == null) {
	supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
	new CompletionContext<>(CompletionContext.Status.DISCARD,
	lbRequest, new EmptyResponse())));
	throw new IllegalStateException("No instances available for " +
	serviceId);
	}
	return execute(serviceId, serviceInstance, lbRequest);
}

choose⽅法,调⽤RoundRobinLoadBalancer的choose⽅法,以轮播⽅式获取⼀个
serviceInstance。代码如下所示。

@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
	ReactiveLoadBalancer<ServiceInstance> loadBalancer =
	loadBalancerClientFactory.getInstance(serviceId);
	if (loadBalancer == null) {
	return null;
	}
	Response<ServiceInstance> loadBalancerResponse =
	Mono.from(loadBalancer.choose(request)).block();
	if (loadBalancerResponse == null) {
	return null;
	}
	return loadBalancerResponse.getServer();
}
  1. RoundRobinLoadBalancer
    RoundRobinLoadBalancer的choose⽅法,以轮播⽅式获取⼀个serviceInstance。代码如下所
    示。
public Mono<Response<ServiceInstance>> choose(Request request) {
	ServiceInstanceListSupplier supplier =
	serviceInstanceListSupplierProvider
	.getIfAvailable(NoopServiceInstanceListSupplier::new);
	return supplier.get(request).next()
	.map(serviceInstances -> processInstanceResponse(supplier,
	serviceInstances));
}

Spirng Cloud OpenFeign

Feign是⼀个声明式的HTTP客户端组件,它旨在是编写Http客户端变得更加容易。OpenFeign添加
了对于Spring MVC注解的⽀持,同时集成了Spring Cloud LoadBalancer和Spring Cloud
CircuitBreaker,在使⽤Feign时,提供负载均衡和熔断降级的功能。

配置步骤

在订单⼯程⼯程的pom.xml中添加如下依赖

  1. 添加依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 开启Feign功能
    使⽤@EnableFeignClients开启Feign功能
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
	public static void main(String[] args) {
		SpringApplication.run(OrderApplication.class, args);
	}
	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}
  1. 创建Feign客户端
    在注解@FeignClient注解中,“cloud-payment-service”是服务名,使⽤这个名字来从Eureka服务
    列表中得到相应的服务,来创建LoadBalancer客户端,也可以使⽤url属性,指定服务的URL。
@FeignClient(value = "cloud-payment-service")
public interface PaymentClient {
	@GetMapping("/payment/{id}")
	public Payment payment(@PathVariable("id") Integer id);
}
  1. OrderController
    在OrderController中使⽤FeignClient访问⽀付服务,代码如下。
@Autowired
private PaymentClient paymentClient;
	@GetMapping("/feign/payment/{id}")
	public ResponseEntity<Payment> getPaymentByFeign(@PathVariable("id")
	Integer id) {
		Payment payment = paymentClient.payment(id);
		return ResponseEntity.ok(payment);
}
  1. 启动并测试
    分别启动⽀付服务9000端⼝,9001端⼝,订单服务,访问
    http://localhost:9002/order/feign/payment/123,执⾏效果如图3-4所示。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值