一、服务远程简单调用

image.png
服务远程调用,可以利用http+传统的servlet服务实现。(关于通信服务,也可使用其他,例如:netty等框架)

  1. 服务消费者通过httpClient,使用ip+port。调用远程服务。
  2. 服务提供者,接受入参,(通过接口信息获取实现类信息)通过反射方式,执行业务处理逻辑,序列化处理结果返回。

    缺陷

    服务消费者,在调用远程服务时,每次都需要进行参数组装(Invocattion参数)、HttpClient数据发送等远程调用逻辑实现。重复的调用逻辑严重影响正常业务逻辑
    image.png

    消费端实现

    1. public class OriginalConsumer {
    2. public static void main(String[] args) throws IOException {
    3. //组装Invocation参数
    4. RpcInvocation invocation = new RpcInvocation();
    5. invocation.setServiceName(DemoServiceImpl.class.getName());
    6. invocation.setMethodName("sayHello");
    7. invocation.setParameterTypes(new Class[]{String.class});
    8. invocation.setArguments(new Object[]{"masterlu"});
    9. //发送数据
    10. Socket socket = new Socket("127.0.0.1", 8080);
    11. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
    12. oos.writeObject(invocation);
    13. //接收数据
    14. InputStream inputStream = socket.getInputStream();
    15. byte[] bytes = new byte[1024];
    16. int read = inputStream.read(bytes);
    17. System.out.println(new String(bytes,0,read));
    18. }
    19. }

    服务端实现

    ```java /**

    • 原始服务提供 */ public class OriginalService {
  1. public static void main(String[] args) throws IOException {
  2. // 1. 注册服务
  3. // 2. 本地注册
  4. // 3. 启动tomcat
  5. // 注册服务
  6. ServerSocket serverSocket = new ServerSocket(8080);
  7. while (true) {
  8. Socket accept = serverSocket.accept();
  9. new Thread(() -> {
  10. try {
  11. InputStream inputStream = accept.getInputStream();
  12. // 获取接口调用信息
  13. ObjectInputStream ois = new ObjectInputStream(accept.getInputStream());
  14. Invocation invocation = (Invocation) ois.readObject();
  15. Class implClass = Class.forName(invocation.getServiceName());//接口
  16. //反射调用方法
  17. Class[] parameterTypes = new Class[invocation.getArguments().length];
  18. for (int i = 0; i < invocation.getArguments().length; i++) {
  19. parameterTypes[i] = invocation.getArguments()[i].getClass();
  20. }
  21. Method method = implClass.getMethod(invocation.getMethodName(), new Class[]{String.class});
  22. String result = (String) method.invoke(implClass.newInstance(), invocation.getArguments());
  23. System.out.println("socket:" + result);
  24. IOUtils.write(result, accept.getOutputStream());
  25. } catch (Exception e) {
  26. System.out.println(e);
  27. }
  28. }).start();
  29. }
  30. }

}

  1. <a name="kMsqd"></a>
  2. # 二、动态代理优化服务远程调用
  3. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/13013491/1666612005954-824e53a0-6099-4cb1-b623-ae1655fd44cd.png#averageHue=%23f1f1f1&clientId=uf4921a63-9744-4&from=paste&height=209&id=u55925192&originHeight=314&originWidth=1407&originalType=binary&ratio=1&rotation=0&showTitle=false&size=80023&status=done&style=none&taskId=u662e767c-14cc-45c6-a2ca-64107097204&title=&width=938)
  4. <a name="Hs5uo"></a>
  5. ## 缺陷
  6. 服务消费者,在代码中写死服务提供者IP。提供者下线、扩容,对服务消费者,影响很大。
  7. <a name="Pwk1A"></a>
  8. ## 优化消费端实现
  9. ```java
  10. public static void main(String[] args) throws IOException {
  11. //将远程调用逻辑进行封装,减少对业务逻辑的影响
  12. DemoService demoService = getProxy(DemoServiceImpl.class);
  13. String result = demoService.sayHello("masterlu");
  14. System.out.println(result);
  15. }
  16. public static <T> T getProxy(final Class interfaceClass) {
  17. return (T)Proxy.newProxyInstance(
  18. interfaceClass.getClassLoader(),
  19. interfaceClass.getInterfaces(),
  20. (proxy, method, args) -> {
  21. //组装Invocation参数
  22. RpcInvocation invocation = new RpcInvocation();
  23. invocation.setServiceName(interfaceClass.getName());
  24. invocation.setMethodName(method.getName());
  25. invocation.setParameterTypes(method.getParameterTypes());
  26. invocation.setArguments(args);
  27. //发送数据
  28. Socket socket = new Socket("127.0.0.1", 8080);
  29. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  30. oos.writeObject(invocation);
  31. //接收数据
  32. InputStream inputStream = socket.getInputStream();
  33. byte[] bytes = new byte[1024];
  34. int read = inputStream.read(bytes);
  35. return new String(bytes,0,read);
  36. });
  37. }
  38. }

三、引入注册中心优化服务远程调用

image.png

引入注册中心,服务提供者,将信息存储在注册中;消费者实时感知可用服务。
注册中心数据缓存
服务消费者,获取注册中心数据后,缓存在本地。注册中心临时短时间故障,不影响服务使用。
失败重试
服务消费获取远程服务列表后,可实现服务的调用策略(随机、轮询、最少活跃、一致性哈希【根据参数计算hash,决定调用服务,参数一致会调用同一台】)

注册中心作用

监控服务提供者
可以利用zk的临时节点,或利用redis的定时节点(定时延期),实时感知服务提供者的服务可用性,监控服务提供者的服务。
发现服务消费者
利用zk的wacth机制(注册到提供的创建的临时节点上)、redis的发布订阅模式,及时感知服务提供者增、减的情况

四、增加监控中心

image.png