🏆 技术专题第五期 | 分布式服务远程调用演进之使用反向代理简化HTTP调用

988 阅读5分钟

1. 剧情回顾

带你入门RPC之反向代理主要回忆了反向代理实现方式,带你入门RPC之Http调用主要介绍了如何使用http方式调用远程服务,在第二篇结尾部分也提交到调用方式存在的缺陷,那就是远程调用逻辑对于开发者来说并不是透明的,开发者需要针对每个接口编写调用代码,增加了开发工作量以及开发成本。

2. 前言

既然之前的调用方式存在很大的弊端,那么此篇文章将结合第一篇讲解的代理技术来进行一定程度的优化,使得远程调用逻辑对开发者来说是透明的,开发者不需要过多的关心远程调用逻辑。

3. 实战

3.1 消费者业务接口改造

public interface PersonHttpCallService1 {

    List<Person> listPerson(String url);

    void savePerson(String url, SavePersonRequest request);
}

相比之前接口多了一个参数,就是请求远程服务对应的url地址

3.2 消费者业务接口实现改造

@Service
public class PersonHttpCallServiceImpl1 implements PersonHttpCallService1 {

    @Override
    public List<PersonlistPerson(String url) {
        return null;
    }

    @Override
    public void savePerson(String url, SavePersonRequest request) {
    }
}

3.3 定义http代理类

public class HttpCallProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (args.length > 2) {
            throw new IllegalArgumentException("method param length more than 2");
        }

        // 指定请求体数据类型
        final MediaType JSON = MediaType.get("application/json; charset=utf-8");
        OkHttpClient client = new OkHttpClient();
        // 如果只有一个url参数,则使用get请求;如果存在2个参数,则使用post请求
        Request request = args.length == 1 ?
                new Request.Builder()
                        .url(String.valueOf(args[0]))
                        .build() :
                new Request.Builder()
                        .post(RequestBody.create(JSONObject.toJSONString(args[1]), JSON))
                        .url(String.valueOf(args[0]))
                        .build();
        // 发送http请求
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                return null;
            }

            ResponseBody responseBody = response.body();
            if (Objects.isNull(responseBody)) {
                return null;
            }

            String responseContent = responseBody.string();
            // 解析返回结果
            return JSONObject.parseObject(responseContent, method.getReturnType());
        } catch (IOException e) {
            return null;
        }
    }
}

将http调用逻辑代码封装在代理类中,当调用被代理对象的时候会执行该方法,进行远程服务调用,使得远程调用逻辑对开发者来将是透明的,开发者无须关心远程调用细节实现。

代理中实现远程调用的方式也比较简单,如果目标方法只有一个参数,则认为是一个get请求,如果目标方法中有两个参数,则认为是一个post请求,这里只是一个简单的实现,希望各位看官不要过于纠结。

3.4 定义http代理工厂

public class HttpCallProxyFactory {

    private HttpCallProxy httpCallProxy;

    private Object target;

    public HttpCallProxyFactory(HttpCallProxy httpCallProxy, Object target) {
        this.httpCallProxy = httpCallProxy;
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), httpCallProxy);
    }
}

3.5 消费者控制器改造

@RestController
@RequestMapping("/persons1")
public class PersonHttpCallController1 {

    @Autowired
    private PersonHttpCallService1 personHttpCallService1;

    @GetMapping("/")
    public List<Person> listPerson() {
        HttpCallProxyFactory proxyFactory = new HttpCallProxyFactory(new HttpCallProxy(), personHttpCallService1);
        PersonHttpCallService1 proxy = (PersonHttpCallService1) proxyFactory.getProxy();
        return proxy.listPerson(RequestUrlConstant.LIST_PERSON_URL);
    }

    @PostMapping("/")
    public void savePerson(@RequestBody SavePersonRequest savePersonRequest) {
        HttpCallProxyFactory proxyFactory = new HttpCallProxyFactory(new HttpCallProxy(), personHttpCallService1);
        PersonHttpCallService1 proxy = (PersonHttpCallService1) proxyFactory.getProxy();
        proxy.savePerson(RequestUrlConstant.SAVE_PERSON_URL, savePersonRequest);
    }
}

4. 总结

本文通过反向代理技术实现了远程过程调用逻辑的透明性,简化了远程调用开发工作。但是依然还存在着很多的不足,比如消费者接口实现类完全没有任何作用,是否可以省略?远程服务调用接口参数中是否可以把url参数去掉?是否可以模仿Mybatis来实现更简洁的实现?这些问题会在下一个章节进行解决,敬请期待。。。

🏆 技术专题第五期 | 聊聊分布式的那些事......