顺便给你们推荐一个Java技术交流群:908676731,里面会分享一些资深架构师录制的视频资料:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多!java
假设有2个微服务A和B分别在端点http:// localhost:8181 /和http:// localhost:8282 /上运行,若是想要在A服务中调用B服务,那么咱们须要在A服务中键入B服务的url,这个url是负载均衡器分配给咱们的,包括负载平衡后的IP地址,那么很显然,B服务与这个URL硬编码耦合在一块儿了,若是咱们使用了服务自动注册机制,就可使用B服务的逻辑ID,而不是使用特定IP地址和端口号来调用服务。git
咱们可使用Netflix Eureka Server建立Service Registry服务器,并将咱们的微服务同时做为Eureka客户端,这样一旦咱们启动微服务,它将自动使用逻辑服务ID向Eureka Server注册。而后,其余微服务(一样也是Eureka客户端)就可使用服逻辑务ID来调用REST端点服务了。github
Spring Cloud使用Load Balanced RestTemplate建立Service Registry并发现其余服务变得很是容易。web
除了使用Netflix Eureka Server做为服务发现,也可使用Zookeeper,可是根据CAP定理,在须要P网络分区容忍性状况下,强一致性C和高可用性A只能选择一个,Zookeeper是属于CP,而Eureka是属于AP,在服务发现方面,高可用性才是更重要,不然没法完成服务之间调用,而服务信息是否一致则不是最重要,A服务发现B服务时,B服务信息没有及时更新,可能发生调用错误,可是调用错误总比没法链接到服务注册中心要强。不然,服务注册中心就成为整个系统的单点故障,存在极大的单点风险,这是咱们为何须要分布式系统的首要缘由。spring
让咱们使用Netflix Eureka建立一个Service Registry,它只是一个带有Eureka Server启动器的SpringBoot应用程序。数据库
使用Intellij的Idea开发工具是很是容易启动Spring cloud的:性能优化
能够从https://start.spring.io/网址,选择相应组件便可。服务器
因为咱们须要创建一个注册服务器,所以选择Eureka Server组件便可,经过这些自动工具其实是能自动生成Maven的配置:网络
<dependency>架构
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
咱们须要给SpringBoot启动类添加@EnableEurekaServer注释,以使咱们的SpringBoot应用程序成为基于Eureka Server的Service Registry。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
默认状况下,每一个Eureka服务器也是Eureka客户端,客户端必定会须要一个服务器URL来定位,不然就会不断报错,因为咱们只有一个Eureka Server节点(独立模式),咱们将经过在application.properties文件中配置如下属性来禁用此客户端行为。
SpringCloud有properties和YAML两种配置方式,这两种配置方式其实只是形式不一样,properties配置信息格式是a.b.c,而YAML则是a:b:c:,二者本质是同样的,只须要其中一个便可,这里以properties为案例:
spring.application.name=jdon-eureka-server server.port=1111 eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false
如今运行ServiceRegistryApplication并访问http:// localhost:1111,若是不能访问,说明没有正常启动,请检查三个环节:pom.xml是否配置正确?须要Eureka和配置
SpringBoot的注释@EnableEurekaServer是否增长了?
最后,application.properties是否配置?
SpringCloud其实很是简单,约定大于配置,默认只要配置服务器端口就能够了,而后是一条注释@EnableEurekaServer,就能启动Eurek服务器了。
服务器准备好后,咱们就要准备服务生产者,向服务器里面注册本身,服务消费者则是从服务器中发现注册的服务而后调用。
服务生产者其实首先是Eureka的客户端,生产者将本身注册到前面启动的服务器当中,引若是是idea的导航,选择CloudDiscovery的EurekaDiscovery,若是是 Maven则引入包依赖是:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
这样,spring-cloud-starter-netflix-eureka-client这个jar包就放入咱们系统的classpath,为了可以正常使用这个jar包,还须要配置,只须要在application.properties中配置eureka.client.service-url.defaultZone属性便可自动注册Eureka Server:
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
当咱们的服务在Eureka Server注册时,它会持续发送必定时间间隔的心跳。若是Eureka服务器没有从任何服务的实例接收到心跳,它将认为这个服务实例已经关闭并从本身的池中剔除它。
以上是服务生产者注册服务的过程,比较简单,为了使咱们的服务生产者能的演示代码够运行起来,咱们还须要新建一个服务生产者代码:
@RestController public class ProducerService { @GetMapping("/pengproducer") public String sayHello(){ return "hello world"; } }
这段代码是将服务暴露成RESTful接口,@RestController是声明Rest接口,/pengproducer是REST的访问url,经过get方式可以得到字符串:hello world
由于REST属于WEB的一种接口,所以须要在pom.xml中引入Web包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
而后在application.properties中加入有关REST接口的配置:
spring.application.name=PengProducerService server.port=2111
指定咱们的生产者服务的名称是PengProducerService,REST端口开在2111。
如今能够在idea中启动咱们的应用了,这样咱们启动这个项目,就能够在http://127.0.0.1:2111/ 访问这个REST服务。同时,由于咱们以前已经启动了注册服务器,访问http://localhost:1111/你会发现PengProducerService出如今服务列表中:
上面启动应用服务是在idea编辑器中,咱们还能够经过命令行启动咱们的服务生产者:
java -jar -Dserver.port=2112 producer-0.0.1-SNAPSHOT.jar
这个是在端口2112开启咱们的服务端点了。如今再问http://localhost:1111/,你会看到可用节点Availability Zones下面已经从(1)变为(2),如今咱们的服务生产者已经有两个实例在运行,当服务的消费者访问这个两个实例时,它能够根据负载平衡策略好比轮询访问其中一个服务生产者实例。
总结一下,为了让服务生产者注册到Euraka服务器中,只须要两个步骤:
请注意,spring-cloud-starter-netflix-eureka-client包是Spring Cloud升级后最新的包名,原来是spring-cloud-starter-eureka,里面没有netflix,这是过去版本,Spring Boot 1.5之后都是加入了netflix的,见Spring Cloud Edgware Release Notes
另外,这里不须要在SpringBoot主代码中再加入@enablediscoveryclient 或 @enableeurekaclient,只要eureka的client包在maven中配置,也就会出如今系统的classpath中,这样就会默认自动注册到eureka服务器中了。
这部分×××:百度网盘。
下面咱们准备访问这个服务生产者PengProducerService的消费者服务:
上个章节咱们已经启动了两个服务生产者实例,如何经过负载平衡从两个中选择一个访问呢?这时就须要Ribbon,为了使用Ribbon,咱们须要使用@LoadBalanced元注解,那么这个注解放在哪里呢?通常有两个DiscoveryClient 和 RestTemplate,这两个的区别是:
1. DiscoveryClient能够得到服务提供者(生产者)的多个实例集合,能让你手工决定选择哪一个实例,这里负载平衡的策略好比round robin轮询就不会派上,实际就没有使用Ribbon:
List<ServiceInstance> instances=discoveryClient.getInstances("PengProducerService"); ServiceInstance serviceInstance=instances.get(0);
2.RestTemplate则是使用Ribbon的负载平衡策略,使用@LoadBalanced注释resttemplate并使用zuul代理服务器做为边缘服务器。那么对zuul边缘服务器的任何请求将默认使用Ribbon进行负载平衡,而resttemplate将以循环方式路由请求。这部分代码以下:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.client.RestTemplate; @Controller public class ConsumerService { @Autowired private RestTemplate restTemplate; public String callProducer() { ResponseEntity<String> result = this.restTemplate.getForEntity( "http://PengProducerService/pengproducer", String.class, ""); if (result.getStatusCode() == HttpStatus.OK) { System.out.printf(result.getBody() + " called in callProducer"); return result.getBody(); } else { System.out.printf(" is it empty"); return " empty "; } } }
RestTemplate是自动注射进这个控制器,在这控制器,咱们调用了服务生产者http://PengProducerService/pengproducer,而后得到其结构。
这个控制器的调用咱们能够在SpringBoot启动函数里调用:
@SpringBootApplication public class ConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(ConsumerApplication .class, args); ConsumerService consumerService = ctx.getBean(ConsumerService.class); System.out.printf("final result RestTemplate=" + consumerService .callProducer() + " \n"); } }
注意到@LoadBalanced是标注在RestTemplate上,而RestTemplate是被注入到ConsumerService中的,这样经过调用RestTemplate对象实际就是得到负载平衡后的服务实例。这个能够经过咱们的服务提供者里面输出hashcode来分辨出来,启动两个服务提供者实例,每次运行ConsumerService,应该是依次打印出不一样的hashcode:
hello world1246528978 called in callProducerfinal result RestTemplate=hello world1246528978
再次运行结果:
hello world1179769159 called in callProducerfinal result RestTemplate=hello world1179769159
hellow world后面的哈希值不一样,可见是来自不一样的服务提供者实例。
若是系统基于https进行负载平衡,那么只须要两个步骤:
1.application.properties中激活ribbon的https:
ribbon.IsSecure=true
2.代码中RestTemplate初始化时传入ClientHttpRequestFactory对象:
@Bean @LoadBalanced public RestTemplate restTemplate() { CloseableHttpClient httpClient = HttpClientUtil.getHttpClient(); HttpComponentsClientHttpRequestFactory clientrequestFactory = new HttpComponentsClientHttpRequestFactory(); clientrequestFactory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(clientrequestFactory); return restTemplate; }
这部分×××:百度网盘
上篇是使用Ribbon实现对多个服务生产者实例使用负载平衡的方式进行消费,在调用服务生产者时,返回的是字符串类型,若是返回是各类本身定义的对象,这些对象传递到消费端是经过JSON方式,那么咱们的消费者须要使用Feign来访问各类Json对象。
须要注意的是:Feign = Eureka +Ribbon + RestTemplate,也就是说,使用Feign访问服务生产者,无需前面章节那么关于负载平衡的代码了,前面咱们使用RestTemplate进行负载平衡访问,代码仍是挺复杂
如今咱们开始Feign的实现:首先咱们在服务的生产者那边进行修改,让咱们生产者项目变得接近实战中项目,增长领域层、服务层和持久层。
假设新增Article领域模型对象,咱们就须要仓储保存,这里咱们使用Spring默认约定,使用JPA访问h2数据库,将Article经过JPA保存到h2数据库中:
要启用JPA和h2数据库,首先只要配置pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Article领域模型对象做为须要持久的实体对象:配置实体@Entity和@Id主键便可:
@Entity public class Article { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private String body; private Date startDate;
而后咱们创建一个空的Article仓储接口便可:
@Repository public interface ArticleRep extends JpaRepository<Article,Long> { }
这样,关于Article的CRUD实现就已经有了,不须要本身再编写任何SQL语句。这样咱们编写一个Service就能够提供Article对象的CRUD方法,这里只编写插入和查询批量两个方法:
@Service public class ArticleService { @Autowired ArticleRep articleRep; public List<Article> getAllArticles(){ return articleRep.findAll(); } public void insertArticle(Article article){ articleRep.save(article); } }
咱们在REST接口中暴露这两种方法:
2. post /article是新增
@RestController public class ProducerService { @Autowired ArticleService articleService; @GetMapping("/articles") public List<Article> getAllArticles(){ return articleService.getAllArticles(); } @GetMapping("/article") public void publishArticle(@RequestBody Article article){ articleService.insertArticle(article); }
上面服务的生产者提供了两个REST url,咱们在消费者这边使用/articles以得到全部文章:
@FeignClient(name="PengProducerService") public interface ConsumerService { @GetMapping("/articles") List<Article> getAllArticles(); }
这是咱们消费者的服务,调用生产者 /articles,这是一个接口,无需实现,注意须要标注FeignClient,其中写入name或value微服务生产者的application.properties配置:
spring.application.name=PengProducerService
固然,这里会直接耦合PengProducerService这个名称,咱们之后能够经过配置服务器更改,这是后话。
而后须要在应用Application代码加入@EnableFeignClients:
@SpringBootApplication @EnableFeignClients public class FeignconsumerApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(FeignconsumerApplication .class, args); ConsumerService consumerService = context.getBean(ConsumerService .class); System.out.printf("#############all articles ok" + consumerService .getAllArticles()); }
在FeignconsumerApplication咱们调用了前面接口ConsumerService,而ConsumerService则经过负载平衡调用另一个生产者微服务,若是咱们给那个生产者服务加入一些Articles数据,则这里就能返回这些数据:
#############all articles ok[com.example.feignconsumer.domain.Article@62b475e2, com.example.feignconsumer.domain.Article@e9474f]
说明调用成功。
在调试过程当中,曾经出现错误:
Load balancer does not have available server for client:PengProducerService
常常排查是因为生产者项目中pom.xml导入的是spring-cloud-starter-netflix-eureka-client,改成pring-cloud-starter-netflix-eureka-server就能够了,这是SpringBoot 2.0发现的一个问题。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
本章的代码下载:百度网盘
经过这个项目学习,咱们如同蚕丝剥茧层层搞清楚了Spring Cloud的微服务之间同步调用方式,发现基于REST/JSON的调用代码最少,也是最方便,Feign封装了Ribbon负载平衡和Eureka服务器访问以及REST格式处理。
顺便给你们推荐一个Java技术交流群:908676731,里面会分享一些资深架构师录制的视频资料:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多!