欢送拜访我的GitHub
这里分类和汇总了欣宸的全副原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 前文《五分钟搞懂spring-cloud-square》具体介绍了什么是spring-cloud-square,以及三种实现类型的具体概念,爱入手的您已急不可待想编码体验spring-cloud-square了,本篇咱们就来畅快实战,体验这个spring官网带给咱们的smart client
- 如题目所述,接下里咱们会将spring-cloud-square提供的三种client都编码体验,总的来说本篇由以下内容形成:
- 新建maven工程,名为<font color="blue">spring-cloud-square-tutorials</font>,这是本篇所有利用的父工程,库版本在此工程中对立治理;
- 创立子工程<font color="blue">eureka</font>,作为注册核心
- 创立子工程<font color="blue">client</font>,放一些专用的数据结构
- 创立子工程<font color="blue">provider</font>,身份是服务提供者,接下来的三个用到spring-cloud-square的子工程,都调用provider的服务
- 创立子工程<font color="blue">consumer-okhttp</font>,基于spring-cloud-square的<font color="red">okhttp</font>能力做近程调用
- 创立子工程<font color="blue">consumer-retrofit-okhttp</font>,基于spring-cloud-square的<font color="red">retrofit + okhttp</font>能力做近程调用
- 创立子工程<font color="blue">consumer-retrofit-webflux</font>,基于spring-cloud-square的<font color="red">retrofit + webflux</font>能力做近程调用
- 上述几个服务的关系如下图:
如何验证
- 代码写完之后,如何验证性能是否合乎预期呢?本篇采纳单元测试的形式,consumer-okhttp、consumer-retrofit-okhttp、consumer-retrofit-webflux这三个子工程都有本人的单元测试代码,执行通过就意味着代码性能合乎预期了
源码下载
- 本篇实战中的残缺源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blo...):
名称 | 链接 | 备注 |
---|---|---|
我的项目主页 | https://github.com/zq2599/blo... | 该我的项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blo... | 该我的项目源码的仓库地址,https协定 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该我的项目源码的仓库地址,ssh协定 |
- 这个git我的项目中有多个文件夹,本篇的源码在<font color="blue">spring-cloud-square-tutorials</font>文件夹下,如下图红框所示:
版本信息
- 本篇实战波及到的次要版本状况如下:
- JDK:1.8.0_291
- IDEA:2021.1.3 (Ultimate Edition)
- maven:3.8.1
- 操作系统:win10 64位
- springboot:2.4.4
- spring-cloud:2020.0.2
- spring-cloud-square:0.4.0-SNAPSHOT
父工程spring-cloud-square-tutorials
- 父工程名为<font color="blue">spring-cloud-square-tutorials</font>,其pom.xml如下,除了依赖库的版本在此对立治理,还要留神的是两个仓库的引入(https://repo.spring.io/snapshot和https://repo.spring.io/milestone),引入它们是因为spring-cloud-square还在孵化阶段,没有公布到maven地方仓库:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.bolingcavalry</groupId> <artifactId>spring-cloud-square-tutorials</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>1.8</java.version> <spring-cloud.version>2020.0.2</spring-cloud.version> <square.dependency.version>0.4.0-SNAPSHOT</square.dependency.version> </properties> <packaging>pom</packaging> <description>Demo project for Spring Cloud Square Retrofit Web</description> <modules> <module>provider</module> <module>eureka</module> <module>consumer-okhttp</module> <module>client</module> <module>consumer-retrofit-okhttp</module> <module>consumer-retrofit-webflux</module> </modules> <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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.14.9</version> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.16</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-okhttp</artifactId> <version>${square.dependency.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-retrofit</artifactId> <version>${square.dependency.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>${square.dependency.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-retrofit-webclient</artifactId> <version>${square.dependency.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <!--skip deploy (this is just a test module) --> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories></project>
注册核心eureka
- eureka利用并没有什么特别之处,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-square-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>eureka</artifactId> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <start-class>com.bolingcavalry.eureka.EurekaApplication</start-class> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId><!-- <version>Finchley.BUILD-SNAPSHOT</version>--> <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> <!-- defined in spring-cloud-starter-parent pom (as documentation hint), but needs to be repeated here --> <configuration> <requiresUnpack> <dependency> <groupId>com.netflix.eureka</groupId> <artifactId>eureka-core</artifactId> </dependency> <dependency> <groupId>com.netflix.eureka</groupId> <artifactId>eureka-client</artifactId> </dependency> </requiresUnpack> </configuration> </plugin> <plugin> <!--skip deploy (this is just a test module) --> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build></project>
- 中规中矩的配置文件application.yml,端口是8761,前面的利用也要保持一致:
server: port: 8761spring: application: name: eurekaeureka: client: registerWithEureka: false fetchRegistry: false server: waitTimeInMsWhenSyncEmpty: 0
- 启动类EurekaApplication.java,记得用注解EnableEurekaServer开启eureka服务:
package com.bolingcavalry.eureka;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication@EnableEurekaServerpublic class EurekaApplication { public static void main(String[] args) throws Exception { SpringApplication.run(EurekaApplication.class, args); }}
- eureka利用曾经实现,接下来是服务提供者了
服务提供者provider
- -新建名为<font color="blue">provider</font>的利用,pom.xml如下,可见是个一般的web工程,会将本人注册到eureka下来:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-square-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>provider</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.bolingcavalry</groupId> <artifactId>client</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> </dependency> <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> </dependencies> <build> <plugins> <!-- 如果父工程不是springboot,就要用以下形式应用插件,能力生成失常的jar --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.bolingcavalry.provider.ProviderApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>
- 配置文件application.yml:
spring: application: name: providerserver: port: 18080eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
- 启动类ProviderApplication .java:
package com.bolingcavalry.provider;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); }}
- web服务类,可见对外提供了两个接口<font color="blue">hello-str</font>和<font color="blue">hello-obj</font>,前者返回字符串,或者返回对象:
package com.bolingcavalry.provider.controller;import com.bolingcavalry.client.HelloResponse;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.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import java.util.Random;@RestControllerpublic class Hello { public static final String HELLO_PREFIX = "Hello World"; @Autowired DiscoveryClient client; /** * 随机取一个provider实例,返回其形容信息, * 如果只有一个provider实例时,返回的就是以后服务信息 * @return */ private String providerDescription() { List<ServiceInstance> instances = client.getInstances("provider"); ServiceInstance selectedInstance = instances .get(new Random().nextInt(instances.size())); return String.format("serviceId [%s], host [%s], port [%d]", selectedInstance.getServiceId(), selectedInstance.getHost(), selectedInstance.getPort()); } private String dateStr(){ return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()); } @GetMapping("/hello-str") public String helloStr() { List<ServiceInstance> instances = client.getInstances("provider"); ServiceInstance selectedInstance = instances .get(new Random().nextInt(instances.size())); return HELLO_PREFIX + " : " + providerDescription() + ", " + dateStr(); } @GetMapping("/hello-obj") public HelloResponse helloObj(@RequestParam("name") String name) { return new HelloResponse(name, dateStr(), providerDescription()); }}
- 这个provider利用算是个最朴实无华的web服务了
启动服务
- 当初能够将eureka和provider服务先后启动,这样前面的利用编码实现后能够间接测试
consumer-okhttp,基于spring-cloud-square的okhttp能力
- 接下来要创立的利用<font color="blue">consumer-okhttp</font>,应用的是spring-cloud-square三种能力的第一种:okhttp
- pom.xml内容如下,重点是spring-cloud-square-okhttp和spring-cloud-starter-loadbalancer这两个库的引入:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-square-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-okhttp</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.bolingcavalry</groupId> <artifactId>client</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-webflux</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-okhttp</artifactId> <version>0.4.0-SNAPSHOT</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies> <build> <plugins> <!-- 如果父工程不是springboot,就要用以下形式应用插件,能力生成失常的jar --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.bolingcavalry.ConsumerApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>
- 配置文件application.yml,还是常见的那几个配置:利用名、端口、eureka:
spring: application: name: consumer-okhttpserver: port: 18081eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
- 启动类:
package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class OkhttpApplication { public static void main(String[] args) { SpringApplication.run(OkhttpApplication.class, args); }}
- 接下来是重要的配置类OkHttpClientConfig.java,用于实例化OkHttpClient.Builder对象并注册到spring环境:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationclass OkHttpClientConfig{ @Bean @LoadBalanced public OkHttpClient.Builder okHttpClientBuilder() { return new OkHttpClient.Builder(); }}
- 而后就能够应用这个Builder来创立OkHttpClient实例了,如下所示,可见入参request的url字段里应用了服务名<font color="blue">provider</font>,相当于OkHttpClient内如也能通过服务名获得具体的服务地址,至于是如何获取的,会在前面的文章详细分析,整段代码除了url应用服务名,并没有什么值得关注的中央了,一般的OkHttpClient应用而已:
package com.bolingcavalry.consumer.controller;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestControllerpublic class RemoteHello { @Autowired private OkHttpClient.Builder builder; @GetMapping("/remote-str") public String hello() throws IOException { // 间接应用服务名 Request request = new Request.Builder().url("http://provider/hello-str").build(); // 近程拜访 Response response = builder.build().newCall(request).execute(); return "get remote response : " + response.body().string(); }}
- 接下来看看单元测试代码,应用MockMvcRequestBuilders结构http申请,查看返回码和返回内容:
package com.bolingcavalry.consumer.controller;import com.bolingcavalry.client.Constants;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import static org.hamcrest.Matchers.containsString;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest { @Autowired private MockMvc mvc; @Test void hello() throws Exception { String responseString = mvc.perform(MockMvcRequestBuilders.get("/remote-str").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(containsString(Constants.HELLO_PREFIX))) .andDo(print()) .andReturn() .getResponse() .getContentAsString(); log.info("response in junit test :\n" + responseString); }}
- 如果eureka和provider都运行起来了,那么此时能够间接运行单元测试类,顺利通过测试,如下图:
consumer-retrofit-okhttp,基于spring-cloud-square的okhttp能力
- 接下来的两个利用都应用了当下热门的retrofit,再搭配Spring Cloud LoadBalance实现服务注册发现,当然了retrofit本身无奈实现网络申请解决,要依赖其余库,先看okhttp库的
- 新建利用<font color="blue">consumer-retrofit-okhttp</font>,其pom.xml如下,要留神的必须依赖spring-cloud-square-retrofit和spring-cloud-square-okhttp,另外,为了:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-square-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-retrofit-okhttp</artifactId> <dependencies> <dependency> <groupId>com.bolingcavalry</groupId> <artifactId>client</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-retrofit</artifactId> <version>0.4.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-okhttp</artifactId> <version>0.4.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <!--skip deploy (this is just a test module) --> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
- 配置文件:
spring: application: name: consumer-retrofit-okhttpserver: port: 18082eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
- 启动类:
package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class RetrofitOkhttpApplication { public static void main(String[] args) { SpringApplication.run(RetrofitOkhttpApplication.class, args); }}
- 配置类,和前一个利用的没啥区别,想想也是,底层可不都是okhttp么:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.square.retrofit.EnableRetrofitClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@EnableRetrofitClientsclass OkHttpClientConfig{ @Bean @LoadBalanced public OkHttpClient.Builder okHttpClientBuilder() { return new OkHttpClient.Builder(); }}
- 接下来,乏味的局部呈现了,先定义HelloService.java,外面的注解RetrofitClient指定了对应的服务名<font color="blue">provider</font>,在hello办法生,用GET注解指定了provider提供的web接口,而且hello办法的返回值Call<HelloResponse>,和provider服务中hello-obj的返回值HelloResponse也是对应的,还有就是hello的入参对应着provider服务中hello-obj的入参,很相熟吧,的确,和feign太像了:
package com.bolingcavalry.consumer.service;import com.bolingcavalry.client.HelloResponse;import org.springframework.cloud.square.retrofit.core.RetrofitClient;import retrofit2.Call;import retrofit2.http.GET;import retrofit2.http.Query;@RetrofitClient("provider")public interface HelloService { @GET("/hello-obj") Call<HelloResponse> hello(@Query("name") String name);}
- 接下来是调用provider服务中hello-obj接口的代码RemoteHello.java,如下所示,神奇的一幕呈现了,方才咱们只写了HelloService接口,并没有写它的实现,然而通过Autowired注解却能 从spring环境拿到实例间接应用,在hello办法中,并没有见到近程调用的代码,而是执行helloService.hello,就能发动近程调用,拿到provider返回的后果:
package com.bolingcavalry.consumer.controller;import com.bolingcavalry.client.HelloResponse;import com.bolingcavalry.consumer.service.HelloService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestControllerpublic class RemoteHello { @Autowired(required = false) HelloService helloService; @GetMapping("/remote-obj") public HelloResponse hello(@RequestParam("name") String name) throws IOException { return helloService.hello(name).execute().body(); }}
- 看到这里,聪慧的您肯定会感觉欣宸就是个<font color="blue">没见过世面的乡巴佬</font>:定义HelloService 接口,无需开发实现类,这玩意在mybatis不就有了嘛,竟然敢说"神奇",我感觉您说得对,欣宸的确没见识,少见多怪的...
- 单元测试类如下,因为返回的是json对象,因而能够用andExpect办法再配合MockMvcResultMatchers,对json进行查看:
package com.bolingcavalry.consumer.controller;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.hamcrest.Matchers.is;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest { private MockMvc mvc; @Autowired private WebApplicationContext webApplicationContext; @BeforeEach public void setUp() { // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1, // 失去的字符串打印进去也是乱码, // 上面的设置能够解决此问题 if (null==mvc) { mvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) .addFilter((request, response, chain) -> { response.setCharacterEncoding("UTF-8"); // this is crucial chain.doFilter(request, response); }, "/*") .build(); } } @Test void hello() throws Exception { // 申请参数是用户名,实时生成一个 String name = System.currentTimeMillis() + "程序员A"; // 申请 String responseString = mvc.perform( MockMvcRequestBuilders .get("/remote-obj") .param("name", name) .accept(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) // 验证状态 .andExpect(jsonPath("$.name", is(name))) // 验证json中返回的字段是否含有name .andDo(print()) .andReturn() .getResponse() .getContentAsString(); log.info("response in junit test :\n" + responseString); }}
- 执行单元测试,如下图,顺利通过:
consumer-retrofit-webflux,基于spring-cloud-square的retrofit + webflux
- 最初退场的是consumer-retrofit-webflux,pom.xml如下,依赖库是spring-cloud-square-retrofit + spring-boot-starter-webflux的组合:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-square-tutorials</artifactId> <groupId>com.bolingcavalry</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-retrofit-webflux</artifactId> <dependencies> <dependency> <groupId>com.bolingcavalry</groupId> <artifactId>client</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-retrofit</artifactId> <version>0.4.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-square-retrofit-webclient</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <!--skip deploy (this is just a test module) --> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
- 配置文件application.yml:
spring: application: name: consumer-retrofit-webfluxserver: port: 18083eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
启动类RetrofitWebfluxApplication.java
package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class RetrofitWebfluxApplication { public static void main(String[] args) { SpringApplication.run(RetrofitWebfluxApplication.class, args); }}
- 配置类AppConfiguration.java,应用的注解是EnableRetrofitClients,实例化的Buider对象是WebClient.Builder,和后面的不一样,要分外留神:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.square.retrofit.webclient.EnableRetrofitClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.reactive.function.client.WebClient;@Configuration@EnableRetrofitClientsclass AppConfiguration { @Bean @LoadBalanced public WebClient.Builder builder() { return WebClient.builder(); }}
- 接下来是接口定义,留神hello办法的返回值是<font color="blue">Mono<HelloResponse></font>,这是weflux格调的返回值,代表异步的0个或一个元素:
package com.bolingcavalry.consumer.service;import com.bolingcavalry.client.HelloResponse;import org.springframework.cloud.square.retrofit.core.RetrofitClient;import reactor.core.publisher.Mono;import retrofit2.http.GET;import retrofit2.http.Query;@RetrofitClient("provider")public interface HelloService { @GET("/hello-obj") Mono<HelloResponse> hello(@Query("name") String name);}
- 最初是单元测试类,和后面的没啥区别:
package com.bolingcavalry.consumer.controller;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.hamcrest.Matchers.is;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest { private MockMvc mvc; @Autowired private WebApplicationContext webApplicationContext; @BeforeEach public void setUp() { // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1, // 失去的字符串打印进去也是乱码, // 上面的设置能够解决此问题 if (null==mvc) { mvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) .addFilter((request, response, chain) -> { response.setCharacterEncoding("UTF-8"); // this is crucial chain.doFilter(request, response); }, "/*") .build(); } } @Test void hello() throws Exception { // 申请参数是用户名,实时生成一个 String name = System.currentTimeMillis() + "程序员B"; // 申请 String responseString = mvc.perform( MockMvcRequestBuilders .get("/remote-obj") .param("name", name) .accept(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) // 验证状态 .andExpect(jsonPath("$.name", is(name))) // 验证json中返回的字段是否含有name .andDo(print()) .andReturn() .getResponse() .getContentAsString(); log.info("response in junit test :\n" + responseString); }}
- 运行单元测试,如下图,顺利通过,并且红框中所示的中文也没有乱码:
- 至此,spring-cloud-square的三种类型,咱们全副编码体验了一遍,聪慧的您当然不会只满足于应用它们,接下来文章,咱们就去深刻spring-cloud-square源码,钻研其实现的细节,欣宸原创,必不会辜负您的期待!
你不孤独,欣宸原创一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 数据库+中间件系列
- DevOps系列
欢送关注公众号:程序员欣宸
微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游Java世界...
https://github.com/zq2599/blog_demos