你的位置:首页 > ASP.net教程

[ASP.net教程]第三章 微服务之间的调用 + 服务发现 + 负载均衡 + retrofit通信


微服务与微服务之间通信。

一、通信协议

  • 我们选用的通信协议是http,其实现工具是retrofit
    • 特点:实现简单,但是速度相较于tcp协议是慢一些
  • 如果对速度要求很高,可以使用tcp协议,实现产品可选用mina2/netty

 

二、服务路由

说明:不使用netflix的ribbon做服务路由,自己来实现。

实现思路:

  1. 拉取可用服务列表(服务发现)serverList
    • 缓存到本地guava cache中去,以后每隔10min从consulServer拉取一次(原本打算这样做),但是consulServer自己通过gossip协议会将服务数据广播给consulClient,每次获取服务器列表的时候,都是直接从本地agent(即consulClient)获取。
  2. 使用配置好的路由算法选出其中1台,执行逻辑

补充:当第二步执行逻辑超时或出错了,我们可以从剩下的机器中再选一台做,这样的失败重试可以通过"while(true)+局部计数器"的形式来实现。(这里暂时不做)

 

三、实现(由于代码改动比较大,列出完全版的代码)

  • framework:基本框架模块
  • myserviceA-server:myserviceA提供的服务逻辑
  • myserviceA-client:对server进行封装,提供接口,供其他微服务(service)或者gateway或者app使用
  • myserviceB-server:myserviceB提供的服务逻辑
  • myserviceB-client:同myserviceA-client

1、framework

1.1、pom.

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4  5   <modelVersion>4.0.0</modelVersion> 6  7   <parent> 8     <groupId>org.springframework.boot</groupId> 9     <artifactId>spring-boot-starter-parent</artifactId>10     <version>1.3.0.RELEASE</version>11   </parent>12 13   <groupId>com.microservice</groupId>14   <artifactId>framework</artifactId>15   <version>1.0-SNAPSHOT</version>16 17   <properties>18     <java.version>1.8</java.version><!-- 官方推荐 -->19   </properties>20 21   <!-- 引入实际依赖 -->22   <dependencies>23     <dependency>24       <groupId>org.springframework.boot</groupId>25       <artifactId>spring-boot-starter-web</artifactId>26     </dependency>27     <!-- consul-client -->28     <dependency>29       <groupId>com.orbitz.consul</groupId>30       <artifactId>consul-client</artifactId>31       <version>0.10.0</version>32     </dependency>33     <!-- consul需要的包 -->34     <dependency>35       <groupId>org.glassfish.jersey.core</groupId>36       <artifactId>jersey-client</artifactId>37       <version>2.22.2</version>38     </dependency>39     <dependency>40       <groupId>com.alibaba</groupId>41       <artifactId>fastjson</artifactId>42       <version>1.1.15</version>43     </dependency>44     <!-- 引入监控工具,包含health检查(用于consul注册) -->45     <dependency>46       <groupId>org.springframework.boot</groupId>47       <artifactId>spring-boot-starter-actuator</artifactId>48     </dependency>49     <!-- 引入lombok,简化pojo -->50     <dependency>51       <groupId>org.projectlombok</groupId>52       <artifactId>lombok</artifactId>53       <version>1.16.8</version>54       <scope>provided</scope>55     </dependency>56     <!-- 引入swagger2 -->57     <dependency>58       <groupId>io.springfox</groupId>59       <artifactId>springfox-swagger2</artifactId>60       <version>2.2.2</version>61     </dependency>62     <dependency>63       <groupId>io.springfox</groupId>64       <artifactId>springfox-swagger-ui</artifactId>65       <version>2.2.2</version>66     </dependency>67     <!-- retrofit -->68     <dependency>69       <groupId>com.squareup.retrofit</groupId>70       <artifactId>retrofit</artifactId>71       <version>1.9.0</version>72     </dependency>73   </dependencies>74 75   <build>76     <plugins>77       <plugin>78         <groupId>org.springframework.boot</groupId>79         <artifactId>spring-boot-maven-plugin</artifactId>80       </plugin>81     </plugins>82   </build>83 </project>

View Code

服务注册相关

1.2、com.microservice.com.microservice(启动类)

 1 package com.microservice; 2  3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5  6 import com.microservice.consul.ConsulRegisterListener; 7  8 import springfox.documentation.swagger2.annotations.EnableSwagger2; 9 10 /**11  * 注意:@SpringBootApplication该注解必须在SpringApplication.run()所在的类上12 */13 @SpringBootApplication14 @EnableSwagger215 public class MySpringAplication {16 17   public void run(String[] args) {18     SpringApplication sa = new SpringApplication(MySpringAplication.class);19     sa.addListeners(new ConsulRegisterListener());20     sa.run(args);21   }22 23   public static void main(String[] args) {24   }25 }

View Code

1.3、com.microservice.consul.ConsulConfig(构建consul单例实例)

 1 package com.microservice.consul; 2  3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5  6 import com.orbitz.consul.Consul; 7  8 @Configuration 9 public class ConsulConfig {10 11   @Bean12   public Consul consul(){13     return Consul.builder().build();14   }15 }

View Code

1.4、com.microservice.consul.ConsulProperties(读取服务注册信息)

 1 package com.microservice.consul; 2  3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.stereotype.Component; 5  6 import lombok.Getter; 7 import lombok.Setter; 8  9 @Component10 @Getter @Setter11 public class ConsulProperties {12 13   @Value("${service.name}")14   private String servicename;15   @Value("${service.port:8080}")16   private int servicePort;17   @Value("${service.tag:dev}")18   private String serviceTag;19   20   @Value("${health.url}")21   private String healthUrl;22   @Value("${health.interval:10}")23   private int healthInterval;24   25 }

View Code

1.5、com.microservice.consul.ConsulRegisterListener(ContextRefreshEvent监听器:用于服务启动注册

 1 package com.microservice.consul; 2  3 import java.net.MalformedURLException; 4 import java.net.URI; 5  6 import org.springframework.context.ApplicationListener; 7 import org.springframework.context.event.ContextRefreshedEvent; 8  9 import com.orbitz.consul.AgentClient;10 import com.orbitz.consul.Consul;11 12 /**13  * 监听contextrefresh事件14 */15 public class ConsulRegisterListener implements ApplicationListener<ContextRefreshedEvent> {16 17   @Override18   public void onApplicationEvent(ContextRefreshedEvent event) {19     Consul consul = event.getApplicationContext().getBean(Consul.class);20     ConsulProperties prop = event.getApplicationContext().getBean(ConsulProperties.class);21 22     AgentClient agentClient = consul.agentClient();23     try {24       agentClient.register(prop.getServicePort(), 25                  URI.create(prop.getHealthUrl()).toURL(),26                  prop.getHealthInterval(), 27                  prop.getServicename(), 28                 prop.getServicename(), // serviceId:29                  prop.getServiceTag());30     } catch (MalformedURLException e) {31       e.printStackTrace();32     }33   }34 35 }

View Code

负载均衡相关

1.6、com.microservice.loadBalancer.ServerAddress(服务器封装类)

 1 package com.microservice.loadBalancer; 2  3 import lombok.AllArgsConstructor; 4 import lombok.Getter; 5 import lombok.Setter; 6  7 /** 8  * 这里只做简单的封装,如果需要复杂的,可以使用java.net.InetAddress类 9 */10 @Getter @Setter11 @AllArgsConstructor12 public class ServerAddress {13   private String ip;14   private int port;15 }

View Code

1.7、com.microservice.loadBalancer.MyLoadBalancer(服务发现+负载均衡)

 1 package com.microservice.loadBalancer; 2  3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.Random; 6  7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Component; 9 10 import com.orbitz.consul.Consul;11 import com.orbitz.consul.HealthClient;12 import com.orbitz.consul.model.health.ServiceHealth;13 14 /**15  * 实现思路:16  * 1、拉取可用服务列表(服务发现)serverList17  * 2、缓存到本地guava cache中去,以后每隔10min从consulServer拉取一次(这里这样做的原因,是因为consul没有做这样的事)18  * 3、使用配置好的路由算法选出其中1台,执行逻辑19 */20 @Component21 public class MyLoadBalancer {22   23   @Autowired24   private Consul consul;25   26   /**27    * 获取被调服务的服务列表28    * @param serviceName 被调服务29   */30   public List<ServerAddress> getAvailableServerList(String serviceName){31     List<ServerAddress> availableServerList = new ArrayList<>();32     HealthClient healthClient = consul.healthClient();//获取Health http client33     List<ServiceHealth> availableServers = healthClient.getHealthyServiceInstances(serviceName).getResponse();//从本地agent查找所有可用节点34     availableServers.forEach(x->availableServerList.add(new ServerAddress(x.getNode().getAddress(), x.getService().getPort())));35     return availableServerList;36   } 37   38   /**39    * 选择一台服务器40    * 这里使用随机算法,如果需要换算法,我们可以抽取接口进行编写41   */42   public ServerAddress chooseServer(String serviceName){43     List<ServerAddress> servers = getAvailableServerList(serviceName);44     Random random = new Random();45     int index = random.nextInt(servers.size());46     return servers.get(index);47   }48   49 }

View Code

说明:

  • 在该类中提供了服务发现的方法;
  • 提供了一个负载均衡算法(随机算法)

retrofit通信相关

1.8、com.microservice.retrofit(通信类)

 1 package com.microservice.retrofit; 2  3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Component; 5  6 import com.microservice.loadBalancer.MyLoadBalancer; 7 import com.microservice.loadBalancer.ServerAddress; 8  9 import retrofit.RestAdapter;10 11 @Component12 public class RestAdapterConfig {13 14   @Autowired15   private MyLoadBalancer myLoadBalancer;16   17   /**18    * 负载均衡并且创建传入的API接口实例19   */20   public <T> T create(Class<T> tclass, String serviceName) {21     ServerAddress server = myLoadBalancer.chooseServer(serviceName);22     String endPointUrl = new StringBuilder("http://").append(server.getIp())23                             .append(":")24                              .append(server.getPort()).toString(); 25     RestAdapter adapter = new RestAdapter.Builder()26         .setEndpoint(endPointUrl).build();27     T tclassInstance = adapter.create(tclass);28     return tclassInstance;29   }30 }

View Code

说明:该类定义了一个泛型方法,在该方法中:首先根据负载均衡选择一台服务器,之后使用RestAdapter指定访问的服务器,最后创建出相应的serviceClientAPI接口的实例(具体看下边)

注意:我们可以设置retrofit使用的http的client,下一节会介绍。

 

2、myserviceA

pom.(总pom,引入上边的framework基础框架)

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4  5   <modelVersion>4.0.0</modelVersion> 6  7   <parent> 8     <groupId>org.springframework.boot</groupId> 9     <artifactId>spring-boot-starter-parent</artifactId>10     <version>1.3.0.RELEASE</version>11   </parent>12 13   <groupId>com.microservice</groupId>14   <artifactId>myserviceA</artifactId>15   <version>1.0-SNAPSHOT</version>16   <packaging>pom</packaging>17 18   <properties>19     <java.version>1.8</java.version><!-- 官方推荐 -->20   </properties>21 22   <modules>23     <module>server</module>24     <module>client</module>25   </modules>26 27   <!-- 引入实际依赖 -->28   <dependencies>29     <dependency>30       <groupId>org.springframework.boot</groupId>31       <artifactId>spring-boot-starter-web</artifactId>32     </dependency>33     <dependency>34       <groupId>com.microservice</groupId>35       <artifactId>framework</artifactId>36       <version>1.0-SNAPSHOT</version>37     </dependency>38   </dependencies>39 </project>

View Code

2.1、myserviceA-server

2.1.1、pom.

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4  5   <modelVersion>4.0.0</modelVersion> 6    7   <parent> 8     <groupId>com.microservice</groupId> 9     <artifactId>myserviceA</artifactId>10     <version>1.0-SNAPSHOT</version>11   </parent>12 13   <artifactId>myserviceA-server</artifactId>14 15   <!-- 引入实际依赖 -->16   <dependencies>17     <dependency>18       <groupId>com.alibaba</groupId>19       <artifactId>fastjson</artifactId>20       <version>1.1.15</version>21     </dependency>22   </dependencies>23 24   <build>25     <plugins>26       <plugin>27         <groupId>org.springframework.boot</groupId>28         <artifactId>spring-boot-maven-plugin</artifactId>29       </plugin>30     </plugins>31   </build>32 </project>

View Code

2.1.2、com.microservice.myserviceA.MyServiceAApplication

 1 package com.microservice.myserviceA; 2  3 import org.springframework.boot.autoconfigure.SpringBootApplication; 4  5 import com.microservice.MySpringAplication; 6  7 @SpringBootApplication 8 public class MyServiceAApplication { 9 10   public static void main(String[] args) {11     MySpringAplication mySpringAplication = new MySpringAplication();12     mySpringAplication.run(args);13   }14 }

View Code

2.1.3、com.microservice.myserviceA.controller.MyserviceAController

 1 package com.microservice.myserviceA.controller; 2  3 import org.springframework.web.bind.annotation.PathVariable; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 import org.springframework.web.bind.annotation.RestController; 7  8 import io.swagger.annotations.Api; 9 import io.swagger.annotations.ApiImplicitParam;10 import io.swagger.annotations.ApiImplicitParams;11 import io.swagger.annotations.ApiOperation;12 13 @Api("Myservice API")14 @RestController15 @RequestMapping("/myserviceA")16 public class MyserviceAController {17 18   @ApiOperation("sayHelloToSomeone")19   @ApiImplicitParams({ @ApiImplicitParam(name = "username", paramType = "path", dataType = "String") })20   @RequestMapping(value = "/hello/{username}", method = RequestMethod.GET)21   public String sayHello(@PathVariable("username") String username) {22     return username;23   }24 25 }

View Code

2.1.4、application.properties

1 service.name=myserviceA2 service.port=80803 service.tag=dev4 health.url=http://localhost:8080/health5 health.interval=10

View Code

 

2.2、myserviceA-client

2.2.1、pom.

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4   <modelVersion>4.0.0</modelVersion> 5   <parent> 6     <groupId>com.microservice</groupId> 7     <artifactId>myserviceA</artifactId> 8     <version>1.0-SNAPSHOT</version> 9   </parent>10 11   <artifactId>myserviceA-client</artifactId>12   <packaging>jar</packaging>13 </project>

View Code

2.2.2、com.microservice.myserviceA.api.MyserviceAAPI

1 package com.microservice.myserviceA.api;2 3 import retrofit.http.GET;4 import retrofit.http.Path;5 6 public interface MyserviceAAPI {7   @GET("/myserviceA/hello/{username}")8   public String sayHello(@Path("username") String username);9 }

View Code

2.2.3、com.microservice.myserviceA.client.MyserviceAClient

 1 package com.microservice.myserviceA.client; 2  3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Component; 5  6 import com.microservice.myserviceA.api.MyserviceAAPI; 7 import com.microservice.retrofit.RestAdapterConfig; 8  9 @Component10 public class MyserviceAClient {11 12   @Autowired13   private RestAdapterConfig restAdapterConfig;14 15   // @Value("${service.name}")16   // private String servicename;//这一块儿是要从consul中去读的17 18   public String sayHello(String username) {19     MyserviceAAPI myserviceAAPI = restAdapterConfig.create(MyserviceAAPI.class, "myserviceA");20     return myserviceAAPI.sayHello(username);21   }22 23 }

View Code

注意:这里我硬编码了servicename为"myserviceA",该值在myserviceA-server的application.properties文件中配置过,但是由于myserviceA-client无法读取myserviceA-server的application.properties文件(因为并没有将myserviceA-server打成jar传给myserviceA-client)。有三种方案解决这个问题:

  • 将myserviceA-server打成jar传给myserviceA-client(不知道为什么没人这样用)
  • 建立myserviceA-common模块,做成全局常量,之后将该common模块打成jar传给server和client模块
    • common模块在每个项目中都应该有的,用于存储一些server和client都需要使用的domain和常量等(这些domain通常是一些controller的返回类型)
  • 直接将配置信息传入consul(配置信息统一管理),server和client都可以直接从consul读取(这是微服务中的一个配置集中管理,之后会集中说)


3、myserviceB

pom.(总pom,引入上边的framework基础框架)

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4  5   <modelVersion>4.0.0</modelVersion> 6  7   <parent> 8     <groupId>org.springframework.boot</groupId> 9     <artifactId>spring-boot-starter-parent</artifactId>10     <version>1.3.0.RELEASE</version>11   </parent>12 13   <groupId>com.microservice</groupId>14   <artifactId>myserviceB</artifactId>15   <version>1.0-SNAPSHOT</version>16   <packaging>pom</packaging>17 18   <properties>19     <java.version>1.8</java.version><!-- 官方推荐 -->20   </properties>21 22   <modules>23     <module>server</module>24     <module>client</module>25   </modules>26 27   <!-- 引入实际依赖 -->28   <dependencies>29     <dependency>30       <groupId>org.springframework.boot</groupId>31       <artifactId>spring-boot-starter-web</artifactId>32     </dependency>33     <dependency>34       <groupId>com.microservice</groupId>35       <artifactId>framework</artifactId>36       <version>1.0-SNAPSHOT</version>37     </dependency>38   </dependencies>39 </project>

View Code

3.1、myserviceB-server

3.1.1、pom.(引入myserviceA-client.jar)

 1 <??> 2 <project ="http://maven.apache.org/POM/4.0.0" ="http://www.w3.org/2001/ 3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4  5   <modelVersion>4.0.0</modelVersion> 6  7   <parent> 8     <groupId>com.microservice</groupId> 9     <artifactId>myserviceB</artifactId>10     <version>1.0-SNAPSHOT</version>11   </parent>12 13   <artifactId>myserviceB-server</artifactId>14 15   <!-- 引入实际依赖 -->16   <dependencies>17     <dependency>18       <groupId>com.alibaba</groupId>19       <artifactId>fastjson</artifactId>20       <version>1.1.15</version>21     </dependency>22     <!-- 引入myserviceA-client -->23     <dependency>24       <groupId>com.microservice</groupId>25       <artifactId>myserviceA-client</artifactId>26       <version>1.0-SNAPSHOT</version>27     </dependency>28   </dependencies>29 30   <build>31     <plugins>32       <plugin>33         <groupId>org.springframework.boot</groupId>34         <artifactId>spring-boot-maven-plugin</artifactId>35       </plugin>36     </plugins>37   </build>38 </project>

View Code

3.1.2、com.microservice.myserviceB.MyServiceBApplication

 1 package com.microservice.myserviceB; 2  3 import org.springframework.boot.autoconfigure.SpringBootApplication; 4  5 import com.microservice.MySpringAplication; 6  7 @SpringBootApplication 8 public class MyServiceBApplication { 9 10   public static void main(String[] args) {11     MySpringAplication mySpringAplication = new MySpringAplication();12     mySpringAplication.run(args);13   }14 }

View Code

3.1.3、com.microservice.myserviceB.config.MyServiceBConfig(构建MyserviceAClient的单例)

 1 package com.microservice.myserviceB.config; 2  3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5  6 import com.microservice.myserviceA.client.MyserviceAClient; 7  8 @Configuration 9 public class MyServiceBConfig {10 11   @Bean12   public MyserviceAClient myserviceAClient(){13     return new MyserviceAClient();14   }15 }

View Code

3.1.4、com.microservice.myserviceB.controller.MyserviceBController

 1 package com.microservice.myserviceB.controller; 2  3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.PathVariable; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestMethod; 7 import org.springframework.web.bind.annotation.RestController; 8  9 import com.microservice.myserviceA.client.MyserviceAClient;10 11 import io.swagger.annotations.Api;12 import io.swagger.annotations.ApiImplicitParam;13 import io.swagger.annotations.ApiImplicitParams;14 import io.swagger.annotations.ApiOperation;15 16 @Api("MyserviceB API")17 @RestController18 @RequestMapping("/myserviceB")19 public class MyserviceBController {20 21   @Autowired22   private MyserviceAClient myserviceAClient;23 24   @ApiOperation("调用myServiceA的client(实现微服务之间的调用)")25   @ApiImplicitParams({ @ApiImplicitParam(name = "username", paramType = "path", dataType = "String") })26   @RequestMapping(value = "/hello/{username}", method = RequestMethod.GET)27   public String sayHello(@PathVariable("username") String username) {28     return myserviceAClient.sayHello(username);29   }30 }

View Code

3.1.5、application.properties

1 service.name=myserviceB2 service.port=80813 service.tag=dev4 health.url=http://localhost:8080/health5 health.interval=106 7 server.port=8081

View Code

说明:除了制定服务注册信息之外,还添加了一个"server.port"属性,避免port冲突导致服务无法启动(仅仅用于测试的时候,因为是一台机器,在线上的话,一般都是用8080默认端口就好了)

3.2、myserviceB-client

3.2.1、pom.

 1 <? 2 <project  3   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4   <modelVersion>4.0.0</modelVersion> 5   <parent> 6     <groupId>com.microservice</groupId> 7     <artifactId>myserviceB</artifactId> 8     <version>1.0-SNAPSHOT</version> 9   </parent>10 11   <artifactId>myserviceB-client</artifactId>12   <packaging>jar</packaging>13 </project>

View Code

 

测试:

  1. 启动consul
  2. 启动myserviceA-server和myserviceB-server(此时,这两个微服务注册到了consul)
  3. 启动swagger,对myserviceB-server进行访问(即可)

 

至此,微服务之间的调用 + 服务发现 + 负载均衡 + retrofit通信完成!!!