标签:
上一节,描述了服务发现、负载均衡以及服务之间的调用。到这里,加上第二节的服务注册,整个微服务的架构就已经搭建出来了,即功能性需求就完成了。从本节开始的记录其实全部都是非功能性需求。
一、集群容错
技术选型:hystrix。(就是上图中熔断器)
熔断的作用:
第一个作用:
假设有两台服务器server1(假设可以处理的请求阈值是1W请求)和server2,在server1上注册了三个服务service1、service2、service3,在server2上注册了一个服务service4,假设service4服务响应缓慢,service1调用service4时,一直在等待响应,那么在高并发下,很快的server1处很快就会达到请求阈值(server1很快就会耗尽处理线程)之后可能宕机,这时候,不只是service1不再可用,server1上的service2和service3也不可用了。
如果我们引入了hystrix,那么service1调用service4的时候,当发现service4超时,立即断掉不再执行,执行getFallback逻辑。这样的话,server1就不会耗尽处理线程,server1上的其他服务也是可用的。当然,这是在合理的配置了超时时间的情况下,如果超时时间设置的太长的话,还是会出现未引入hystrix之前的情况。
第二个作用:
当被调服务经常失败,比如说在10min(可配)中之内调用了20次,失败了15次(可配),那么我们认为这个服务是失败的,先关闭该服务,等一会儿后再自动重新启动该服务!(这是真正的熔断!)
二、实现
由于代码变动比较大,我会列出全部关键代码。
1、framework
1.1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 </dependency> 55 <!-- 引入swagger2 --> 56 <dependency> 57 <groupId>io.springfox</groupId> 58 <artifactId>springfox-swagger2</artifactId> 59 <version>2.2.2</version> 60 </dependency> 61 <dependency> 62 <groupId>io.springfox</groupId> 63 <artifactId>springfox-swagger-ui</artifactId> 64 <version>2.2.2</version> 65 </dependency> 66 <!-- retrofit --> 67 <dependency> 68 <groupId>com.squareup.retrofit</groupId> 69 <artifactId>retrofit</artifactId> 70 <version>1.9.0</version> 71 </dependency> 72 <!-- converter-jackson --> 73 <dependency> 74 <groupId>com.squareup.retrofit</groupId> 75 <artifactId>converter-jackson</artifactId> 76 <version>1.9.0</version> 77 </dependency> 78 <!-- okhttp --> 79 <dependency> 80 <groupId>com.squareup.okhttp</groupId> 81 <artifactId>okhttp</artifactId> 82 <version>2.4.0</version> 83 </dependency> 84 <!-- hystrix --> 85 <dependency> 86 <groupId>com.netflix.hystrix</groupId> 87 <artifactId>hystrix-core</artifactId> 88 <version>1.5.3</version> 89 </dependency> 90 <dependency> 91 <groupId>com.netflix.hystrix</groupId> 92 <artifactId>hystrix-metrics-event-stream</artifactId> 93 <version>1.5.3</version> 94 </dependency> 95 </dependencies> 96 97 <build> 98 <plugins> 99 <plugin> 100 <groupId>org.springframework.boot</groupId> 101 <artifactId>spring-boot-maven-plugin</artifactId> 102 </plugin> 103 </plugins> 104 </build> 105 </project>
1.2、启动类
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 @SpringBootApplication 14 @EnableSwagger2 15 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 }
1.3、服务注册(consul包)
1.3.1、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 @Component 10 @Getter @Setter 11 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 @Value("${health.url}") 20 private String healthUrl; 21 @Value("${health.interval:10}") 22 private int healthInterval; 23 24 }
1.3.2、ConsulConfig
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 @Bean 12 public Consul consul(){ 13 return Consul.builder().build(); 14 } 15 }
1.3.3、ConsulRegisterListener
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 @Override 18 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 }
1.4、服务发现+负载均衡(loadBalance包)
1.4.1、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 @Setter 11 @AllArgsConstructor 12 public class ServerAddress { 13 private String ip; 14 private int port; 15 }
1.4.2、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、拉取可用服务列表(服务发现)serverList 17 * 2、缓存到本地guava cache中去,以后每隔10min从consulServer拉取一次(这里这样做的原因,是因为consul没有做这样的事) 18 * 3、使用配置好的路由算法选出其中1台,执行逻辑 19 */ 20 @Component 21 public class MyLoadBalancer { 22 23 @Autowired 24 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 client 33 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 }
以上代码均与第三节一样。这的负载均衡之后会用ribbon来做。
1.5、服务通信(retrofit)+集群容错(hystrix)
注意:这里我先给出代码,最后我会好好的说一下调用流程。
1.5.1、RestAdapterConfig
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 import retrofit.converter.JacksonConverter; 11 12 @Component 13 public class RestAdapterConfig { 14 15 @Autowired 16 private MyLoadBalancer myLoadBalancer; 17 18 /** 19 * 负载均衡并且创建传入的API接口实例 20 */ 21 public <T> T create(Class<T> tclass, String serviceName) { 22 String commandGroupKey = tclass.getSimpleName();// 获得简单类名作为groupKey 23 24 ServerAddress server = myLoadBalancer.chooseServer(serviceName);// 负载均衡 25 RestAdapter restAdapter = new RestAdapter.Builder() 26 .setConverter(new JacksonConverter()) 27 .setErrorHandler(new MyErrorHandler()) 28 .setClient(new MyHttpClient(server, commandGroupKey)) 29 .setEndpoint("/").build(); 30 T tclassInstance = restAdapter.create(tclass); 31 return tclassInstance; 32 } 33 }
说明:这里我们定义了自己的retrofit.Client和自己的retrofit.ErrorHandler
1.5.2、MyHttpClient(自定义retrofit的Client)
1 package com.microservice.retrofit; 2 3 import java.io.IOException; 4 5 import com.microservice.hystrix.HttpHystrixCommand; 6 import com.microservice.loadBalancer.ServerAddress; 7 import com.netflix.hystrix.HystrixCommand.Setter; 8 import com.netflix.hystrix.HystrixCommandGroupKey; 9 import com.netflix.hystrix.HystrixCommandProperties; 10 11 import retrofit.client.Client; 12 import retrofit.client.Request; 13 import retrofit.client.Response; 14 15 public class MyHttpClient implements Client { 16 private ServerAddress server; 17 private String commandGroupKey; 18 private int hystrixTimeoutInMillions = 3000;// 这里暂且将数据硬编码在这里(之后会改造) 19 20 public MyHttpClient(ServerAddress server, String commandGroupKey) { 21 this.server = server; 22 this.commandGroupKey = commandGroupKey; 23 } 24 25 @Override 26 public Response execute(Request request) throws IOException { 27 Setter setter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(commandGroupKey)); 28 setter.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(hystrixTimeoutInMillions)); 29 return new HttpHystrixCommand(setter, server, request).execute();// 同步执行 30 } 31 }
说明:在execute()中引入了hystrix
1.5.3、HttpHystrixCommand(hystrix核心类)
1 package com.microservice.hystrix; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 9 import com.microservice.loadBalancer.ServerAddress; 10 import com.microservice.retrofit.RequestBodyWrapper; 11 import com.microservice.retrofit.ResponseBodyWrapper; 12 import com.netflix.hystrix.HystrixCommand; 13 import com.squareup.okhttp.Headers; 14 import com.squareup.okhttp.OkHttpClient; 15 import com.squareup.okhttp.Request.Builder; 16 17 import retrofit.client.Header; 18 import retrofit.client.Request; 19 import retrofit.client.Response; 20 21 public class HttpHystrixCommand extends HystrixCommand<Response> { 22 private static final Logger LOGGER = LoggerFactory.getLogger(HttpHystrixCommand.class); 23 24 private ServerAddress server; 25 private Request request; 26 private String requestUrl; 27 28 public HttpHystrixCommand(Setter setter, ServerAddress server, Request request) { 29 super(setter); 30 this.server = server; 31 this.request = request; 32 } 33 34 @Override 35 public Response run() throws Exception { 36 com.squareup.okhttp.Request okReq = retroReq2okReq(request, server);// 将retrofit类型的request转化为okhttp类型的request 37 com.squareup.okhttp.Response okRes = new OkHttpClient().newCall(okReq).execute(); 38 return okResToRetroRes(okRes);// 将okhttp的response转化为retrofit的response 39 } 40 41 public com.squareup.okhttp.Request retroReq2okReq(Request request, ServerAddress server) { 42 Builder requestBuilder = new Builder(); 43 /***********************1、将retrofit的header转化为ok的header*************************/ 44 List<Header> headers = request.getHeaders(); 45 Headers.Builder okHeadersBulder = new Headers.Builder(); 46 headers.forEach(x -> okHeadersBulder.add(x.getName(), x.getValue())); 47 requestBuilder.headers(okHeadersBulder.build()); 48 /***********************2、根据之前负载均衡策略选出的机器构建访问URL*************************/ 49 String url = new StringBuilder("http://").append(server.getIp()).append(":").append(server.getPort()) 50 .append("/").append(request.getUrl()).toString(); 51 requestUrl = url; 52 requestBuilder.url(url); 53 /***********************3、构造方法请求类型和请求体(GET是没有请求体的,这里就是null)**********/ 54 requestBuilder.method(request.getMethod(), new RequestBodyWrapper(request.getBody())); 55 return requestBuilder.build(); 56 } 57 58 public Response okResToRetroRes(com.squareup.okhttp.Response okRes) { 59 return new Response(okRes.request().urlString(), 60 okRes.code(), 61 okRes.message(), 62 getHeaders(okRes.headers()), 63 new ResponseBodyWrapper(okRes.body())); 64 } 65 66 private List<Header> getHeaders(Headers okHeaders) { 67 List<Header> retrofitHeaders = new ArrayList<>(); 68 int count = okHeaders.size(); 69 for (int i = 0; i < count; i++) { 70 retrofitHeaders.add(new Header(okHeaders.name(i), okHeaders.value(i))); 71 } 72 return retrofitHeaders; 73 } 74 75 /** 76 * 超时后的一些操作,或者如果缓存中有信息,可以从缓存中拿一些,具体的要看业务,也可以打一些logger TODO 这里调用失败了 77 */ 78 @Override 79 public Response getFallback() { 80 LOGGER.error("请求超时了!requestUrl:‘{}‘", requestUrl); 81 /** 82 * 想要让自定义的ErrorHandler起作用以及下边的404和reason有意义,就一定要配置requestUrl和List<header> 83 * 其实这里可以看做是定义自定义异常的状态码和状态描述 84 * 其中状态码用于自定义异常中的判断(见HystrixRuntimeException) 85 */ 86 return new Response(requestUrl, 87 404, //定义状态码 88 "execute getFallback because execution timeout",//定义消息 89 new ArrayList<Header>(),null); 90 } 91 }
说明:首先调用run(),run()失败或超时候调用getFallback()
1.5.4、MyErrorHandler(自定义retrofit的错误处理器)
1 package com.microservice.retrofit; 2 3 import com.microservice.exception.HystrixRuntimeException; 4 5 import retrofit.ErrorHandler; 6 import retrofit.RetrofitError; 7 import retrofit.client.Response; 8 9 public class MyErrorHandler implements ErrorHandler{ 10 @Override 11 public Throwable handleError(RetrofitError cause) { 12 Response response = cause.getResponse(); 13 /** 14 * 这里是一个可以定制的地方,自己可以定义所有想要捕获的异常 15 */ 16 if(response!=null && response.getStatus()==404){ 17 return new HystrixRuntimeException(cause); 18 } 19 return cause; 20 } 21 }
说明:当发生了retrofit.error时(不只是上边的getFallback()返回的Response),我们可以在该ErrorHandler的handleError方法来进行相应Response的处理。这里我们指定当404时返回一个自定义异常。
1.5.5、HystrixRuntimeException(自定义异常)
1 package com.microservice.exception; 2 3 /** 4 * 自定义异常 5 */ 6 public class HystrixRuntimeException extends RuntimeException { 7 private static final long serialVersionUID = 8252124808929848902L; 8 9 public HystrixRuntimeException(Throwable cause) { 10 super(cause);//只有这样,才能将异常信息抛给客户端 11 } 12 }
说明:自定义异常只能通过super()来向客户端抛出自己指定的异常信息(上边的Response的reason,但是抛到客户端时还是一个500错误,因为run()错误或超时就是一个服务端错误)。
1.5.6、RequestBodyWrapper(将TypedOutput转化成RequestBody的工具类)
1 package com.microservice.retrofit; 2 3 import java.io.IOException; 4 5 import com.squareup.okhttp.MediaType; 6 import com.squareup.okhttp.RequestBody; 7 8 import okio.BufferedSink; 9 import retrofit.mime.TypedOutput; 10 11 /** 12 * 将TypedOutput转为RequestBody,并设置mime 13 */ 14 public class RequestBodyWrapper extends RequestBody{ 15 private final TypedOutput wrapped; 16 17 public RequestBodyWrapper(TypedOutput body) { 18 this.wrapped = body; 19 } 20 21 /** 22 * 首先获取retrofit中的request请求的mime类型,即Content-Type, 23 * 如果为null,就返回application/json 24 */ 25 @Override 26 public MediaType contentType() { 27 if (wrapped != null && wrapped.mimeType() != null) { 28 return MediaType.parse(wrapped.mimeType()); 29 } 30 return MediaType.parse("application/json; charset=UTF-8"); 31 } 32 33 /** Writes the content of this request to {@code out}. */ 34 @Override 35 public void writeTo(BufferedSink sink) throws IOException { 36 if (wrapped != null) { 37 wrapped.writeTo(sink.outputStream()); 38 } 39 } 40 }
说明:该方法是将TypedOutput转化成RequestBody的工具类(用于在将retrofit.Request转化为okhttp.Request的时候的请求方法体的封装)
1.5.7、ResponseBodyWrapper(将ResponseBody转化为TypedInput的工具类)
1 package com.microservice.retrofit; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 6 import com.squareup.okhttp.ResponseBody; 7 8 import retrofit.mime.TypedInput; 9 10 public class ResponseBodyWrapper implements TypedInput { 11 private final ResponseBody wrapped; 12 13 public ResponseBodyWrapper(ResponseBody body) { 14 this.wrapped = body; 15 } 16 17 /** Returns the mime type. */ 18 @Override 19 public String mimeType() { 20 return wrapped.contentType().type(); 21 } 22 23 /** Length in bytes. Returns {@code -1} if length is unknown. */ 24 @Override 25 public long length() { 26 try { 27 return wrapped.contentLength(); 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 return 0; 32 } 33 34 /** 35 * Read bytes as stream. 36 */ 37 @Override 38 public InputStream in() throws IOException { 39 return wrapped.byteStream(); 40 } 41 }
说明:该方法是将ResponseBody转化为TypedInput的工具类(用于在讲okhttp.Response转化为retrofit.Response的时候响应体的封装)
小结:
整个流程:
当myserviceB调用myserviceA的一个方法时,首先会执行自定义的MyHttpClient的execute()方法,在该execute()方法中我们执行了自定义的HttpHystrixCommand的execute()方法,此时就会执行执行HttpHystrixCommand的run()方法,如果该方法运行正常并在超时时间内返回数据,则调用结束。
如果run()方法调用失败或该方法超时,就会直接运行HttpHystrixCommand的getFallback()方法。该方法返回一个retrofit.Response对象,该对象的status是404,错误信息也是自定义的。之后该对象会被包装到RetrofitError对象中,之后RetrofitError对象会由MyErrorHandler的handleError()进行处理:从RetrofitError对象中先取出Response,之后根据该Response的status执行相应的操作,我们这里对404的情况定义了一个自定义异常HystrixRuntimeException。
注意点:
2、myserviceA
2.1、myserviceA-server
2.1.1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
2.1.2、application.properties
1 service.name=myserviceA 2 service.port=8080 3 service.tag=dev 4 health.url=http://localhost:8080/health 5 health.interval=10
2.1.3、启动类
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 }
2.1.4、Province(构造返回的模型类)
1 package com.microservice.myserviceA.model; 2 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 5 import lombok.AllArgsConstructor; 6 import lombok.Getter; 7 import lombok.NoArgsConstructor; 8 import lombok.Setter; 9 10 /** 11 * 省 12 */ 13 @Getter 14 @Setter 15 @NoArgsConstructor 16 @AllArgsConstructor 17 @JsonIgnoreProperties(ignoreUnknown = true) 18 public class Province { 19 private int id; 20 private String provinceName;// 省份名称 21 private long personNum; // 人口数量 22 }
说明:实际上一些返回值的模型类一般不仅会在server中用到,也会在client中用到。所以一般我们还会建立一个myserviceA-common模块,该模块专门用于存放server和client公共用到的一些东西。
2.1.5、MyserviceAController
1 package com.microservice.myserviceA.controller; 2 3 import org.apache.commons.lang3.builder.ToStringBuilder; 4 import org.apache.commons.lang3.exception.ExceptionUtils; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.web.bind.annotation.PathVariable; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.RequestMethod; 10 import org.springframework.web.bind.annotation.RequestParam; 11 import org.springframework.web.bind.annotation.RestController; 12 13 import com.microservice.myserviceA.model.Province; 14 15 import io.swagger.annotations.Api; 16 import io.swagger.annotations.ApiImplicitParam; 17 import io.swagger.annotations.ApiImplicitParams; 18 import io.swagger.annotations.ApiOperation; 19 20 @Api("Myservice API") 21 @RestController 22 @RequestMapping("/myserviceA") 23 public class MyserviceAController { 24 private static final Logger LOGGER = LoggerFactory.getLogger(MyserviceAController.class); 25 26 @ApiOperation("创建省份信息并返回") 27 @ApiImplicitParams({ @ApiImplicitParam(name = "provincename", paramType = "path", dataType = "String") }) 28 @RequestMapping(value = "/provinces/{provincename}", method = RequestMethod.POST) 29 public Province getProvinceByCityName(@PathVariable("provincename") String provincename, 30 @RequestParam("personNum") long personNum) { 31 long startTime = System.currentTimeMillis(); 32 LOGGER.info("start - MyserviceAController:getProvinceByCityName,provincename:‘{}‘,personNum:‘{}‘", provincename, personNum); 33 try { 34 Thread.sleep(5000); 35 Province province = new Province(1, provincename, personNum); 36 LOGGER.info("end - MyserviceAController:getProvinceByCityName,province:‘{}‘,耗时:‘{}‘", ToStringBuilder.reflectionToString(province),System.currentTimeMillis()-startTime); 37 return province; 38 } catch (Exception e) { 39 LOGGER.error(ExceptionUtils.getStackTrace(e)); 40 return new Province(); 41 } 42 } 43 }
说明:注意该controller值目前为止最标准的写法。
2.2、myserviceA-client
2.2.1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
2.2.2、Province(构造返回的模型类)
1 package com.microservice.myserviceA.model; 2 3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 5 import lombok.AllArgsConstructor; 6 import lombok.Getter; 7 import lombok.NoArgsConstructor; 8 import lombok.Setter; 9 10 /** 11 * 省 12 */ 13 @Getter 14 @Setter 15 @NoArgsConstructor 16 @AllArgsConstructor 17 @JsonIgnoreProperties(ignoreUnknown = true) 18 public class Province { 19 private int id; 20 private String provinceName;// 省份名称 21 private long personNum; // 人口数量 22 }
说明:
2.2.3、MyserviceAAPI
1 package com.microservice.myserviceA.api; 2 3 import com.microservice.myserviceA.model.Province; 4 5 import retrofit.http.POST; 6 import retrofit.http.Path; 7 import retrofit.http.Query; 8 9 public interface MyserviceAAPI { 10 @POST("/myserviceA/provinces/{provincename}") 11 public Province getProvinceByCityName(@Path("provincename") String provincename, 12 @Query("personNum") long personNum); 13 }
2.2.4、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.myserviceA.model.Province; 8 import com.microservice.retrofit.RestAdapterConfig; 9 10 @Component 11 public class MyserviceAClient { 12 13 @Autowired 14 private RestAdapterConfig restAdapterConfig; 15 16 public Province getProvinceByCityName(String provincename, long personNum) { 17 MyserviceAAPI myserviceAAPI = restAdapterConfig.create(MyserviceAAPI.class, "myserviceA"); 18 return myserviceAAPI.getProvinceByCityName(provincename, personNum); 19 } 20 21 }
3、myserviceB
3.1、myserviceB-server
3.1.1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
3.1.2、application.properties
1 service.name=myserviceB 2 service.port=8081 3 service.tag=dev 4 health.url=http://localhost:8080/health 5 health.interval=10 6 7 server.port=8081
3.1.3、启动类
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 }
3.1.4、MyServiceBConfig
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 @Bean 12 public MyserviceAClient myserviceAClient(){ 13 return new MyserviceAClient(); 14 } 15 }
3.1.5、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.RequestParam; 8 import org.springframework.web.bind.annotation.RestController; 9 10 import com.microservice.myserviceA.client.MyserviceAClient; 11 import com.microservice.myserviceA.model.Province; 12 13 import io.swagger.annotations.Api; 14 import io.swagger.annotations.ApiOperation; 15 16 @Api("MyserviceB API") 17 @RestController 18 @RequestMapping("/myserviceB") 19 public class MyserviceBController { 20 21 @Autowired 22 private MyserviceAClient myserviceAClient; 23 24 @ApiOperation("调用myServiceA的client(实现微服务之间的调用)") 25 @RequestMapping(value = "/provinces/{provincename}", method = RequestMethod.POST) 26 public Province getProvinceByCityName(@PathVariable("provincename") String provincename, 27 @RequestParam("personNum") long personNum) { 28 Province provinceInfo = myserviceAClient.getProvinceByCityName(provincename, personNum); 29 return provinceInfo; 30 } 31 }
最后,启动consul,启动服务,swagger测试就好了!!!
标签:
原文地址:http://www.cnblogs.com/java-zhao/p/5595157.html