Spring Cloud 生产者和消费者

学习了《Spring Cloud与Docker微服务架构实战》这本书,想要记录一下学习历程,同时把总结的经验以及自己补充的知识记录下来,于是就有了这篇文章。

创建一个Spring Cloud项目

本文及后续的内容使用Spring Cloud的版本为Edgware.SR6,Spring Boot的版本为1.5.x,因为在Edgware之后的版本,Spring Boot也升级为2.x版本的了。为了保证能够顺利进行书上的练习,因为选用了Edgware最新的版本。

创建一个父项目

首先创建一个父项目micro-service-spring-cloud-study,用于管理其他子项目依赖的版本,注意父项目的打包方式为pom,其中标签内的是即将创建的生产者和消费者子项目,子项目从父项目中获取依赖的jar包及属性信息。

dependencies和dependencyManagement的说明

  1. 如果dependencies里的dependency自己没有声明version元素,那么maven就会到dependencyManagement里面去找有没有对该artifactId和groupId进行过版本声明,如果有,就继承它,如果没有就会报错,告诉你必须为dependency声明一个version。
  2. 如果dependencies中的dependency声明了version,那么无论dependencyManagement中有无对该jar的version声明,都以dependency里的version为准。

以下为父pom文件pom.xml



	4.0.0

	com.fanfan.cloud
	micro-service-spring-cloud-study
	0.0.1-SNAPSHOT
	pom

	
		micro-service-provider-user
		micro-service-consumer-movie
	

	
		org.springframework.boot
		spring-boot-starter-parent
		1.5.4.RELEASE
	

	
		UTF-8
		UTF-8
		1.8
	

	
	
	
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Edgware.SR6
                pom
                import
            
        
    

	
	
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

		
		
			org.springframework.boot
			spring-boot-starter-test
			test
		

		
		
			org.springframework.boot
			spring-boot-starter-actuator
		

		
		
			ch.qos.logback
			logback-core
			compile
			1.1.11
		
		
			ch.qos.logback
			logback-classic
			compile
			1.1.11
		

	


创建生产者项目

创建生成者项目micro-service-provider-user,由于书中使用的是内置的h2数据库,因此需要建表的schema.sql文件和插入数据的data.sql文件,application.yml为配置文件,配置数据库等信息,项目结构如下图所示。
Spring Cloud 生产者和消费者_第1张图片

生产者的pom文件 pom.xml



	4.0.0
	com.test.cloud
	micro-service-provider-user
	0.0.1-SNAPSHOT
	jar

	
	
		com.fanfan.cloud
		micro-service-spring-cloud-study
		0.0.1-SNAPSHOT
	

	
		
			org.springframework.boot
			spring-boot-starter-data-jpa
		
		
			org.springframework.boot
			spring-boot-starter-web
		
		
			com.h2database
			h2
			runtime
		

		
			com.alibaba
			fastjson
			1.2.29
		

		
		
			org.projectlombok
			lombok
			1.16.10
		
	

    
        provider-user
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



建表语句schema.sql

drop table user if exists;
create table user(id bigint generated by default as IDENTITY,
username varchar(40),
name varchar(20),
age int(3),
balance decimal(10,2),
primary key(id));

插入数据

insert into user(id,username,name,age,balance) VALUES (1,‘user1’,‘张三’,20,100.00);
insert into user(id,username,name,age,balance) VALUES (2,‘user2’,‘李四’,30,200.00);
insert into user(id,username,name,age,balance) VALUES (3,‘user3’,‘二毛’,40,300.00);
insert into user(id,username,name,age,balance) VALUES (4,‘user4’,‘大毛’,50,400.00);

application.yml配置文件


server:
  port: 8010
spring:
  jpa:
    generate-ddl: false
    show-sql: true
    hibernate:
      ddl-auto: none
  datasource:   # 指定数据源
    platform: h2  # 指定数据源类型
    schema: classpath:schema.sql # 指定h2数据库的建表脚本
    data: classpath:data.sql # 指定h2数据库的数据脚本
  application:
    name: provider #名称要小写 指定应用名
logging:
  level:
    root: INFO # 配置日志级别,让hibernate打印执行的SQL
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
    com.spring.cloud.test: DEBUG


日志文件



    
    
    
    
    
        
        
            %date [%thread] %-5level %logger{50}:%L - %msg%n
        
    

    
    
        
        ${logDir}/service.log
        
            ${logDir}/history/service.%d{yyyy-MM-dd}.log
            30
        
        
            %date [%thread] %-5level %logger{50}:%L - %msg%n
        
    

    
    
        
        
    

实体类

注意@Data是lombok的注解,可以节省代码量,@Entity是JPA的注解。

@Entity
@Data
public class User implements Serializable{
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;
    /**注意不要写成userName, 否则会报找不到user_name字段错误*/
    @Column
    private String username;
    @Column
    private String name;
    @Column
    private Integer age;
    @Column
    private BigDecimal balance;
}

Dao层

ORM框架使用的是JPA,继承的JpaRepository中有默认的方法。

@Repository
public interface UserRepository extends JpaRepository {
}

Controller层

因为代码比较简单所以省略了Service层,实际开发中不可这样写。

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/simple/{id}")
    public User findById(@PathVariable Long id) {
        return this.userRepository.findOne(id);
    }

	 @PostMapping("/user")
    public User postUser(@RequestBody User user) {
        return user;
    }
     @GetMapping("/user")
    public User getUser(User user) {
        return user;
    }
    @PostMapping("/save")
    public User saveUser(@RequestBody User user) {
        return userRepository.save(user);
    }

}

启动类

@SpringBootApplication
public class ProviderUserApplication {
	public static void main(String[] args) {
		SpringApplication.run(ProviderUserApplication.class, args);
	}
}

以上步骤结束后即可启动ProviderUserApplication了。

单元测试

代码编写完成后不要忽略单元测试,可以到Dao层和Controller层进行单元测试,其中Controller层的单元测试是模拟浏览器发出请求,因为是生产者需要确认接口是否可用,因此建议写单元测试,当然也可以使用PostMan或JMeter进行测试,此处不做介绍。

Dao层单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional // 事务回滚,不污染数据
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testSave() {
        User user = new User();
        user.setName("王五");
        user.setAge(25);
        user.setUsername("user5");
        user.setBalance(new BigDecimal(500.00));
        User result = userRepository.save(user);
        System.err.println(result);
    }

    @Test
    public void testFindOne() {
        User result = userRepository.findOne(4L);
        System.err.println(result);
    }
}

Controller层单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@Transactional
@WebAppConfiguration
@ContextConfiguration
public class UserControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    MockHttpServletRequest request;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void testSaveUser() {
        try {
            User user = new User();
            user.setName("王五");
            user.setAge(25);
            user.setUsername("user5");
            user.setBalance(new BigDecimal(500.00));
            String requestJson = JSONObject.toJSONString(user);
            String responseString = mockMvc.perform(
                    MockMvcRequestBuilders.post("/user/save")
                    .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE).content(requestJson))
                    .andExpect(status().isOk())
                    .andDo(print())
                    .andReturn().getResponse().getContentAsString();
            System.err.println(responseString);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testGetUser() {
        String responseString = null;
        try {
            responseString = mockMvc.perform(get("/user/simple/1")
                    .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)).andExpect(status().isOk())
                    .andDo(print())
                    .andReturn().getResponse().getContentAsString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.err.println(responseString);
    }
}

需要注意单元测试的事务回滚,这就是@Transactional注解的作用。

创建消费者项目

创建消费者项目micro-service-consumer-movie,使用RestTemplate调用micro-service-provider-user生产者提供的接口。

application.yml配置文件

server:
  port: 8020

spring:
  application:
    name: cunsumer-movie

logback日志文件



    
    
    
    
    
        
        
            %date [%thread] %-5level %logger{50}:%L - %msg%n
        
    

    
    
        
        ${logDir}/service.log
        
            ${logDir}/history/service.%d{yyyy-MM-dd}.log
            30
        
        
            %date [%thread] %-5level %logger{50}:%L - %msg%n
        
    

    
    
        
        
    

注意配置文件的默认名为logback-spring.xml

实体类

@Data
public class User implements Serializable{

    private Long id;
    private String username;
    private String name;
    private String age;
    private BigDecimal balance;

}

实体类同生产者,因为movie项目不直接操作user表,因此此处无需@Entity注解,@Data注解要注入lombok的引入

Controller

@RestController
public class MovieController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/movie/{id}")
    public User findById(@PathVariable Long id) {
        return restTemplate.getForObject("http://localhost:8010/user/simple/" + id, User.class);
    }

    @PostMapping("/movie/save")
    public User save(User user) {
        return restTemplate.postForObject("http://localhost:8010/user/save", user, User.class);
    }
}

注意需要在启动类或者单独的配置类中配置RestTemplate,下面为在启动类中配置的。

启动类

@SpringBootApplication
public class ConsumerMovieApplication {

	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(ConsumerMovieApplication.class, args);
	}
}

测试消费者调用生产者的接口

消费者和生产者编写完成后,分别启动生产者和消费者,两个都启动成功后,使用postman测试消费者的接口,结果如下。
Spring Cloud 生产者和消费者_第2张图片
Spring Cloud 生产者和消费者_第3张图片

Spring Boot Actuator

Spring Boot Actuator提供了很多监控端点,如/health、/shutdown等,/health端点提供基本的应用程序健康信息,/shutdown默认是不启动用的,如需启动需在application.yml中添加如下配置。

endpoints:
  shutdown:
    enabled: true

在消费者micro-service-consumer-movie中配置,配置后重启消费者的服务,分别看一下/health和/shutdown的结果。
Spring Cloud 生产者和消费者_第4张图片
Spring Cloud 生产者和消费者_第5张图片如上图所示,/health只有一个status,/shutdown则直接报无权限。
因为默认情况下,所有敏感的HTTP端点都是安全的,只有具有ACTUATOR角色的用户 可以访问它们。
可以在application.yml中添加配置,将management.security.enabled 设为false,将通知Spring Boot跳过额外的角色检查。

management:
  security:
    enabled: false

重启micro-service-consumer-movie查看postman请求结果为:
Spring Cloud 生产者和消费者_第6张图片![在这里插入图片描述](https://img-blog.csdnimg.cn/20190610175328249.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM0MDgxODg=,size_16,color_FFFFFF,t_70

自定义info端点

可以在application.yml中添加如下配置,重启micro-service-consumer-movie查看postman请求结果。

info:
  app:
    name: @project.artifactId@
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@

Spring Cloud 生产者和消费者_第7张图片

本节学习完成后,将进行Eureka服务发现组件的学习。

你可能感兴趣的:(Spring,Cloud)