33.服务之间的调用之RPC、Restful深入理解

2018年05月08日 01:52:42 郑学炜 阅读数 13577更多

分类专栏: 6.框架
 
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。

RPC、REST API深入理解

一:RPC

RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法)。通常的实现有 XML-RPC , JSON-RPC , 通信方式基本相同, 所不同的只是传输数据的格式.

RPC是分布式架构的核心,按响应方式分如下两种:

同步调用:客户端调用服务方方法,等待直到服务方返回结果或者超时,再继续自己的操作

异步调用:客户端把消息发送给中间件,不再等待服务端返回,直接继续自己的操作。

同步调用的实现方式有WebService和RMI。Web Service提供的服务是基于web容器的,底层使用http协议,因而适合不同语言异构系统间的调用。RMI实际上是Java语言的RPC实现,允许方法返回 Java 对象以及基本数据类型,适合用于JAVA语言构建的不同系统间的调用。

异步调用的JAVA实现版就是JMS(Java Message Service),目前开源的的JMS中间件有Apache社区的ActiveMQ、Kafka消息中间件,另外有阿里的RocketMQ。

RPC架构里包含如下4个组件:

1、 客户端(Client):服务调用方

2、 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数打包成网络消息,再通过网络发送给服务方

3、 服务端存根(Server Stub):接受客户端发送过来的消息并解包,再调用本地服务

4、服务端(Server):真正的服务提供者。

具体实现步骤:

1、 服务调用方(client)(客户端)以本地调用方式调用服务;

2、 client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;在Java里就是序列化的过程

3、 client stub找到服务地址,并将消息通过网络发送到服务端;

4、 server stub收到消息后进行解码,在Java里就是反序列化的过程;

5、 server stub根据解码结果调用本地的服务;

6、 本地服务执行处理逻辑;

7、 本地服务将结果返回给server stub;

8、 server stub将返回结果打包成消息,Java里的序列化;

9、 server stub将打包后的消息通过网络并发送至消费方

10、 client stub接收到消息,并进行解码, Java里的反序列化;

11、 服务调用方(client)得到最终结果。

RPC框架的目标就是把2-10步封装起来,把调用、编码/解码的过程封装起来,让用户像调用本地服务一样的调用远程服务。要做到对客户端(调用方)透明化服务, RPC框架需要考虑解决如下问题: 
1、通讯问题 : 主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。 
2、寻址问题 : A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。 
3、序列化与反序列化 : 当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。 
同理,B服务器接收参数要将参数反序列化。B服务器应用调用自己的方法处理后返回的结果也要序列化给A服务器,A服务器接收也要经过反序列化的过程。

二:REST

  REST即表述性状态传递(Representational State Transfer,简称REST),是一种软件架构风格。REST通过HTTP协议定义的通用动词方法(GET、PUT、DELETE、POST) ,以URI对网络资源进行唯一标识,响应端根据请求端的不同需求,通过无状态通信,对其请求的资源进行表述。 
  Rest架构的主要原则:

1.   网络上的所有事物都被抽象为资源

2.   每个资源都有一个唯一的资源标识符

3.   同一个资源具有多种表现形式(xml,json等)

4.   对资源的各种操作不会改变资源标识符

5.   所有的操作都是无状态的

其中表述性状态,是指(在某个瞬间状态的)资源数据的快照,包括资源数据的内容、表述格式(XML、JSON)等信息。

其中无状态通信,是指服务端(响应端)不保存任何与特定HTTP请求相关的资源,应用状态必须由请求方在请求过程中提供。要求在网络通信过程中,任意一个Web请求必须与其他请求隔离,当请求端提出请求时,请求本身包含了响应端为响应这一请求所需的全部信息。

REST使用HTTP+URI+XML /JSON 的技术来实现其API要求的架构风格:HTTP协议和URI用于统一接口和定位资源,文本、二进制流、XML、JSON等格式用来作为资源的表述。

举例: 
在Restful之前的操作: 请求的地址对应具体的业务操作 
http://127.0.0.1/user/query/1 GET 根据用户id查询用户数据 
http://127.0.0.1/user/save POST 新增用户 
http://127.0.0.1/user/update POST 修改用户信息 
http://127.0.0.1/user/delete GET/POST 删除用户信息

RESTful用法: 请求 
http://127.0.0.1/user/1 GET 根据用户id查询用户数据 
http://127.0.0.1/user POST 新增用户 
http://127.0.0.1/user PUT 修改用户信息 
http://127.0.0.1/user DELETE 删除用户信息

RESTful风格的体现,在你使用了get请求,就是查询;使用post请求,就是新增的请求;使用put请求,就是修改的请求;使用delete请求,就是删除的请求。这样做就完全没有必要对crud做具体的描述。

满足REST约束条件和原则的架构,就被称为是RESTful架构。就像URL都是URI(统一资源标识)的表现形式一样,RESTful是符合REST原则的表现形式。

如何使用:

SpringMVC实现restful服务:

SpringMVC原生态的支持了REST风格的架构设计

所涉及到的注解:

--@RequestMapping

---@PathVariable

---@ResponseBody

  1. package cn.itcast.mybatis.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.http.HttpStatus;
  4. import org.springframework.http.ResponseEntity;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.web.bind.annotation.PathVariable;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestMethod;
  9. import org.springframework.web.bind.annotation.RequestParam;
  10. import org.springframework.web.bind.annotation.ResponseBody;
  11. import cn.itcast.mybatis.pojo.User;
  12. import cn.itcast.mybatis.service.NewUserService;
  13. @RequestMapping("restful/user")
  14. @Controller
  15. public class RestUserController {
  16. @Autowired
  17. private NewUserService newUserService;
  18. /**
  19. * 根据用户id查询用户数据
  20. *
  21. * @param id
  22. * @return
  23. */
  24. @RequestMapping(value = "{id}", method = RequestMethod.GET)
  25. @ResponseBody
  26. public ResponseEntity<User> queryUserById(@PathVariable("id") Long id) {
  27. try {
  28. User user = this.newUserService.queryUserById(id);
  29. if (null == user) {
  30. // 资源不存在,响应404
  31. return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
  32. }
  33. // 200
  34. // return ResponseEntity.status(HttpStatus.OK).body(user);
  35. return ResponseEntity.ok(user);
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. // 500
  40. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
  41. }
  42. /**
  43. * 新增用户
  44. *
  45. * @param user
  46. * @return
  47. */
  48. @RequestMapping(method = RequestMethod.POST)
  49. public ResponseEntity<Void> saveUser(User user) {
  50. try {
  51. this.newUserService.saveUser(user);
  52. return ResponseEntity.status(HttpStatus.CREATED).build();
  53. } catch (Exception e) {
  54. // TODO Auto-generated catch block
  55. e.printStackTrace();
  56. }
  57. // 500
  58. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
  59. }
  60. /**
  61. * 更新用户资源
  62. *
  63. * @param user
  64. * @return
  65. */
  66. @RequestMapping(method = RequestMethod.PUT)
  67. public ResponseEntity<Void> updateUser(User user) {
  68. try {
  69. this.newUserService.updateUser(user);
  70. return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. }
  74. // 500
  75. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
  76. }
  77. /**
  78. * 删除用户资源
  79. *
  80. * @param user
  81. * @return
  82. */
  83. @RequestMapping(method = RequestMethod.DELETE)
  84. public ResponseEntity<Void> deleteUser(@RequestParam(value = "id", defaultValue = "0") Long id) {
  85. try {
  86. if (id.intValue() == 0) {
  87. // 请求参数有误
  88. return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
  89. }
  90. this.newUserService.deleteUserById(id);
  91. // 204
  92. return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
  93. } catch (Exception e) {
  94. e.printStackTrace();
  95. }
  96. // 500
  97. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
  98. }
  99. }

1.           以ApacheThrift为代表的二进制RPC,支持多种语言(但不是所有语言),四层通讯协议,性能高,节省带宽。相对Restful协议,使用Thrifpt RPC,在同等硬件条件下,带宽使用率仅为前者的20%,性能却提升一个数量级。但是这种协议最大的问题在于,无法穿透防火墙。

2.           以Spring Cloud为代表所支持的Restful 协议,优势在于能够穿透防火墙,使用方便,语言无关,基本上可以使用各种开发语言实现的系统,都可以接受Restful 的请求。但性能和带宽占用上有劣势。

所以,业内对微服务的实现,基本是确定一个组织边界,在该边界内,使用RPC; 边界外,使用Restful。这个边界,可以是业务、部门,甚至是全公司。

使用RPC远程服务调用方式与传统http接口直接调用方式的差别在于:

1. 从使用方面看,Http接口只关注服务提供方(服务端),对于客户端怎么调用,调用方式怎样并不关心,通常情况下,客户端使用Http方式进行调用时,只要将内容进行传输即可,这样客户端在使用时,需要更关注网络方面的传输,比较不适用与业务方面的开发;而RPC服务则需要客户端接口与服务端保持一致,服务端提供一个方法,客户端通过接口直接发起调用,业务开发人员仅需要关注业务方法的调用即可,不再关注网络传输的细节,在开发上更为高效。

2. 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。

3. 从运维角度看,使用Http接口时,常常使用一个前端代理,来进行Http转发代理请求的操作,需要进行扩容时,则需要去修改代理服务器的配置,较为繁琐,也容易出错。而使用RPC方式的微服务,则只要增加一个服务节点即可,注册中心可自动感知到节点的变化,通知调用客户端进行负载的动态控制,更为智能,省去运维的操作。

1.   首先要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,B服务器的IP,以及应用绑定的端口,还有方法的名称,这样才能完成调用

2.   方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式

3.   在B服务器上完成寻址后,需要对参数进行反序列化,恢复为内存中的表达方式,然后找到对应的方法进行本地调用,然后得到返回值,

4.   返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给应用

1、实现技术方案

下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。

2、RPC框架架构

RPC架构分为三部分:

1.     服务提供者,运行在服务器端,提供服务接口定义与服务实现类。

2.     服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。

3.     服务消费者,运行在客户端,通过远程代理对象调用远程服务。

3、 具体实现

1)服务提供者接口定义与实现,代码如下:

[java] view plain copy

1.  package services;

2.

3.

4.  public interface HelloService {

5.

6.

7.      String sayHi(String name);

8.

9.

10. }

2)HelloServices接口实现类:

[java] view plain copy

1.  package services.impl;

2.

3.

4.  import services.HelloService;

5.

6.

7.  public class HelloServiceImpl implements HelloService {

8.

9.

10.     public String sayHi(String name) {

11.         return "Hi, " + name;

12.     }

13.

14.

15. }

3)服务中心代码实现,代码如下:

[java] view plain copy

1.  package services;

2.

3.

4.  import java.io.IOException;

5.

6.

7.  public interface Server {

8.      public void stop();

9.

10.

11.     public void start() throws IOException;

12.

13.

14.     public void register(Class serviceInterface, Class impl);

15.

16.

17.     public boolean isRunning();

18.

19.

20.     public int getPort();

21. }

4)服务中心实现类:

[java] view plain copy

1.  package services.impl;

2.

3.

4.

5.

6.

7.

8.  import java.io.IOException;

9.  import java.io.ObjectInputStream;

10. import java.io.ObjectOutputStream;

11. import java.lang.reflect.Method;

12. import java.net.InetSocketAddress;

13. import java.net.ServerSocket;

14. import java.net.Socket;

15. import java.util.HashMap;

16. import java.util.concurrent.ExecutorService;

17. import java.util.concurrent.Executors;

18.

19.

20. import services.Server;

21.

22.

23. public class ServiceCenter implements Server {

24.     private static ExecutorService              executor        = Executors.newFixedThreadPool(Runtime.getRuntime()

25.                                                                         .availableProcessors());

26.

27.

28.     private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();

29.

30.

31.     private static boolean                      isRunning       = false;

32.

33.

34.     private static int                          port;

35.

36.

37.     public ServiceCenter(int port) {

38.         this.port = port;

39.     }

40.

41.

42.     public void stop() {

43.         isRunning = false;

44.         executor.shutdown();

45.     }

46.

47.

48.     public void start() throws IOException {

49.         ServerSocket server = new ServerSocket();

50.         server.bind(new InetSocketAddress(port));

51.         System.out.println("start server");

52.         try {

53.             while (true) {

54.                 // 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行

55.                 executor.execute(new ServiceTask(server.accept()));

56.             }

57.         } finally {

58.             server.close();

59.         }

60.     }

61.

62.

63.     public void register(Class serviceInterface, Class impl) {

64.         serviceRegistry.put(serviceInterface.getName(), impl);

65.     }

66.

67.

68.     public boolean isRunning() {

69.         return isRunning;

70.     }

71.

72.

73.     public int getPort() {

74.         return port;

75.     }

76.

77.

78.     private static class ServiceTask implements Runnable {

79.         Socket clent = null;

80.

81.

82.         public ServiceTask(Socket client) {

83.             this.clent = client;

84.         }

85.

86.

87.         public void run() {

88.             ObjectInputStream input = null;

89.             ObjectOutputStream output = null;

90.             try {

91.                 // 2.将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果

92.                 input = new ObjectInputStream(clent.getInputStream());

93.                 String serviceName = input.readUTF();

94.                 String methodName = input.readUTF();

95.                 Class<?>[] parameterTypes = (Class<?>[]) input.readObject();

96.                 Object[] arguments = (Object[]) input.readObject();

97.                 Class serviceClass = serviceRegistry.get(serviceName);

98.                 if (serviceClass == null) {

99.                     throw new ClassNotFoundException(serviceName + " not found");

100.                 }

101.                 Method method = serviceClass.getMethod(methodName, parameterTypes);

102.                 Object result = method.invoke(serviceClass.newInstance(), arguments);

103.

104.

105.                 // 3.将执行结果反序列化,通过socket发送给客户端

106.                 output = new ObjectOutputStream(clent.getOutputStream());

107.                 output.writeObject(result);

108.             } catch (Exception e) {

109.                 e.printStackTrace();

110.             } finally {

111.                 if (output != null) {

112.                     try {

113.                         output.close();

114.                     } catch (IOException e) {

115.                         e.printStackTrace();

116.                     }

117.                 }

118.                 if (input != null) {

119.                     try {

120.                         input.close();

121.                     } catch (IOException e) {

122.                         e.printStackTrace();

123.                     }

124.                 }

125.                 if (clent != null) {

126.                     try {

127.                         clent.close();

128.                     } catch (IOException e) {

129.                         e.printStackTrace();

130.                     }

131.                 }

132.             }

133.

134.

135.         }

136.     }

137.

138.

139.

140.

141.

142.

143. }

5)客户端的远程代理对象:

[java] view plain copy

1.  package client;

2.

3.

4.  import java.io.ObjectInputStream;

5.  import java.io.ObjectOutputStream;

6.  import java.lang.reflect.InvocationHandler;

7.  import java.lang.reflect.Proxy;

8.  import java.net.InetSocketAddress;

9.  import java.net.Socket;

10. import java.lang.reflect.Method;

11.

12.

13. public class RPCClient<T> {

14.     @SuppressWarnings("unchecked")

15.     public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {

16.         // 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用

17.         return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[] { serviceInterface },

18.                 new InvocationHandler() {

19.                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

20.                         Socket socket = null;

21.                         ObjectOutputStream output = null;

22.                         ObjectInputStream input = null;

23.                         try {

24.                             // 2.创建Socket客户端,根据指定地址连接远程服务提供者

25.                             socket = new Socket();

26.                             socket.connect(addr);

27.

28.

29.                             // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者

30.                             output = new ObjectOutputStream(socket.getOutputStream());

31.                             output.writeUTF(serviceInterface.getName());

32.                             output.writeUTF(method.getName());

33.                             output.writeObject(method.getParameterTypes());

34.                             output.writeObject(args);

35.

36.

37.                             // 4.同步阻塞等待服务器返回应答,获取应答后返回

38.                             input = new ObjectInputStream(socket.getInputStream());

39.                             return input.readObject();

40.                         } finally {

41.                             if (socket != null)

42.                                 socket.close();

43.                             if (output != null)

44.                                 output.close();

45.                             if (input != null)

46.                                 input.close();

47.                         }

48.                     }

49.                 });

50.     }

51. }

6)最后为测试类:

[java] view plain copy

1.  package client;

2.

3.

4.  import java.io.IOException;

5.  import java.net.InetSocketAddress;

6.

7.

8.  import services.HelloService;

9.  import services.Server;

10. import services.impl.HelloServiceImpl;

11. import services.impl.ServiceCenter;

12.

13.

14. public class RPCTest {

15.     public static void main(String[] args) throws IOException {

16.         new Thread(new Runnable() {

17.             public void run() {

18.                 try {

19.                     Server serviceServer = new ServiceCenter(8088);

20.                     serviceServer.register(HelloService.class, HelloServiceImpl.class);

21.                     serviceServer.start();

22.                 } catch (IOException e) {

23.                     e.printStackTrace();

24.                 }

25.             }

26.         }).start();

27.         HelloService service = RPCClient

28.                 .getRemoteProxyObj(HelloService.class, new InetSocketAddress("localhost", 8088));

29.         System.out.println(service.sayHi("test"));

30.     }

31.

32.

33. }

Restful里面的:(微服务里的)都要同时注册到服务的注册中心里面去。

FeignClient

除了上面的方式,我们还可以用FeignClient。

1

2

3

4

5

6

7

@FeignClient(value = "users", path = "/users")

public interface UserCompositeService {

@RequestMapping(value = "/getUserDetail/{id}",

method = RequestMethod.GET,

produces = MediaType.APPLICATION_JSON_VALUE)

UserDTO getUserById(@PathVariable Long id);

}

我们只需要使用@FeignClient定义一个接口,Spring Cloud Feign会帮我们生成一个它的实现,从相应的users服务获取数据。

其中,@FeignClient(value = “users”, path = “/users/getUserDetail”)里面的value是服务ID,path是这一组接口的path前缀。在下面的方法定义里,就好像设置Spring MVC的接口一样,对于这个方法,它对应的URL是/users/getUserDetail/{id}。然后,在使用它的时候,就像注入一个一般的服务一样注入后使用即可:

1

2

3

4

5

6

7

8

9

public class SomeOtherServiceClass {

@Autowired

private UserCompositeService userService;

public void doSomething() {

// .....

UserDTO results = userService.getUserById(userId);

// other operation...

}

}

从使用方面看,Http接口只关注服务提供方(服务端),对于客户端怎么调用,调用方式怎样并不关心,通常情况下,客户端使用Http方式进行调用时,只要将内容进行传输即可,这样客户端在使用时,需要更关注网络方面的传输,比较不适用与业务方面的开发;(restful是服务端把方法写好,客户端通过http方式调用,直接定位到方法上面去。)

而RPC服务则需要客户端接口与服务端保持一致,服务端提供一个方法,客户端通过接口直接发起调用,业务开发人员仅需要关注业务方法的调用即可,不再关注网络传输的细节,在开发上更为高效。(PRC是服务端提供好方法给客户端调用。定位到类,然后通过类去调用方法。)

比如这种,自己要了一个计算服务,拿到计算服务类后,自己调用服务类里的加法去获得结果

如果是restful,就根据Calculate方法对应的url去传参(people),从而获得结果。

RPC:所谓的远程过程调用 (面向方法)

SOA:所谓的面向服务的架构(面向消息)

REST:所谓的 Representational state transfer (面向资源)

RPC 即远程过程调用, 很简单的概念, 像调用本地服务(方法)一样调用服务器的服务(方法).

通常的实现有 XML-RPC , JSON-RPC , 通信方式基本相同, 所不同的只是传输数据的格式.

REST 的三个要素是 唯一的资源标识, 简单的方法 (此处的方法是个抽象的概念),一定的表达方式.

重要的特性:无状态

33.服务之间的调用之RPC、Restful深入理解的更多相关文章

  1. 服务之间的调用为啥不直接用 HTTP 而用 RPC?

    什么是 RPC?RPC原理是什么? 什么是 RPC? RPC(Remote Procedure Call)-远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.比 ...

  2. SpringCloud实战 | 第五篇:SpringCloud整合OpenFeign实现微服务之间的调用

    一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述SpringCloud整合OpenFeign实现微服务之间的相互调用,有兴趣的朋友可 ...

  3. 第五章 SpringCloud之Eureka-Client使用RestTemplate实现服务之间的调用

    注意:这个章节,请结合前几章节一起使用,因为其要调用上一章节的服务 1.pom.xml <?xml version="1.0" encoding="UTF-8&qu ...

  4. 微服务之间的调用(Ribbon与Feign)

    来源:https://blog.csdn.net/jrn1012/article/details/77837658 使用Eureka作为服务注册中心,在服务启动后,各个微服务会将自己注册到Eureka ...

  5. springcloud 入门 3 (服务之间的调用)

    服务调用: 指的是注册到服务端上的客户端之间数据的相互调用问题:服务与服务的通讯是基于http restful的 服务直接调用主要有两种实现:ribbon  和 feign ribbon是实现负载均衡 ...

  6. JHipster技术栈定制 - 基于UAA的微服务之间安全调用

    本文通过代码实例演示如何通过UAA实现微服务之间的安全调用. uaa: 身份认证服务,同时也作为被调用的资源服务.服务端口9999. microservice1: 调用uaa的消费者服务,服务端口80 ...

  7. 从服务之间的调用来看 我们为什么需要Dapr

    Dapr 相关的文章我已经写了20多篇了[1] . 当向其他人推荐Dapr 的时候,需要回答的一个问题就是: Dapr 似乎并不是特别令人印象深刻.它提供了一组"构建块",解决了与 ...

  8. 微服务~Eureka实现的服务注册与发现及服务之间的调用

    微服务里一个重要的概念就是服务注册与发现技术,当你有一个新的服务运行后,我们的服务中心可以感知你,然后把加添加到服务列表里,然后当你死掉后,会从服务中心把你移除,而你作为一个服务,对其它服务公开的只是 ...

  9. 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子

    上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...

随机推荐

  1. liunx 一些文件下载上传的命令

    xshell 上传 rz sftp 语法: put path/filename.txt Put -r path/文件夹 或者 put “path/filename.txt” Put -r “path/ ...

  2. java 根据html模板生成html文件

    1.代码部分 import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test. ...

  3. 3.开始使用Spring Cloud实战微服务

                     开始使用Spring Cloud实战微服务 3.1. Spring Cloud实战前提 3.1.1. 需要的技术储备 语言方面:可以使用Java.scala.Groo ...

  4. 忘记Linux 3.X/4.x/5.x 宝塔面板密码的解决方案

    进入ssh 输入以下命令重置密码(把命令最后面的   “testpasswd”  替换成你要改的新密码)注:若是debian/ubuntu用户,请使用有root权限的账户去执行这条命令 cd /www ...

  5. PHP 分页+查询

    首先是主页面,与上篇分页页面相同 <table width="100%" border="1" cellpadding="0" cel ...

  6. IDEA内存设置

    之前,博主的idea一直运行正常,今天,开发了一个接口,因为接口在项目中另一个模块,之前从来没有启动过这个模块,由于调试接口将该模块也放到tomcat容器中,结果我的idea不停崩溃,于是查看idea ...

  7. leetcode124二叉树最大路径和

    第一选择是将其转化成图用动态规划,但这样还是太麻烦 使用递归的思路,对于当前的节点root,分别求左右孩子到当前节点的单项路径权值最大的路径权值,然后记包含当前节点的路径权值为 path_price= ...

  8. tensorflow分布式运行

    1.知识点 """ 单机多卡:一台服务器上多台设备(GPU) 参数服务器:更新参数,保存参数 工作服务器:主要功能是去计算 更新参数的模式: 1.同步模型更新 2.异步模 ...

  9. SQL学习(六)select into:复制表信息

    在实际工作中,可能需要将一个表中的数据复制到另一个表中,以供后续使用.此时可以用关键字select into(在MySQL中不支持select into,可使用insert into...select ...

  10. mysql双主架构

    注意:最好不要用innodedb来同步数据库,要用databus来同步数据库,数据量大要用上mycat中间件 Mysql主主同步环境部署: centos 7.4 三台云主机: mysql1 :10.1 ...