SpringCloud服务端/客户端/消费者搭建(二)
文 / 汝淉
服务端/客户端/消费者其实都是一个,,,他们可以相互转变角色,,只是谁调用谁的问题…
我们先搭建第一个服务端/客户端/消费者
二:创建服务消费者Client
步骤:
新建client工程,步骤相似,选择Cloud Discovery,勾选Eureka Discovery
其实这里也不用这么严格,,这里的步骤选项,,都是减少了手动在pom中导入依赖,,,当然手动导入依赖也是很好的选择….
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
修改启动类…在该client服务的启动类上加上@EnableEurekaClient 加上该注解,,,意味着这个服务就是服务端/客户端/消费者
启动类的作用:1:启动类可以启动这个服务.也就是这个服务启动的入口…
2:启动类会扫描和自己同级的(同级的子集)每个文件.让他们的注解生效…比如.
启动类(我的)
package cloud.client.clientboot;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableEurekaClient
public class ClientbootApplication {
public static void main(String[] args) {
SpringApplication.run(ClientbootApplication.class, args);
}
}
配置文件application.properties中加入
#是指,这个服务的名称
spring.application.name=CONSUME-SERVICE
#该服务独有的端口号
server.port=5555
#下面这个是指向Eureka注册中心,,,这样就注册成功了..
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
刚试了一下…原来是先启动Eureka 注册中心启动起来以后,,,再启动client就不会报错误了,,…
到此一个服务端/客户端/消费者..就搭建好了…
还可以以相同的步骤,类似的配置搭建多个这样的服务……
三:Ribbon服务端的负载均衡
这里的负载均衡的配置比传统的都要简单…Feign中也使用Ribbon
Ribbon的负载模式有好多种方案,,默认一个服务一次比如,,A和B2个服务,,第一个请求负载给A,,第二个请求就会负载给B…
情景: 一个或者多个Client(客户端)发起请求 通过 Eureka传递给后面的多个Client服务端…让多个Client服务端出现负载均衡..
首先..按照二中的步骤,,创建2个服务端… 用到的服务 一个Client(客户端) 一个Eureka 2个Client(服务端,实现负载均衡)
在2个Client服务端的application.properties中加入下面的配置….其实跟创建客户端的配置类似…
服务1:
spring.application.name=compute-service
server.port=3333
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
服务二:
spring.application.name=compute-service
server.port=2222
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
在2个服务端的pom文件中均加入依赖…
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
加上我们实例创建的端口号问5555的…服务名称叫CONSUME-SERVICE 的客户端…几个服务就搭建好了…先启动Eureka注册中心,..然后依次启动其他3个Client服务
然后打开注册中心Eureka的地址….还记得第一部分创建的注册中心的端口为1111..当然其他3个的配置文件中也可以看见…
打开浏览器访问地址::::http://localhost:1111
就会出现如下图所示…
然后写代码测试::::
在一个客户端和2个服务端创建conroller类…加上注解@RestController….这个是组合注解,,为了减少注解数量..很方便…
首先贴出来的是2个服务端的controller 的代码…user类自己创建…2个服务端的代码都是这个…对待开房访问接口的地址…
package client.server.clientserver2.controller;
import client.server.clientserver2.model.UserVo;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
/**
* Created by RuGuo on 2018/6/7.
*/
@RestController
public class ClientController {
private final Logger logger = Logger.getLogger(getClass());
@RequestMapping(value = "add",method = RequestMethod.GET)
public UserVo service(@RequestParam String userId){
UserVo userVo = new UserVo();
userVo.setUserId(userId);
userVo.setUserName("汝淉");
logger.info("执行了这里.");
return userVo;
}
}
然后需要在消费者也就是客户端启动类中加入下面代码;;
在应用主类中,通过@EnableDiscoveryClient注解来添加发现服务能力。创建RestTemplate实例,并通过@LoadBalanced注解开启均衡负载能力。
package client.consumer.clientconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableEurekaClient
@SpringBootApplication
public class ClientconsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ClientconsumerApplication.class, args);
}
}
然后贴出一个消费者也就是客户端的controller…同样加注解RestController
package client.consumer.clientconsumer.controller;
import client.consumer.clientconsumer.interfaces.FeignClientService;
import client.consumer.clientconsumer.model.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
/**
* Created by RuGuo on 2018/6/7.
*/
@RestController
public class ConsumeController {
@Autowired
RestTemplate restTemplate;
//ribbon的调用方式
@RequestMapping(value = "select",method = RequestMethod.GET)
public UserVo query( String userId){
//下面这个地址就是2个服务端的 服务名称+新建的controller的开放接口...
return restTemplate.getForObject("http://COMPUTE-SERVICE/add?userId="+userId,UserVo.class);
}
}
}
然后均启动服务,,,
请求消费者,,Client..查看结果…访问成功
查看2个服务端的日志…
端口为2222的服务执行了一次
端口好为3333的服务也执行了一次
到此Ribbon的负载均衡就演示成功了…虽然代码是low了点…
Feign
(引用别人的话)
Feign是一个声明式的Web Service客户端,它使得编写Web Serivce客户端变得更加简单。我们只需要使用Feign来创建一个接口并用注解来配置它既可完成。它具备可插拔的注解支持,包括Feign注解和JAX-RS注解。Feign也支持可插拔的编码器和解码器。Spring Cloud为Feign增加了对Spring MVC注解的支持,还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现
接下来就继续展示Feign的使用…
所用服务,还是用我们之前创建好的 1个客户端服务(sconsume-service) 1个注册中心Eureka服务 2个服务端服务(compute-service)
修改客户端/消费者服务的pom.xml 加入以下依赖,,,覆盖原来Ribbon的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--这是Feign的依赖,,,覆盖原来的Ribbon依赖,,因为Feign包含了Ribbon的负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在该服务的启动类上加上注解@@EnableFeignClients..该注解就会开启该服务的Feign功能
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class ClientconsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ClientconsumerApplication.class, args);
}
}
然后在该服务上创建一个接口,,,该接口的作用就是指向需要调用的服务端..
方式就是使用注解@FeignClient(“compute-service”)..里面的内容就是2个服务端的服务名称…可以把该接口当成Springmvc中的service层的接口…代码如下
接口上方的RequestMapping指向的是2个服务端”compute-service”被调用接口的rest的路径…..(一会贴2个服务端的接口.就知道了…)
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by RuGuo on 2018/6/7.
*/
@FeignClient("compute-service")
public interface FeignClientService {
@RequestMapping(value = "/add",method = RequestMethod.GET)
UserVo add(@RequestParam(value = "userId")String userId,@RequestParam("userName")String userName);
}
在该消费服务创建一个controller类…并对外提供消费的接口..
先注入上面创建的接口..我吧Ribbon的代码也贴出来,,方便比较和选择…
代码如下
@RestController
public class ConsumeController {
@Autowired
RestTemplate restTemplate;//ribbon的调用方式
@Autowired
private FeignClientService feignClientService;//现在在使用的feign方式,,
//ribbon的调用方式
@RequestMapping(value = "query",method = RequestMethod.GET)
public UserVo query( String userId){
return restTemplate.getForObject("http://COMPUTE-SERVICE/add?userId="+userId,UserVo.class);
}
//现在在使用的feign方式,,
@RequestMapping(value = "select",method = RequestMethod.GET)
public UserVo query( UserVo userVo ){
String userId = userVo.getUserId();
String userName = userVo.getUserName();
return feignClientService.add(userId,userName);
}
然后再贴出2个Client服务端的代码…其实还是那些代码..没有变化
@RestController
public class ClientController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "add",method = RequestMethod.GET)
public UserVo service(@RequestParam String userId,String userName){
UserVo userVo = new UserVo();
userVo.setUserId(userId);
userVo.setUserName(userName);
logger.info("执行了这里.");
return userVo;
}
看到这里,,就会明白,,消费者也就是客户端那个interface接口中RequestMapping注解的作用的..就是为了指向调用这里的add方法…..
当然,,有了Feign就不用Ribbon 了,,,2个服务端的pom中也用Feign依赖替换掉Ribbon的依赖吧,,原因是因为1:Feign也有负载均衡,切功能比Ribbon丰富..2:每一个服务角色都不是固定的…随时都有可能被别的服务调用,,角色就会发生转化..所以建议都留着Feign负载均衡的依赖吧…
然后其他各项配置都无需改变..先启动注册中心,,,再一次启动各个服务..
查看访问结果
服务1执行的日志..端口为2222的服务
服务二执行的日志.端口为3333的服务
到此简单的Feign演示就结束了…