什么是微服务?微服务之间是如何独立通讯的?

什么是微服务

  • 微服务架构是一个分布式系统,按照业务进行划分成为不同的服务单元,解决单体系统性能等不足。
  • 微服务是一种架构风格,一个大型软件应用由多个服务单元组成。系统中的服务单元可以单独部署,各个服务单元之间是松耦合的。

微服务概念起源:Microservices

微服务之间是如何独立通讯的

同步

REST HTTP 协议

REST 请求在微服务中是最为常用的一种通讯方式,它依赖于 HTTP\HTTPS 协议。RESTFUL 的特点是:

  1. 每一个 URI 代表 1 种资源
  2. 客户端使用 GET、POST、PUT、DELETE 4 个表示操作方式的动词对服务端资源进行操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源
  3. 通过操作资源的表现形式来操作资源
  4. 资源的表现形式是 XML 或者 HTML
  5. 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息

举个例子,有一个服务方提供了如下接口:

  1. @RestController
  2. @RequestMapping("/communication")
  3. public class RestControllerDemo {
  4. @GetMapping("/hello")
  5. public String s() {
  6. return "hello";
  7. }
  8. }

另外一个服务需要去调用该接口,调用方只需要根据 API 文档发送请求即可获取返回结果。

  1. @RestController
  2. @RequestMapping("/demo")
  3. public class RestDemo{
  4. @Autowired
  5. RestTemplate restTemplate;
  6. @GetMapping("/hello2")
  7. public String s2() {
  8. String forObject = restTemplate.getForObject("http://localhost:9013/communication/hello", String.class);
  9. return forObject;
  10. }
  11. }

通过这样的方式可以实现服务之间的通讯。

RPC TCP 协议

RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。它的工作流程是这样的:

  1. 执行客户端调用语句,传送参数
  2. 调用本地系统发送网络消息
  3. 消息传送到远程主机
  4. 服务器得到消息并取得参数
  5. 根据调用请求以及参数执行远程过程(服务)
  6. 执行过程完毕,将结果返回服务器句柄
  7. 服务器句柄返回结果,调用远程主机的系统网络服务发送结果
  8. 消息传回本地主机
  9. 客户端句柄由本地主机的网络服务接收消息
  10. 客户端接收到调用语句返回的结果数据

举个例子。

首先需要一个服务端:

  1. import java.io.IOException;
  2. import java.io.ObjectInputStream;
  3. import java.io.ObjectOutputStream;
  4. import java.lang.reflect.Method;
  5. import java.net.InetSocketAddress;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. import java.util.concurrent.ExecutorService;
  10. import java.util.concurrent.Executors;
  11. /**
  12. * RPC 服务端用来注册远程方法的接口和实现类
  13. */
  14. public class RPCServer {
  15. private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  16. private static final ConcurrentHashMap<String, Class> serviceRegister = new ConcurrentHashMap<>();
  17. /**
  18. * 注册方法
  19. * @param service
  20. * @param impl
  21. */
  22. public void register(Class service, Class impl) {
  23. serviceRegister.put(service.getSimpleName(), impl);
  24. }
  25. /**
  26. * 启动方法
  27. * @param port
  28. */
  29. public void start(int port) {
  30. ServerSocket socket = null;
  31. try {
  32. socket = new ServerSocket();
  33. socket.bind(new InetSocketAddress(port));
  34. System.out.println("服务启动");
  35. System.out.println(serviceRegister);
  36. while (true) {
  37. executor.execute(new Task(socket.accept()));
  38. }
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. } finally {
  42. if (socket != null) {
  43. try {
  44. socket.close();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. }
  50. }
  51. private static class Task implements Runnable {
  52. Socket client = null;
  53. public Task(Socket client) {
  54. this.client = client;
  55. }
  56. @Override
  57. public void run() {
  58. ObjectInputStream input = null;
  59. ObjectOutputStream output = null;
  60. try {
  61. input = new ObjectInputStream(client.getInputStream());
  62. // 按照顺序读取对方写过来的内容
  63. String serviceName = input.readUTF();
  64. String methodName = input.readUTF();
  65. Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
  66. Object[] arguments = (Object[]) input.readObject();
  67. Class serviceClass = serviceRegister.get(serviceName);
  68. if (serviceClass == null) {
  69. throw new ClassNotFoundException(serviceName + " 没有找到!");
  70. }
  71. Method method = serviceClass.getMethod(methodName, parameterTypes);
  72. Object result = method.invoke(serviceClass.newInstance(), arguments);
  73. output = new ObjectOutputStream(client.getOutputStream());
  74. output.writeObject(result);
  75. } catch (Exception e) {
  76. e.printStackTrace();
  77. } finally {
  78. try {
  79. // 这里就不写 output!=null才关闭这个逻辑了
  80. output.close();
  81. input.close();
  82. client.close();
  83. } catch (IOException e) {
  84. e.printStackTrace();
  85. }
  86. }
  87. }
  88. }
  89. }

其次需要一个客户端:

  1. import java.io.ObjectInputStream;
  2. import java.io.ObjectOutputStream;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.net.InetSocketAddress;
  7. import java.net.Socket;
  8. /**
  9. * RPC 客户端
  10. */
  11. public class RPCclient<T> {
  12. /**
  13. * 通过动态代理将参数发送过去到 RPCServer ,RPCserver 返回结果这个方法处理成为正确的实体
  14. */
  15. public static <T> T getRemoteProxyObj(final Class<T> service, final InetSocketAddress addr) {
  16. return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
  17. @Override
  18. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  19. Socket socket = null;
  20. ObjectOutputStream out = null;
  21. ObjectInputStream input = null;
  22. try {
  23. socket = new Socket();
  24. socket.connect(addr);
  25. // 将实体类,参数,发送给远程调用方
  26. out = new ObjectOutputStream(socket.getOutputStream());
  27. out.writeUTF(service.getSimpleName());
  28. out.writeUTF(method.getName());
  29. out.writeObject(method.getParameterTypes());
  30. out.writeObject(args);
  31. input = new ObjectInputStream(socket.getInputStream());
  32. return input.readObject();
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. } finally {
  36. out.close();
  37. input.close();
  38. socket.close();
  39. }
  40. return null;
  41. }
  42. });
  43. }
  44. }

再来一个测试的远程方法。

  1. public interface Tinterface {
  2. String send(String msg);
  3. }
  4. public class TinterfaceImpl implements Tinterface {
  5. @Override
  6. public String send(String msg) {
  7. return "send message " + msg;
  8. }
  9. }

测试代码如下:

  1. import java.net.InetSocketAddress;
  2. public class RunTest {
  3. public static void main(String[] args) {
  4. new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. RPCServer rpcServer = new RPCServer();
  8. rpcServer.register(Tinterface.class, TinterfaceImpl.class);
  9. rpcServer.start(10000);
  10. }
  11. }).start();
  12. Tinterface tinterface = RPCclient.getRemoteProxyObj(Tinterface.class, new InetSocketAddress("localhost", 10000));
  13. System.out.println(tinterface.send("rpc 测试用例"));
  14. }
  15. }

输出 send message rpc 测试用例

异步

消息中间件

常见的消息中间件有 Kafka、ActiveMQ、RabbitMQ、RocketMQ ,常见的协议有 AMQP、MQTTP、STOMP、XMPP。这里不对消息队列进行拓展了,具体如何使用还是请移步官网。