概述
在前面的文章中,我们讲了使用Eureka
作为服务注册中心,在服务启动后,各个微服务会将自己注册到Eureka server
。那么服务之间是如何调用?又是如何进行负载均衡的呢?本文讲讲服务之间调用及负载均衡Ribbon。
目前,在Spring cloud 中服务之间通过restful方式调用有两种方式
restTemplate+Ribbon
feign
从实践上看,采用feign的方式更优雅(feign内部也使用了ribbon做负载均衡)。
本文使用如下的项目实例来分别演示这两种写法。
hello
服务,读取数据库,返回信息world
服务,返回信息helloworld
服务,调用了hello
服务和world
服务(restTemplate+Ribbon方式)helloworldfeign
服务,调用了hello
服务和world
服务(feign方式)
项目之间关系.png
本文项目代码:springcloud-demo
如何理解客户端Ribbon
zuul也有负载均衡的功能,它是针对外部请求做负载,那客户端ribbon的负载均衡又是怎么一回事?
客户端ribbon的负载均衡,解决的是服务发起方(在Eureka注册的服务)对被调用的服务的负载,比如我们查询商品服务要调用显示库存和商品明细服务,通过商品服务的接口将两个服务组合,可以减少外部应用的请求,比如手机App发起一次请求即可,可以节省网络带宽,也更省电。
ribbon是对服务之间调用做负载,是服务之间的负载均衡,zuul是可以对外部请求做负载均衡。
Ribbon负载均衡.png
hello服务与world服务
hello服务
Git项目代码:hello
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!--connect the db--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>1.5.4.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>1.5.6.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
启动类HelloApplication:
package com.example.hello;import com.example.hello.model.Info;import com.example.hello.repository.InfoRepository;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.context.annotation.Bean;@EnableDiscoveryClient@SpringBootApplicationpublic class HelloApplication { private static final Logger log= LoggerFactory.getLogger(HelloApplication.class); public static final String KEY="Database"; public static final String VALUE="MYSQL FROM BILLJIANG"; public static void main(String[] args) { SpringApplication.run(HelloApplication.class, args); } @Bean public CommandLineRunner initDatabase(InfoRepository repository){ return (args) -> { repository.save(new Info(KEY,VALUE)); }; } }
入口类HelloController:
package com.example.hello.controller;import com.example.hello.HelloApplication;import com.example.hello.model.HelloMessage;import com.example.hello.model.Info;import com.example.hello.repository.InfoRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestControllerpublic class HelloController { @Autowired private DiscoveryClient discoveryClient; @Autowired private InfoRepository infoRepository; @GetMapping("/") public String home(){ return "hello"; } @GetMapping("/message") public HelloMessage getMessage() { HelloMessage helloMessage = new HelloMessage(); helloMessage.setName(getLocalInstanceInfo()); helloMessage.setMessage(getInfoFromDatabase()); return helloMessage; } private String getLocalInstanceInfo() { ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance(); return serviceInstance.getServiceId() + ":" + serviceInstance.getHost() + ":" + serviceInstance.getPort(); } private String getInfoFromDatabase() { List<Info> infoList = infoRepository.findByName(HelloApplication.KEY); for (Info info : infoList) { return info.toString(); } return "(no database info)"; } }
配置文件application.yml:
#随机端口server: port: 0#服务注册中心eureka: client: service-url: default-zone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}#数据源spring: application: name: hello datasource: url: jdbc:mysql://127.0.0.1:3306/${{MYSQL_DATABASE}:foodb} username: ${{MYSQL_USERNAME}:root} password: ${{MYSQL_PASSWORD}:billjiang} testWhileIdle: true validationQuery: SELECT 1 jpa: show-sql: true hibernate: ddl-auto: update naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: dialect: org.hibernate.dialect.MySQL5Dialect zipkin: base-url: http://127.0.0.1:9411
其他类略,请参照源码:
world
world服务与hello服务类似,不过没有连接数据库
项目代码:world
helloworld调用hello服务和world服务
采用restTemplate+Ribbon调用服务。
项目代码:helloworld
启动类HelloworldApplication:
配置restTemplate的Bean
package com.example.helloworld;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;@SpringBootApplication@EnableDiscoveryClientpublic class HelloworldApplication { public static void main(String[] args) { SpringApplication.run(HelloworldApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
入口类HelloworldController:
package com.example.helloworld.controller;import com.example.helloworld.model.HelloMessage;import com.example.helloworld.model.HelloworldMessage;import com.example.helloworld.model.WorldMessage;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;/** * @author billjiang 475572229@qq.com * @create 17-8-22 */@RestControllerpublic class HelloworldController { private static final Logger log = LoggerFactory.getLogger(HelloworldController.class); private static final String HELLO_SERVICE_NAME = "hello"; private static final String WORLD_SERVICE_NAME = "world"; @Autowired private RestTemplate restTemplate; @GetMapping("/") public String home() { return "hello world"; } @GetMapping("/message") public HelloworldMessage getMessage() { HelloMessage hello = getMessageFromHelloService(); WorldMessage world = getMessageFromWorldService(); HelloworldMessage helloworld = new HelloworldMessage(); helloworld.setHello(hello); helloworld.setWord(world); log.debug("Result helloworld message:{}", helloworld); return helloworld; } private HelloMessage getMessageFromHelloService() { HelloMessage hello = restTemplate.getForObject("http://hello/message", HelloMessage.class); log.debug("From hello service : {}.", hello); return hello; } private WorldMessage getMessageFromWorldService() { WorldMessage world = restTemplate.getForObject("http://world/message", WorldMessage.class); log.debug("From world service : {}.", world); return world; } }
测试:
helloworldfeign调用hello服务和world服务
feign方式调用服务,项目代码参考:helloworldfeign
pom.xml引入feignx
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
配置启动类注解:
package com.example.helloworldfeign;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.feign.EnableFeignClients;@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublic class HelloworldFeignApplication { public static void main(String[] args) { SpringApplication.run(HelloworldFeignApplication.class, args); } }
被调用接口HelloService
package com.example.helloworldfeign.service;import com.example.helloworldfeign.model.HelloMessage;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;/** * @author billjiang 475572229@qq.com * @create 17-8-23 */@FeignClient(value="hello")public interface HelloService { @GetMapping("/message") HelloMessage hello(); }
被调用接口WorldService
package com.example.helloworldfeign.service;import com.example.helloworldfeign.model.WorldMessage;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;/** * @author billjiang 475572229@qq.com * @create 17-8-23 */@FeignClient(value="world")public interface WorldService { @GetMapping("/message") WorldMessage world(); }
入口类HelloworldController:
package com.example.helloworldfeign.controller;import com.example.helloworldfeign.model.HelloMessage;import com.example.helloworldfeign.model.HelloworldMessage;import com.example.helloworldfeign.model.WorldMessage;import com.example.helloworldfeign.service.HelloService;import com.example.helloworldfeign.service.WorldService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;/** * @author billjiang 475572229@qq.com * @create 17-8-23 */@RestControllerpublic class HelloworldController { private static final Logger log = LoggerFactory.getLogger(HelloworldController.class); private static final String HELLO_SERVICE_NAME = "hello"; private static final String WORLD_SERVICE_NAME = "world"; @Autowired private HelloService helloService; @Autowired private WorldService worldService; @GetMapping("/") public String home() { return "hello world"; } @GetMapping("/message") public HelloworldMessage getMessage() { HelloMessage hello = helloService.hello(); WorldMessage world = worldService.world(); HelloworldMessage helloworld = new HelloworldMessage(); helloworld.setHello(hello); helloworld.setWord(world); log.debug("Result helloworld message:{}", helloworld); return helloworld; } }
为了更好的地查看效果,hello服务和world服务可以启动多个实例,并把调用实例的ip和端口输出来。
作者:billJiang
链接:https://www.jianshu.com/p/7ca91139dca5
共同学习,写下你的评论
评论加载中...
作者其他优质文章