一、背景
假如现在有一台服务A,两台服务B,可以简化为如下图模型:
需求:一次请求服务A需要同时将请求广播打到两台服务B上。
二、实现方案
2.1 需要应用到两个类:
2.1.1:LoadBalancerClient
package org.springframework.cloud.client.loadbalancer;
import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;
public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
2.1.2:DiscoveryClient
package org.springframework.cloud.client.discovery;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
public interface DiscoveryClient extends Ordered {
int DEFAULT_ORDER = 0;
String description();
List<ServiceInstance> getInstances(String serviceId);
List<String> getServices();
default void probe() {
this.getServices();
}
default int getOrder() {
return 0;
}
}
以上两个类都是SpringCloud中的,读者自己可以搜索引入对应的包,这里就不写引入的包的配置文件了,因为笔者懒。
2.2 自定义核心实现
2.2.1:服务A中的Controller
@RestController
@RequestMapping("/v1/test_a")
public class TestController {
@Resource
private TestService testService;
@getMapping(path = "/invidate")
public Result<Boolean> test() {
return Result.Builder.success(testService.test());
}
}
2.2.2:服务A中的TestService(核心实现)
public void test(){
String serviceId = "test_b";
final List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
if (CollectionUtils.isNotEmpty(instances)) {
for (ServiceInstance instance : instances) {
try {
Server server = new Server(instance.getHost(), instance.getPort());
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(instance.getServiceId(), server);
loadBalancerClient.execute(instance.getServiceId(), ribbonServer, v -> {
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/v1/test_b/invalidate";
url = String.format(url, req.getWbCode());
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, Result.class);
});
} catch (IOException e) {
log.error("loadBalancerClient happen error:{}", e.getMessage(), e);
}
}
}
}
代码中的serviceId=test_b,这个test_b是笔者nacos注册中心注册的服务的名称,笔者的服务是用nacos作为服务发现和注册中心的,如果读者的服务的发现和注册中心不一致,可以自己理解了原理后自己研究一下。
代码中的“/v1/test_b/invalidate”就是服务A需要请求的服务B中的接口。这个段代码的核心是先用组件discoveryClient获取到服务提供者的列表,然后遍历每个服务提供者,向每个服务提供者发送信息。
2.2.3 服务B中的Controller
@RestController
@RequestMapping("/v1/test_b")
public class InvalidCacheInnerController {
@GetMapping(path = "/invalidate")
public Result<Boolean> invalidate() {
return Result.Builder.success(true);
}
}
服务B中就是简单返回一个true就完事了。
化繁为简,大道至简~~~~
最后的最后,重点~重点~题主开通了公众号:有理唔理,喜欢的话,可以关注一下,一起探讨技术与生活。